/* ============================================================================
 * Form controls — input, textarea, select, field, checkbox/radio, switch
 * ========================================================================== */

.ui-field { display: block; }
.ui-field__label {
  display: block;
  color: var(--strong);
  font-size: var(--text-base);
  font-weight: var(--weight-medium);
  margin-bottom: 8px;
}
/* Required marker. Decoration only — the control carries `required`, which is
   what assistive technology reads, so this asterisk is aria-hidden. */
.ui-field__req { color: var(--pink); margin-left: 4px; }
.ui-field__hint { color: var(--text); font-size: var(--text-sm); margin-top: 7px; line-height: 1.5; }
.ui-field__error { color: var(--pink); font-size: var(--text-sm); margin-top: 7px; display: flex; align-items: center; gap: 6px; }
.ui-field__error svg { width: 16px; height: 16px; flex: none; stroke-width: 2.4; }

.ui-input,
.ui-textarea,
.ui-select {
  font-family: var(--font-sans);
  font-size: var(--text-base);
  color: var(--strong);
  /* Fields use the card surface and an edge in both themes.
     why: docs/foundations.md#elevation */
  background: var(--field-bg); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  border: 1px solid var(--field-edge);
  border-radius: var(--radius-md);
  padding: 12px 15px;
  width: 100%;
  transition: border-color var(--dur-med) var(--ease), box-shadow var(--dur-med) var(--ease);
}
.ui-textarea { resize: vertical; min-height: 96px; line-height: 1.55; }

.ui-input:hover,
.ui-textarea:hover,
.ui-select:hover { border-color: var(--field-edge-hover); }

.ui-input::placeholder,
.ui-textarea::placeholder { /* muted-ink: placeholder — empty form field */ color: var(--muted); opacity: 0.9; }

/* The value a reader typed is still the value; it goes quiet, it does not fade
   into the field under it the way an opacity would take both down together. #220
   `.ui-select` joined the pair in #273: it paints its own field, which outranks the
   UA's grey-out, so a disabled one had looked exactly like a live one.
   `background-color`, not `background` — the shorthand also resets the three
   longhands the chevron is drawn with (#511). Held by stories/select-chevron.test.js.
   `opacity` is the other half of that grey-out, and no colour here outranks it, so
   the fade is reset below — it took the edge and the words down together. #564,
   argued at docs/foundations.md#colour-and-contrast.
   why: docs/foundations.md#icons-and-glyphs */
.ui-input:disabled,
.ui-select:disabled,
.ui-textarea:disabled {
  background-color: var(--disabled-surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  border-color: var(--disabled-border);
  /* muted-ink: state — unavailable control */ color: var(--disabled-ink);
  opacity: 1; /* fade-reset: the UA's select:disabled grey-out. #564 */
  cursor: not-allowed;
}

.ui-input.is-invalid, .ui-select.is-invalid,
.ui-textarea.is-invalid { border-color: var(--pink); }
/* Keep the border's width, but make its ink transparent while the ring shows.
   This also outranks the invalid and hover edges, so focus has one line. */
.ui-input:focus-visible,
.ui-textarea:focus-visible,
.ui-select:focus-visible {
  border-color: transparent;
  outline: var(--ring);
  outline-offset: var(--ring-offset);
}

/* A search field with a value gets the browser's own clear button, and the
   browser paints it — near-black on the light field, white on the dark one,
   beside the kit's --muted magnifier. Two glyphs, and only one answers to a
   token. .ui-select refuses UA chrome for the same reason, hand-drawing its
   chevron per theme; and the vanilla search specimen is a text input with a
   `search` glyph and no clear button, so a field without one is what the
   design already says. Measured in Chromium; Firefox draws nothing here and
   Edge is untested.
   why: docs/foundations.md#colour-and-contrast */
.ui-input[type="search"]::-webkit-search-cancel-button {
  appearance: none;
  display: none;
}

/* Input with a leading icon / adornment */
.ui-input-group { position: relative; display: flex; align-items: center; }
.ui-input-group .ui-input { padding-left: 42px; }
.ui-input-group__icon {
  position: absolute;
  left: 14px;
  width: 17px;
  height: 17px;
  /* muted-ink: glyph — field icon */ color: var(--muted);
  pointer-events: none;
}
.ui-input-group__icon svg { width: 100%; height: 100%; stroke: currentColor; fill: none; stroke-width: 2.2; }

/* The chevron is a data URI, and a data URI cannot read a token: `var()` does not
   substitute inside url() and `currentColor` does not cross into the image's own
   document. So the ink is written twice, once per theme, each the theme's --muted —
   which is the colour every other quiet glyph on a field takes. It was one frozen
   hex until #295, dark --muted's old value, which the ladder's re-pick left behind
   at 2.49:1 on the light field. Held by stories/colour-tokens.test.js.
   The mark is .ui-dropdown__chevron drawn as a path: its 8px border caret turned 45
   degrees, at its 1.6px stroke and lifted by --caret-off, so both controls show one
   chevron. It is a plain stroke with butt ends and a mitred point, 11.3px wide.
   why: docs/foundations.md#colour-and-contrast */
.ui-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-6 -6 12 12' fill='none' stroke='%23a29db6' stroke-width='1.6'%3E%3Cpath d='M-5.09 -2.83L0 2.26l5.09-5.09'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 15px center;
  padding-right: 40px;
  cursor: pointer;
}
:root[data-theme="light"] .ui-select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='-6 -6 12 12' fill='none' stroke='%235c6270' stroke-width='1.6'%3E%3Cpath d='M-5.09 -2.83L0 2.26l5.09-5.09'/%3E%3C/svg%3E");
}

/* Checkbox / radio — custom, token-driven. DataTable's selection boxes are bare
   inputs in a cell with no label, and take the same box so a partial page reads in
   the kit's paint rather than the browser's. #619 */
.ui-check {
  display: inline-flex;
  align-items: flex-start;
  gap: 11px;
  cursor: pointer;
  font-size: var(--text-base);
  color: var(--text);
  line-height: 1.5;
}
.ui-check input,
.ui-table__selection input[type="checkbox"] {
  appearance: none;
  width: 19px;
  height: 19px;
  flex: none;
  margin-top: 1px;
  border: 1.5px solid var(--border-strong);
  border-radius: var(--radius-xs);
  background: var(--surface-2); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
  position: relative;
}
/* In a cell there is no label line to sit beside, so the box centres on the row's
   text instead of dropping 1px to meet a label's first line. */
.ui-table__selection input[type="checkbox"] { margin-top: 0; vertical-align: middle; }
/* The target, not the ink. The box above is drawn at 19x19 because that is what
   sits beside a 15px label without shouting; WCAG 2.5.8 wants 24 CSS px of
   pointer target, which is a different question. This overlay answers it and
   the checkbox keeps its size. ::after is the tick and the dot, so the target
   is ::before — it paints first, under both, and is transparent anyway.
   Centred, so the overhang is 2.5px on every side: 11px of gap to the label
   inside the same <label> (which toggles the same input, so an overlap there
   would cost nothing) and 14px between stacked rows in the specimen. Measured
   in stories/guidelines/accessibility-floor.test.js. #219 */
.ui-check input::before,
.ui-table__selection input[type="checkbox"]::before {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 24px;
  height: 24px;
  transform: translate(-50%, -50%);
}
.ui-check input[type="radio"] { border-radius: 50%; }
/* :enabled, or the accent lights the border of a box that cannot be clicked —
   the same "press me" a disabled control has to stop making. #429 */
/* The hover edge steps aside under the band: an accent edge one pixel inside an
   accent band is the second band #578 round r34 took off the focus rules, and a
   pointer resting on a focused control would put it back. One indicator, which is
   what the kit's rows already chose. why: docs/foundations.md#the-focus-ring */
.ui-check input:enabled:hover:not(:focus-visible),
.ui-table__selection input[type="checkbox"]:enabled:hover:not(:focus-visible) { border-color: var(--accent); }
.ui-check input:checked,
.ui-table__selection input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
.ui-check input[type="checkbox"]:checked::after,
.ui-table__selection input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 5px; top: 1.5px;
  width: 5px; height: 10px;
  border: solid var(--accent-contrast);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg); /* motion: still — the turn is the tick's shape, not travel; it lands with the box's fill, which transitions */
}
.ui-check input[type="radio"]:checked::after {
  content: "";
  position: absolute;
  inset: 4px;
  border-radius: 50%;
  background: var(--accent-contrast);
}
.ui-check input:focus-visible { outline: var(--ring); outline-offset: var(--ring-offset); }

/* Off. `.ui-check` had no disabled rule at all until #429 put a disabled checkbox
   on screen: the box kept --surface-2 and --border-strong, its words kept --text,
   and it was the word "unavailable" in the label that carried the state. The same
   flat pair the fields and the buttons take (#220, #273) — a paint on the box and
   quiet ink on the words, never an `opacity`, which drags label and box toward the
   ground together. A checked box drops the accent for the reason a disabled primary
   button does: the accent IS the "press me". Its mark inverts to --disabled-surface,
   so the tick and the dot keep the measured --disabled-ink pair in both themes. */
.ui-check input:disabled,
.ui-table__selection input[type="checkbox"]:disabled {
  background: var(--disabled-surface); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  border-color: var(--disabled-border);
  cursor: not-allowed;
}
.ui-check input:disabled:checked,
.ui-table__selection input[type="checkbox"]:disabled:checked { background: var(--disabled-ink); border-color: var(--disabled-ink); }
.ui-check input[type="checkbox"]:disabled:checked::after,
.ui-table__selection input[type="checkbox"]:disabled:checked::after { border-color: var(--disabled-surface); }
.ui-check input[type="radio"]:disabled:checked::after { background: var(--disabled-surface); /* code-bg: inherit — the mark inside the box; the box carries the focus indicator. */ }
/* Partial: some of a group is on, not all. The checked box's fill with a level
   dash where the tick would be, so the box still reads as "on" from across a table
   and the dash alone says "not all". It wins over a tick when both are set, as the
   browser's own box does. Drawn with a border, not a background, so forced colours
   keeps the dash. #619 */
.ui-check input[type="checkbox"]:indeterminate,
.ui-table__selection input[type="checkbox"]:indeterminate { background: var(--accent); border-color: var(--accent); }
.ui-check input[type="checkbox"]:indeterminate::after,
.ui-table__selection input[type="checkbox"]:indeterminate::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 11px; height: 0; /* odd, so the 17px padding box splits into equal gaps */
  border: solid var(--accent-contrast);
  border-width: 2px 0 0;
  transform: none;
}
/* White ink falls below 3:1 on the lighter dark-theme accents, so the tick, the dash and
   the radio's dot take the ink a selected Segmented pill takes there. */
:where(:root[data-theme="dark"]) .ui-check input[type="checkbox"]:enabled:is(:checked, :indeterminate)::after,
:where(:root[data-theme="dark"]) .ui-table__selection input[type="checkbox"]:enabled:is(:checked, :indeterminate)::after { border-color: var(--signal-contrast); }
:where(:root[data-theme="dark"]) .ui-check input[type="radio"]:enabled:checked::after { background: var(--signal-contrast); }
.ui-check input[type="checkbox"]:disabled:indeterminate,
.ui-table__selection input[type="checkbox"]:disabled:indeterminate { background: var(--disabled-ink); border-color: var(--disabled-ink); }
.ui-check input[type="checkbox"]:disabled:indeterminate::after,
.ui-table__selection input[type="checkbox"]:disabled:indeterminate::after { border-color: var(--disabled-surface); }
/* Forced colours replace every fill, so a disabled box that painted only a fill and
   a --disabled-* edge drew the same edge and mark as an enabled one, and a radio's dot
   vanished. GrayText is the system colour for an unavailable control. The tick and the
   dash are borders, so they keep it; the dot is a fill, so it names a system colour. */
@media (forced-colors: active) {
  .ui-check input:disabled,
  .ui-check input:disabled:is(:checked, :indeterminate),
  .ui-check input[type="checkbox"]:disabled:is(:checked, :indeterminate),
  .ui-table__selection input[type="checkbox"]:disabled,
  .ui-table__selection input[type="checkbox"]:disabled:is(:checked, :indeterminate) { border-color: GrayText; }
  .ui-check input[type="checkbox"]:disabled:is(:checked, :indeterminate)::after,
  .ui-table__selection input[type="checkbox"]:disabled:is(:checked, :indeterminate)::after { border-color: GrayText; }
  .ui-check input[type="radio"]:enabled:checked::after { background: CanvasText; }
  .ui-check input[type="radio"]:disabled:checked::after { background: GrayText; }
}
/* The label is the click target, so the cursor belongs on it, and the words it
   holds go quiet with the box beside them. */
.ui-check:has(input:disabled) { /* muted-ink: state — unavailable control */ color: var(--disabled-ink); cursor: not-allowed; }

/* A hint under a box. It sits outside the label, so the label's name stays the label
   words and the hint is read as a description (aria-describedby), and it takes the
   field hint's type and ink. The left edge is the label words': the 19px box and the
   11px gap, a sum that lies between two grid steps for that reason alone. */
.ui-check-item { display: flex; flex-direction: column; align-items: flex-start; }
.ui-check__hint {
  color: var(--text);
  font-size: var(--text-sm);
  line-height: 1.5;
  margin-top: var(--space-1);
  margin-left: 30px;
}
.ui-check-item:has(input:disabled) .ui-check__hint { /* muted-ink: state — unavailable control */ color: var(--disabled-ink); }

/* A group of boxes under a legend, the legend in the field label's type. */
.ui-check-list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* Toggle switch */
.ui-switch {
  --sw-w: 44px;
  --sw-h: 26px;
  position: relative;
  display: inline-block;
  width: var(--sw-w);
  height: var(--sw-h);
  flex: none;
}
.ui-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.ui-switch__track {
  position: absolute;
  inset: 0;
  background: var(--surface-3); /* code-bg: inherit — control paint; the containing surface is the ground, for a chip and for a band alike. */
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background var(--dur-med) var(--ease);
}
.ui-switch__track::before {
  content: "";
  position: absolute;
  top: 3px;
  left: 3px;
  width: calc(var(--sw-h) - 6px);
  height: calc(var(--sw-h) - 6px);
  background: var(--control-knob);
  /* --shadow-sm was the knob's only edge until #295, and a white knob on a light
     off-track has 1.20:1 to spare. --border-strong and not --border, which measures
     1.03:1 against that track and draws nothing — the checkbox above edges itself
     the same way. why: docs/foundations.md#elevation */
  border: 1px solid var(--border-strong);
  box-sizing: border-box;
  border-radius: 50%;
  transition: transform var(--dur-med) var(--ease);
}
.ui-switch input:checked + .ui-switch__track { background: var(--accent); }
.ui-switch input:checked + .ui-switch__track::before {
  transform: translateX(calc(var(--sw-w) - var(--sw-h)));
}
.ui-switch input:focus-visible + .ui-switch__track { outline: var(--ring); outline-offset: var(--ring-offset); }
.ui-switch input:disabled + .ui-switch__track { opacity: 0.5; cursor: not-allowed; }
