/* ==========================================================================
   Forms — the .form-item pattern.

   Every field in a page form is one .form-item: a wrapper div holding a
   label, the control, and an optional description. It replaces the <p> the
   Rails scaffold generates, which can't legally contain the <p class=
   "description"> the hint text wants to be.

     <div class="form-item">
       <label for="user_name">Name</label>
       <input type="text" name="user[name]" id="user_name" required>
       <p class="description">This is the name of the user</p>
     </div>

   WHAT IS NOT A FORM-ITEM. Only stacked, label-above-control page forms.
   Three existing patterns are deliberately outside it and must stay that way:

     - .filter-bar (list-blocks.css) — a horizontal control strip. Block
       labels and a bottom margin would break the row.
     - .line-item inline edit (line-items.css) — a row that becomes a form.
     - .collection-picker — brings its own .picker-label and field chrome.

   LOAD ORDER. `stylesheet_link_tag :app` sorts logical paths alphabetically,
   so this file loads BEFORE line-items, list-blocks, main and utilities.
   Anything here that has to beat one of those wins on specificity, not order
   — that is the only job the :root wrapper below does. It costs one
   pseudo-class, which is enough to clear main.css's global field rules.

   FIELD SIZING IS NOT HERE, ON PURPOSE. main.css already sizes inputs,
   selects and textareas: width 100%, capped at --input-max-width. That cap
   is a ceiling narrower contexts can duck under, which is exactly what the
   three exempt patterns above already do. Do not add a min-width here — a
   floor beats `width`, and every one of those overrides would have to be
   rewritten. Use .full-width to lift the cap for one field.

   The same rule makes a mobile breakpoint unnecessary: below 30rem of
   viewport the fields are already at 100%.

   DELIBERATELY NOT HERE, from the same source file. Add when a form actually
   needs one, so it arrives tested rather than guessed:

     .form-row              two fields side by side — nothing needs it yet
     FORM.submitting        needs a Stimulus controller to set the class
     .optional-form-item    a <details> that hides its own summary when open
     .screenreader-only     duplicate of .visually-hidden in utilities.css
     .disabled              duplicate of BUTTON.disabled in utilities.css
     FORM.button_to         already handled by line-items / list-blocks
     #error_explanation     Voces uses <section role="alert"> instead
   ========================================================================== */

:root {

  /* ------------------------------------------------------------------------
     1. THE FIELD WRAPPER
     ---------------------------------------------------------------------- */

  DIV.form-item {
    margin-bottom: var(--space-4);
  }

  /* main.css already makes labels block, bold and heading-coloured. Inside a
     form-item they step down one size, so the label reads as the field's
     caption rather than as body copy. */
  DIV.form-item LABEL {
    font-size: var(--fs-small);
    text-align: left;
  }

  /* A form-item whose "field" is a link rather than a control — the password
     row, which sends you to a page instead of taking a value. There is no
     control to point at, so a <label> would be a lie; this carries the label's
     look without its semantics. Mirrors main.css's label rule plus the
     step-down above. */
  DIV.form-item P.item-label {
    display: block;
    font-size: var(--fs-small);
    font-weight: var(--fw-bold);
    margin-bottom: var(--space-1);
    color: var(--vn-heading);
  }

  /* The value of a form-item that reports rather than asks — the role on a
     staff form the reader is not allowed to set. Pairs with P.item-label
     above, for the same reason: with no control in the row there is nothing a
     <label> could point at.

     Plain body text, not field chrome. A disabled <select> was the obvious
     first try and reads as a control that is broken, which is the wrong
     sentence: the role is not unavailable, it is simply not yours to set. */
  DIV.form-item P.static-value {
    margin: 0;
    font-size: var(--fs-body);
    color: var(--color-text);
  }

  /* Checkbox fields put the control FIRST and the label after it:

       <div class="form-item">
         <input type="checkbox" name="course[visible]" id="course_visible">
         <label for="course_visible">Visible</label>
       </div>

     The sibling combinator is what pulls the label back onto the control's
     line, so that order is required, not stylistic. (margin-left is a Voces
     addition — a single text space leaves the two too tight.) */
  DIV.form-item INPUT[type=checkbox] + LABEL {
    display: inline-block;
    margin-bottom: 0;
    margin-left: var(--space-2);
  }

  /* Hint text under a field. Replaces the <small> the scaffold reaches for,
     which only got used because a <p> can't nest inside a <p>. The muted
     colour is a Voces addition; the sizing follows the reference. */
  DIV.form-item P.description,
  DIV.form-item P.fine-print {
    margin-top: var(--space-2);
    margin-bottom: 0;
    font-size: var(--fs-small);
    color: var(--color-text-muted);
  }


  /* ------------------------------------------------------------------------
     2. MODIFIERS
     ---------------------------------------------------------------------- */

  /* Lifts main.css's --input-max-width cap for one field. */
  DIV.form-item.full-width {
    position: relative;

    INPUT[type=text],
    INPUT[type=email],
    INPUT[type=date],
    INPUT[type=datetime-local],
    INPUT[type=number],
    INPUT[type=password],
    INPUT[type=search],
    INPUT[type=url],
    INPUT[type=tel],
    SELECT,
    TEXTAREA,
    .upload-target,
    .upload-file-line {
      max-width: none;
    }
  }

  /* A <details> that holds a field — an optional or advanced setting folded
     away. The base wrapper rule is div-scoped, so it needs saying again. */
  DETAILS.form-item {
    margin-bottom: var(--space-4);
  }

  DETAILS.form-item > SUMMARY {
    cursor: pointer;
    margin-bottom: var(--space-2);
  }

  DIV.form-item TEXTAREA.large {
    max-width: none;
    min-height: 10rem;
  }

  DIV.form-item TEXTAREA.tall {
    min-height: 10rem;
  }

  /* A list of checkboxes that are all one question — an attendance roster, a
     set of permissions. The label WRAPS its own control here, so the whole
     row is a hit target; that makes it the one place the adjacent-sibling
     checkbox rule above does not apply. The names are the content, so they
     drop main.css's bold heading-blue label treatment. */
  UL.checkbox-list {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  UL.checkbox-list LABEL {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-bottom: 0;
    padding: var(--space-1) 0;
    font-weight: var(--fw-regular);
    color: var(--color-text);
    cursor: pointer;
  }

  /* A word between two fields — "or", "to". Pushed down by one line-height so
     it sits level with the controls rather than with their labels. */
  .form-item-separator {
    display: block;
    text-align: center;
    font-weight: var(--fw-bold);
    padding: var(--space-3) var(--space-1);
    margin-top: calc(1lh + var(--space-1));
  }


  /* ------------------------------------------------------------------------
     3. FORM FURNITURE
     ---------------------------------------------------------------------- */

  /* The submit row. Named .actions to match the Rails scaffold convention;
     the reference file's second name for the same thing (.form-submit) is
     dropped rather than carried as a synonym.

     A wrapping flex row so a secondary link — "Forgot your password?" beside
     Sign in — sits level with the button and drops beneath it when the row
     runs out of width, rather than breaking the button's line. Nearly every
     form has a lone submit here, and a one-child flex row lays out exactly
     as the block did. */
  FORM DIV.actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3) var(--space-4);
    margin-top: var(--space-8);
  }

  /* main.css strips fieldset borders and margins, which leaves stacked
     fieldsets running together and <legend> looking like body text. A
     fieldset is a section of a form, so give it the weight of one. */
  FORM FIELDSET + FIELDSET {
    margin-top: var(--space-8);
  }

  FORM FIELDSET > LEGEND {
    padding: 0;
    margin-bottom: var(--space-3);
    font-family: var(--font-family-display);
    font-size: var(--fs-h4);
    font-weight: var(--fw-bold);
    color: var(--vn-heading);
  }


  /* ------------------------------------------------------------------------
     4. THE UPLOAD CONTROL — a drop panel standing in for an input[type=file].

     .upload-target is the CONTROL, not the field. The form-item around it,
     its <label> and any description are ordinary markup written at the call
     site, so an upload field is put together exactly the way a text field is:

       <div class="form-item">
         <label for="document_file">File</label>
         <div class="upload-target" data-controller="simple-uploader">
           <label class="upload-panel">        ← the drop target
             <span class="upload-instruction">…</span>
             <span class="upload-hint">…</span>
             <span class="upload-drop-now">…</span>
             <input type="file" …>             ← real, focusable, invisible
           </label>
           <ul class="upload-file-list" hidden></ul>
           <p class="upload-status" aria-live="polite"></p>
         </div>
         <p class="description">Current file: handout.pdf</p>
       </div>

     Rendered by shared/_upload_field, driven by simple_uploader_controller.js.
     ---------------------------------------------------------------------- */

  /* Takes the same --input-max-width cap as the text fields and selects, so
     it lines up with them — and .full-width lifts it by the same rule that
     lifts theirs, rather than needing one of its own. */
  DIV.upload-target {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: var(--input-max-width);
  }

  .upload-panel {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.375rem;

    box-sizing: border-box;
    min-height: 8.25rem;
    padding: 1.25rem var(--space-6);

    border: 2px dashed var(--brisa);
    border-radius: var(--radius-lg);
    /* Niebla pulled most of the way to white. At full strength it competes
       with the callout blocks that use niebla as their own surface. */
    background: color-mix(in srgb, var(--niebla) 40%, var(--nieve));
    cursor: pointer;

    /* Colour only — no scale, no bounce. */
    transition: background-color 0.3s linear;
  }

  /* The panel is a <label> sitting inside a .form-item, so it inherits the
     treatment meant for the field's caption: bold weight, heading colour and
     a bottom margin from main.css, and left alignment from the
     DIV.form-item LABEL rule in section 1. None of that belongs to a drop
     target.

     The selector leads with .upload-target purely to outrank that rule — a
     bare .upload-panel loses to it, which is what left-aligned the
     instruction even though the panel had already asked for centre. */
  .upload-target LABEL.upload-panel {
    margin-bottom: 0;
    font-weight: var(--fw-regular);
    color: var(--color-text);
    text-align: center;
  }

  /* The ring belongs on the panel: the input it would otherwise land on is
     invisible and stretched edge to edge. */
  .upload-panel:focus-within {
    outline: 2px solid var(--focus-ring-color);
    outline-offset: 2px;
  }

  /* The real control. Invisible, but still focusable, still keyboard
     operable, and still the thing the browser drops onto — which is why it
     is opacity, not display:none. Covering the panel is what makes a click
     anywhere in it open the file dialog. */
  .upload-panel INPUT[type=file] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
  }

  .upload-instruction {
    max-width: 19rem;
    font-size: var(--fs-small);
    font-weight: var(--fw-medium);
    line-height: 1.5;
    color: var(--crepusculo);
  }

  .upload-hint {
    font-size: var(--fs-meta);
    color: var(--color-text-muted);
  }

  /* The attached file, on one line under the drop panel: what is there on the
     left, the way to be rid of it on the right.

     It takes --input-max-width so the two ends land on the panel's own edges
     — the line is a sibling of .upload-target, not a child, so it does not
     inherit the cap and would otherwise run to the full width of the form
     while the panel above it stopped short. Baseline alignment rather than
     centre because a long filename wraps and the link should stay level with
     its first line, not drift to the middle of the block. */
  .upload-file-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    max-width: var(--input-max-width);
    font-size: var(--fs-small);
    color: var(--color-text-muted);
  }

  /* Long names break rather than push the link off the end. */
  .upload-file-name {
    min-width: 0;
    overflow-wrap: anywhere;
  }

  /* What is left after the file goes. No dashed ghost box here, unlike a
     deleted card: the drop panel directly above is already a dashed brisa
     rectangle over the same niebla wash, and a second one stacked under it
     reads as two empty wells rather than as one message. The sentence carries
     it on its own. */
  .upload-file-line-removed {
    font-style: italic;
  }

  .upload-drop-now {
    display: none;
  }

  /* Written one level deeper than .has-error below so that it outranks it:
     a drag in progress is always calabaza, never tomate. */
  .upload-target .upload-panel.drag-over {
    border-color: var(--calabaza);
    background: var(--niebla);

    .upload-instruction,
    .upload-hint {
      display: none;
    }

    .upload-drop-now {
      display: block;
      font-size: var(--fs-small);
      font-weight: var(--fw-medium);
      color: var(--crepusculo);
    }
  }

  UL.upload-file-list {
    list-style: none;
    margin: 0;
    padding: 0;

    LI {
      display: flex;
      align-items: center;
      gap: var(--space-3);
      padding: 0.625rem var(--space-1);
      border-bottom: 1px solid var(--color-border);
    }

    .file-dot {
      flex: none;
      width: 0.5rem;
      height: 0.5rem;
      border-radius: var(--radius-full);
      background: var(--autobus-escolar);
    }

    .file-name {
      flex: 1;
      min-width: 0;
      font-size: var(--fs-small);
      color: var(--color-text);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .file-meta {
      flex: none;
      font-size: var(--fs-meta);
      color: var(--color-text-muted);
    }

    /* The row's remove control is BUTTON.text from utilities.css — an
       underlined link. Only its size is set here. */
    BUTTON.file-remove {
      flex: none;
      font-size: var(--fs-meta);
    }
  }

  P.upload-status {
    margin: 0;
    font-size: var(--fs-meta);
    color: var(--color-text-muted);
  }

  /* The control blanks the line rather than removing it, so the aria-live
     region survives to announce the next selection. .upload-target is a flex
     column with a gap, and an empty <p> still takes a gap — take it out of
     the layout instead of letting it space the panel off what's below. */
  P.upload-status:empty {
    display: none;
  }

  .upload-target.has-error {
    .upload-panel {
      border-color: var(--tomate);
    }

    .upload-status {
      color: var(--tomate);
    }
  }

  .upload-target.uploading .upload-panel {
    cursor: progress;
  }

  /* Image preview - Show an image alongside the control to replace it */ 
  .image-preview {
    display: grid;
    grid-template-columns: auto min-content;
    max-width: var(--input-max-width);
    gap: 1rem;

    .image-with-action {
      display: grid;
      grid-template-rows: auto min-content;
      gap: 0;
      text-align: center;
      justify-items: center;
    }
  }

  /* One line of instruction is plenty once the panel itself is narrow. */
  @media (max-width: 48rem) {
    .upload-instruction {
      max-width: 100%;
    }

    .image-preview {
      grid-template-columns: 1fr;

      div.upload-target {
        order: 2;
      }
    }
  }
}
