/* ---- The form layer -------------------------------------------------------
   Everything a form is made of: the controls, the field wrapper around one
   control, the sections and rows that arrange them, where the actions sit,
   and the two shapes an error takes. Linked straight after `buttons.css` on
   every shell, because forms appear on all of them.

   It exists because the control block had been written out nine times (twice
   in `base.css`, once each in seven page sheets), every time as a list of
   input TYPES. The type list is the bug: `url`, `password`, `number` and
   `datetime-local` were never on any of them, so those four rendered as raw
   OS boxes beside styled ones (CODE-AUDIT §5, UI audit RC-1). The one rule
   below styles the ELEMENT and subtracts the handful of types that are not
   text-like boxes, so a field added tomorrow is styled by existing.

   Three conventions to keep:

   - Colours, sizes and spaces are tokens. `--control-h`, the two control
     padding tokens, `--form-measure` and `--icon-chevron` live in
     `tokens.css` with the rest of the scale.
   - The global `:focus-visible` outline in `base.css` still serves links,
     buttons and summaries. Controls take the ring below instead, and are the
     only elements here that turn `outline` off.
   - Scope lists are written with `:where()`, which contributes no
     specificity. That is deliberate: it lets a modifier further down this
     file (`.field--short`, `.inline-field`, `.field.is-invalid`) win with a
     single class instead of an ever-longer selector, which is how the nine
     copies grew in the first place. CSS nesting is used inside the control
     block and nowhere else, for the same reason.
   - `:has()` is used exactly once, in "Rows": a field holding a textarea
     takes the whole row. It is a parent selector because the fact that
     decides the layout (this cell contains a paragraph control) is only
     known from the child, and the templates that render the row do not know
     which of their fields is a textarea.
   -------------------------------------------------------------------------- */

/* ---- Controls -------------------------------------------------------------
   The scopes are the form containers, not the page: the topbar search button,
   the topbar language switch and the sidebar's organization picker have their
   own rules in `shell.css`, `base.css` and `picker.css` and must not be
   dragged in here. `.filter-bar` is on the list because it is a form
   container like the rest of them: the bar above a list is a `<form
   method="get">` of a search box and up to four selects (style guide 7.8),
   and `lists.css` lays those out without writing a tenth control block. */

:where(
    .panel-form,
    .auth-form,
    .detail-panel form,
    .field,
    .field-group,
    .inline-field,
    .transition-form,
    .filter-bar
  )
  :is(input, select, textarea):not(
    [type="checkbox"],
    [type="radio"],
    [type="hidden"],
    [type="submit"],
    [type="button"],
    [type="reset"],
    [type="image"]
  ) {
  display: block;
  width: 100%;
  /* The one declaration that stops a date input or a select from blowing out
     its grid column: both have an intrinsic minimum wider than `1fr`, and a
     select's is its longest option (the case workspace scrolled sideways on a
     phone because one option held a full case title). */
  min-width: 0;
  box-sizing: border-box;
  min-height: var(--control-h);
  padding: var(--control-pad-y) var(--control-pad-x);
  font: inherit;
  font-size: var(--fs-body);
  /* `font: inherit` above also inherits the page's line-height, and a page
     that sets a reading leading (the pre-login card) made its controls 42px
     while every other page's were 38px. Naming it here is what actually
     holds `--control-h`. */
  line-height: var(--lh-snug);
  color: var(--ink);
  background: var(--panel);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  transition: border-color var(--dur-1) var(--ease), box-shadow var(--dur-1) var(--ease);

  &::placeholder { color: var(--slate); }

  &:hover { border-color: var(--ink-2); }

  /* The ring replaces the global outline on controls only: a border colour
     alone is one hairline of difference, which is not a focus indicator. */
  &:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: var(--ring);
  }

  &:disabled {
    background: var(--panel-sunk);
    color: var(--slate);
    border-color: var(--line-soft);
    cursor: not-allowed;
  }
  /* Read-only is not refused input; it is input that is already decided. */
  &[readonly] {
    background: var(--panel-sunk);
    color: var(--slate);
    border-color: var(--line-soft);
    cursor: default;
  }

  /* The variants are nested rather than written as sibling rules because the
     scope above is one class heavier than a bare `textarea` or `select`
     selector, and a sibling rule would silently lose to it. */

  /* A textarea sizes by content, so it takes a floor rather than a height,
     and may only grow downwards: horizontal resize breaks the grid column.
     The floor is also what makes the nineteen `rows="2"` boxes that hold
     paragraphs readable without touching their markup. */
  &:is(textarea) {
    min-height: 5.5rem;
    line-height: var(--lh-snug);
    resize: vertical;
  }

  /* A date, a timestamp and a quantity are fixed-width values, and a box
     sized for a product name says the opposite. Left uncapped,
     `support_period_end` was 1128px wide and `cadence_days` (two digits)
     took a third of its row. */
  &:is([type="date"], [type="datetime-local"], [type="time"]) { max-width: 16rem; }
  &:is([type="number"]) { max-width: 9rem; }

  /* The select draws its own chevron so it matches the other controls rather
     than whatever the OS paints. `--icon-chevron` carries the stroke colour,
     so nothing here writes one and dark mode stays the token layer's
     problem. */
  &:is(select) {
    appearance: none;
    padding-right: 2.2rem;
    background-image: var(--icon-chevron);
    background-repeat: no-repeat;
    background-position: right var(--control-pad-x) center;
    background-size: 0.9rem;
  }
  /* A multiple select is a list, not a dropdown, so it takes no chevron. */
  &:is(select[multiple]) {
    background-image: none;
    padding-right: var(--control-pad-x);
  }

  /* The file input keeps its border, so the chosen filename reads inside the
     control; only its button is restyled, as the quiet button in miniature. */
  /* A file control's content is a filename, not a measure, so it is capped
     like the date and the quantity above. */
  &:is([type="file"]) {
    padding: var(--space-1) var(--control-pad-x);
    max-width: 24rem;
  }
  &:is([type="file"])::file-selector-button {
    margin-right: var(--space-3);
    padding: var(--space-1) var(--space-3);
    font: inherit;
    font-size: var(--fs-small);
    color: var(--accent);
    background: var(--panel-sunk);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    cursor: pointer;
  }
}

/* Boxes and radios keep their native shape and take the tenant's accent,
   which `accent-color: auto` was ignoring on every one of them. */
:where(.panel-form, .auth-form, .detail-panel form, .field, .field-group,
       .checkbox-label) :is(input[type="checkbox"], input[type="radio"]) {
  width: 1.05rem;
  height: 1.05rem;
  accent-color: var(--accent);
  flex: none;
}

/* ---- Labels ---------------------------------------------------------------
   Thirty-three panels still write `<label>Name {{ control }}</label>` by hand
   (that markup stays; only the stylesheet reaches them), so the wrapping
   label is styled here as well as the `.field` one. A label is the field's
   NAME, not a caption: the caption is the help text under it. */

:where(.panel-form, .detail-panel form, .transition-form, .field-group)
  label:not(.checkbox-label) {
  display: block;
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--ink-2);
  line-height: var(--lh-snug);
}

/* ---- One field ------------------------------------------------------------
   Fixed order: label, control, help, error. The help and error carry the ids
   Django's `aria-describedby` already points at, which every hand-rendered
   field but one left dangling (CODE-AUDIT §2). */

.field {
  display: grid;
  gap: var(--space-1);
  align-content: start;
  min-width: 0;
}
.field > label {
  font-size: var(--fs-ui);
  font-weight: var(--fw-medium);
  color: var(--ink-2);
  line-height: var(--lh-snug);
}
/* Optional is the exception in a compliance record, so optional is the thing
   that gets said. Required fields carry nothing: an asterisk on nine fields
   in ten is noise, and it has no reading in zh-Hant. */
.field__optional {
  font-weight: var(--fw-regular);
  color: var(--slate);
}
/* Help and error are blocks under the control, never a continuation of its
   sentence: "…by (PRE-6-RQ-01). This field is required." was one running
   paragraph on five pages. */
.field > .note,
.field-group > .note {
  display: block;
  margin: 0;
  font-size: var(--fs-micro);
  color: var(--slate);
  line-height: var(--lh-snug);
}
.field .field-error,
.field-group > .field-error { display: block; }

.field.is-invalid > label { color: var(--ink); }
.field.is-invalid
  :is(input, select, textarea):not([type="checkbox"], [type="radio"], [type="hidden"]) {
  border-color: var(--crit);
}
.field.is-invalid
  :is(input, select, textarea):not([type="checkbox"], [type="radio"], [type="hidden"]):focus {
  box-shadow: var(--ring-crit);
}

/* A quantity is not a sentence: the response-time settings are days. */
.field--short :is(input, select) { max-width: 9rem; }

/* A box reads beside its label, and a label long enough to wrap keeps its box
   on the first line. */
.checkbox-label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-size: var(--fs-small);
  font-weight: var(--fw-regular);
  color: var(--ink);
  line-height: var(--lh-snug);
}
.checkbox-label input { margin-top: 0.15rem; }

/* ---- A section ------------------------------------------------------------
   A fieldset with no box: the legend is an eyebrow on a hairline that runs
   the full width, the same eyebrow the read side sets its `.fields-grid`
   labels in, so reading a record and editing it are one document. Where the
   section answers a clause, the reference sits at the right end of that
   hairline as a margin note, the way the standard prints its own numbering.
   The slot is filled only where the form's own source already names the
   requirement, and is never invented. */

.field-group {
  border: 0;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
  min-width: 0;
}
.field-group > legend {
  width: 100%;
  padding: 0 0 var(--space-2);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--rule);
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-size: var(--fs-micro);
  font-weight: var(--fw-medium);
  color: var(--slate);
}
/* The clause reference: a margin note at the far end of the rule, set in the
   face this workspace uses for every requirement ID. */
.field-group__clause {
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: var(--fs-small);
  color: var(--slate);
  white-space: nowrap;
}

/* ---- Rows -----------------------------------------------------------------
   `align-items: start`, never `end`. A label that wraps in zh-Hant but not in
   English, or a help text on one field of three, used to push its neighbour's
   control off the row and stagger three labels over 70px (base.css:357,
   patched afterwards on exactly one form by `.bom-field-row`).
   `min(100%, …)` is what keeps a single wide field from overflowing a narrow
   panel: below 12rem the track collapses to the container instead. */

.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: var(--space-3) var(--space-4);
  align-items: start;
}
/* The bilingual pairs and the policy dates want a wider cell before they
   wrap: a title in two languages side by side is unreadable at 12rem. */
.field-row--wide {
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
}
/* A paragraph is not a cell. A description or a rationale sharing a row with
   three single-line fields gets a third of the width and five visible words,
   so it takes the whole row instead. Bilingual rows are the exception: their
   two textareas ARE the pair, and they keep their two cells. */
.field-row:not(.field-row--wide) > .field:has(textarea) { grid-column: 1 / -1; }

/* ---- The form itself ------------------------------------------------------ */

.panel-form,
.auth-form,
.detail-panel form {
  display: grid;
  gap: var(--space-3);
  min-width: 0;
}
.panel-form { margin-top: var(--space-3); }
.auth-form { margin: 0; }
/* Sections breathe more than the fields inside them. */
.panel-form > .field-group + * { margin-top: var(--space-2); }

/* A standalone page form is a reading measure; a form inside a case panel or
   a detail panel fills it, because the panel already set the measure. */
.panel-form--page { max-width: var(--form-measure); }

/* A form that is one element of a sentence or a table cell. */
.inline-form { display: inline; }

/* A single control and its button on one line: the role grant, the
   end-of-support date, the finding actions, the evidence chooser. It is the
   answer to `.btn-row` having been used as a field row, where five controls
   in a `flex` container with no `wrap` squeezed to 53px on a phone. */
.inline-field {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.inline-field :is(input, select, textarea) {
  width: auto;
  flex: 1 1 10rem;
}
.inline-field input:is([type="date"], [type="datetime-local"], [type="time"]) {
  flex: 0 1 10rem;
}
.inline-field > label:not(.checkbox-label) {
  font-size: var(--fs-small);
  color: var(--ink-2);
}
.inline-field .btn { flex: none; }

/* A case transition is a form in a card. It was the one scope matching no
   control rule at all, so its select and its two textareas rendered raw; and
   the select, sized to its widest option, was what pushed the case workspace
   off the page. */
.transition-form {
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  display: grid;
  gap: var(--space-2);
  min-width: 14rem;
  max-width: 22rem;
}

/* In the page header the transition is the page's one action and nothing
   else: no card around it, no width of its own (style guide 6.2). The card
   above is for the forms that carry a rationale with them, which live in the
   rail and in the State panel. */
.transition-form--header {
  border: 0;
  padding: 0;
  min-width: 0;
  max-width: none;
}

/* ---- Actions --------------------------------------------------------------
   The button family itself is `buttons.css`; what a form adds is where its
   actions sit. */

/* The actions of a form, closed off from the fields above them, and only
   when the row ends the form: a `.btn-row` in the middle of one is a group of
   choices, not a conclusion. */
:where(.panel-form, .auth-form, .detail-panel form) > .btn-row:last-child {
  border-top: 1px solid var(--line-soft);
  padding-top: var(--space-3);
  margin-top: var(--space-2);
}

/* On a phone the closing row stops being a row: a thumb gets the full column
   and 44px of it, which is the touch floor rather than the pointer one. */
@media (max-width: 40rem) {
  :where(.panel-form, .auth-form, .detail-panel form) > .btn-row:last-child {
    display: grid;
    gap: var(--space-2);
  }
  :where(.panel-form, .auth-form, .detail-panel form)
    > .btn-row:last-child > .btn {
    width: 100%;
    min-height: 2.75rem;
  }
}

/* ---- Messages -------------------------------------------------------------
   A non-field error is a refusal notice now: `.notice-crit` in
   `partials/notice.html` through `partials/form_errors.html` (style guide
   7.9). `.error-box` is gone; nothing renders it. */

/* Django's own `<ul class="errorlist">`, for anywhere a raw
   `{{ form.non_field_errors }}` still reaches the page. A safety net, not a
   licence: `partials/form_errors.html` is what a form should render. */
.errorlist {
  margin: 0;
  padding: 0;
  list-style: none;
  color: var(--crit);
  font-size: var(--fs-small);
}

/* One line per error. Never two errors joined into one sentence by
   `|striptags`, which is how "Enter a valid URL.Enter a value." happened. */
.field-error {
  color: var(--crit);
  font-size: var(--fs-micro);
  line-height: var(--lh-snug);
}

/* The field a human must never see and a bot fills in. */
.honeypot-field { display: none; }
