/**
 * Form fields — canonical shared component (A-08b)
 * ----------------------------------------------------------------------------
 * `.tr-field` is the canonical form-field component for the theme, per
 * CLAUDE.md §9.4a and impeccable/DESIGN.md §6. It is the first shared answer to
 * AUDIT A-08: before it, the theme had bare element styling and no error,
 * success or disabled state for any control, so every module solved it locally.
 *
 * SHIPPED WITH ZERO CONSUMERS, deliberately. This task builds the foundation
 * only. No template, module stylesheet or script references `.tr-field` yet;
 * migration is A-08c. The file loads globally and is inert until markup adopts
 * the classes — the same rationale inc/unified-components.php already documents
 * for cards, buttons and badges.
 *
 * Structure:
 *   .tr-field                     wrapper (label + control + help/error)
 *     .tr-field__label            <label for="…">
 *       .tr-field__required       the required marker inside the label
 *     .tr-field__control          <input> / <select> / <textarea>
 *     .tr-field__help             persistent hint
 *     .tr-field__error            validation message, written by TiorAI.Field
 *   Modifiers: --error --success --disabled --flat --sm
 *   Layout:    .tr-form__row  .tr-form__actions
 *
 * Contract decisions, and why:
 *
 *   Geometry matches `.btn` — `min-height: 44px`, `--radius-default` (12px),
 *   `--font-size-base` (16px). Fields and buttons sit next to each other in
 *   every form, and the legacy field styling used `--radius-md` (6px) against
 *   the button's 12px. 16px is also the iOS focus-zoom guard, which today
 *   exists in exactly one place in the theme (`.signin-modal__input`).
 *
 *   Rest fill is `--surface-sunken`. The token's own source comment names
 *   inputs first ("inputs, code blocks, media wells — recessed below base").
 *   In light the alternative is `--surface-base` (#ffffff) on a white page,
 *   where the 1px `--border-default` (#e2e8f0) is the only thing separating the
 *   field from the page at 1.24:1 — a recessed fill gives the affordance a
 *   second, non-colour cue. `--flat` opts back into `--surface-base` for forms
 *   that sit on a card where a sunken fill would read as a hole.
 *
 *   Rest boundary is `--tiorai-field-border` (A-08c1b). A-08b shipped this on
 *   `--border-default` and recorded the failure it could not resolve alone:
 *   1.13:1 against the fill, where SC 1.4.11 wants 3:1 on the information that
 *   identifies a component. The first live consumer settled it, and the fix is
 *   a field-only semantic token rather than a redefinition of a shared neutral,
 *   because a 3:1 line on every card, table and divider in the theme is a
 *   different decision that nobody asked for. 3.12:1 light / 4.21:1 dark.
 *
 *   Focus is `outline` only — the A-02/A-02b tokens, no box-shadow halo and no
 *   transform. An outline paints outside the border box so focus can never
 *   shift layout, and the tokens are already theme-paired and measured
 *   (4.48:1 light / 7.93:1 dark). The halo the legacy fields carried could not
 *   reach 3:1 at any alpha over either page colour, so it added nothing this
 *   ring does not already do. NEVER write `outline: none` here — that is the
 *   defect A-02, A-02b and A-08a each had to repair.
 *
 *   Status colours read the A-09a on-tint family rather than the raw status
 *   fills, because the raw fills are 500-band values that do not pass as text:
 *   `--color-danger` (#ef4444) measures 3.76:1 on the light field and
 *   `--color-success` (#10b981) 2.56:1, both short of 4.5:1. The on-tint tokens
 *   are theme-paired steps on the same ramp and pass in both themes as text AND
 *   as a border (measurements in the state blocks below).
 *
 *   The error state does NOT tint the control fill. Tinting the surface a user
 *   is typing into lowers the contrast of their own text, which is the primary
 *   content; the border, the label marker and the message carry the state
 *   instead. This also keeps every ratio here measurable against one known
 *   surface rather than a translucent stack.
 *
 *   No `--space-8`, `--space-16` or `--space-24`: a second `:root` later in
 *   theme-unified.css silently overrides all three (AUDIT A-28, unresolved).
 *
 *   Logical properties throughout, so no `[dir="rtl"]` rule is ever needed.
 *
 * @package TiorAI
 * @since 1.15.0
 */

/* ============================================================================
   FIELD WRAPPER
   ========================================================================== */

.tr-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

/* ============================================================================
   LABEL
   ========================================================================== */

.tr-field__label {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-medium);
  line-height: var(--line-height-normal);
  color: var(--color-text-primary);
}

/* The required marker is a separate element so the accessible name stays clean:
   mark the control with `required` / `aria-required`, and let this carry the
   visual cue. Uses the on-tint danger step (8.31:1 light / 7.07:1 dark on the
   field surface) rather than --color-danger, which is 3.76:1 in light. */
.tr-field__required {
  color: var(--tiorai-on-danger-tint);
  margin-inline-start: var(--space-1);
}

/* ============================================================================
   CONTROL — input / select / textarea
   ========================================================================== */

.tr-field__control {
  width: 100%;
  min-width: 0;
  min-height: 44px;
  padding-block: var(--space-3);
  padding-inline: var(--space-4);
  background: var(--surface-sunken);
  color: var(--color-text-primary);
  /* A-08c1b: was --border-default, which measured 1.13:1 against this fill —
     the one contract decision A-08b recorded and deliberately deferred, since
     it is a border-language question and there were no consumers to judge it
     against. The first live consumer (the User Hub Profile form) settled it.
     --tiorai-field-border is a dedicated semantic token: 3.12:1 light /
     4.21:1 dark on this fill, so a control's boundary now clears SC 1.4.11.
     Only form controls read it — see its definition in theme-unified.css. */
  border: 1px solid var(--tiorai-field-border);
  border-radius: var(--radius-default);
  font-family: var(--font-family-body);
  font-size: var(--font-size-base);
  line-height: var(--line-height-normal);
  transition: border-color 0.15s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.15s cubic-bezier(0.4, 0, 0.2, 1);
}

/* --color-text-tertiary was the obvious choice and was rejected on measurement:
   #64748b on the light sunken fill (#f1f5f9) is 4.34:1, just under the 4.5:1
   text minimum. It passes on white (4.76:1), which is why the legacy fields got
   away with it — the recessed fill is what pushed it below the bar. The
   secondary token measures 6.94:1 on the same fill and 6.35:1 in dark. */
.tr-field__control::placeholder {
  color: var(--color-text-secondary);
  opacity: 1; /* Firefox dims placeholders by default; the token is already the muted step. */
}

/* Focus: the canonical ring, nothing else. No halo, no transform, no geometry
   change — `outline` paints outside the border box, so 0px layout shift. The
   border also steps to the brand-dark hue, which independently measures 3.74:1
   light / 5.23:1 dark, so the state survives even where an outline is clipped. */
.tr-field__control:focus-visible {
  outline: var(--tiorai-focus-ring-width) solid var(--tiorai-focus-ring-color);
  outline-offset: var(--tiorai-focus-ring-offset);
  border-color: var(--color-primary-dark);
}

/* Pointer focus keeps the border cue without the ring; the global
   `*:focus:not(:focus-visible)` rule from A-02 handles the outline itself. */
.tr-field__control:focus {
  border-color: var(--color-primary-dark);
}

textarea.tr-field__control {
  min-height: 7rem;
  resize: vertical;
}

/* Native arrow retained: a custom one would need a background-image, which
   cannot follow the theme's text colour across light and dark. */
select.tr-field__control {
  appearance: auto;
  cursor: pointer;
}

/* File inputs have no author-styleable text box; keep the box metrics but let
   the UA draw the button so the control never loses its affordance. */
.tr-field__control[type="file"] {
  padding-block: var(--space-2);
  cursor: pointer;
}

/* ============================================================================
   HELP AND ERROR TEXT
   ========================================================================== */

.tr-field__help {
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
  color: var(--color-text-secondary);
}

/* Hidden until TiorAI.Field.setError writes into it. `:empty` rather than a
   state class so the element can sit in the markup permanently and keep a
   stable id for aria-describedby. */
.tr-field__error {
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
  font-weight: var(--font-weight-medium);
  color: var(--tiorai-on-danger-tint);
}

.tr-field__error:empty {
  display: none;
}

/* ============================================================================
   STATE — ERROR
   ----------------------------------------------------------------------------
   Foreground: --tiorai-on-danger-tint. Measured on the field fill —
   light  #991b1b on #f1f5f9 = 7.58:1  (on #ffffff = 8.31:1)
   dark   #f87171 on #07080b = 7.07:1
   Both clear 4.5:1 as text and 3:1 as a border. --color-danger was rejected:
   #ef4444 measures 3.76:1 on the light field, which fails as text.
   ========================================================================== */

.tr-field--error .tr-field__control {
  border-color: var(--tiorai-on-danger-tint);
}

.tr-field--error .tr-field__control:focus-visible {
  /* The ring still wins as the focus signal; the error border stays so the two
     states are distinguishable while focused. */
  border-color: var(--tiorai-on-danger-tint);
}

/* ============================================================================
   STATE — SUCCESS
   ----------------------------------------------------------------------------
   Foreground: --tiorai-on-success-tint. Measured on the field fill —
   light  #065f46 on #f1f5f9 = 7.11:1  (on #ffffff = 7.79:1)
   dark   #10b981 on #07080b = 7.81:1
   --color-success was rejected for light: #10b981 measures 2.56:1 on #ffffff,
   below even the 3:1 non-text bar.
   ========================================================================== */

.tr-field--success .tr-field__control {
  border-color: var(--tiorai-on-success-tint);
}

.tr-field--success .tr-field__control:focus-visible {
  border-color: var(--tiorai-on-success-tint);
}

.tr-field__success-message {
  font-size: var(--font-size-xs);
  line-height: var(--line-height-normal);
  font-weight: var(--font-weight-medium);
  color: var(--tiorai-on-success-tint);
}

.tr-field__success-message:empty {
  display: none;
}

/* ============================================================================
   STATE — DISABLED
   ----------------------------------------------------------------------------
   No `pointer-events: none`. `.btn` uses it because a disabled button must not
   swallow a click, but a disabled field still needs to be hoverable so a
   title/tooltip explaining why it is locked can be read, and native disabled
   semantics already block interaction and remove it from the tab order.

   A-08c1b: the boundary deliberately does NOT move to --tiorai-field-border.
   It stays --border-default, which measures 1.18:1 on the disabled fill in
   light — and that is the point. A disabled control is an "inactive user
   interface component", which SC 1.4.11 exempts by name, and the DROP from the
   3.12:1 rest boundary to a nearly invisible one is itself the state cue, in
   the same way the fill and the value both step back. Making disabled as loud
   as rest would erase the distinction the new token just created.
   ========================================================================== */

.tr-field--disabled .tr-field__control,
.tr-field__control:disabled {
  background: var(--surface-disabled);
  color: var(--color-text-disabled);
  border-color: var(--border-default);
  cursor: not-allowed;
}

.tr-field__control:disabled::placeholder {
  color: var(--color-text-disabled);
}

/* The LABEL AND HELP TEXT ARE NOT GREYED. Measured, they would land at 2.56:1 —
   and unlike the control's own value, they are not "inactive user interface
   components", so 1.4.3 is not waived for them. They are also the only thing
   telling the user what the locked field is and why, which is exactly when that
   information matters most. Only the control's value dims (2.45:1 on the
   disabled fill), which is the documented token pair and is exempt. */

/* ============================================================================
   STATE — BUSY
   ----------------------------------------------------------------------------
   Set by TiorAI.Field.setBusy on the wrapper, alongside `aria-busy`. The helper
   also disables the control, so the disabled treatment above supplies the
   visual change; this only corrects the cursor over the surrounding label and
   help text, which are not disabled elements.
   ========================================================================== */

.tr-field--busy {
  cursor: progress;
}

/* ============================================================================
   MODIFIER — FLAT
   ----------------------------------------------------------------------------
   For fields hosted on a card or panel, where the sunken fill would read as a
   hole punched in the surface rather than a recess in the page.

   A-08c1b: only the fill changes, so the boundary is inherited from
   .tr-field__control and is --tiorai-field-border here too. Measured on this
   fill: 3.42:1 light (#7e8ca0 on #ffffff) / 4.11:1 dark (#64748b on #0B0C10).
   Both clear 3:1, so --flat is not the weaker variant it was before.
   ========================================================================== */

.tr-field--flat .tr-field__control {
  background: var(--surface-base);
}

.tr-field--flat .tr-field__control:disabled,
.tr-field--flat.tr-field--disabled .tr-field__control {
  background: var(--surface-disabled);
}

/* ============================================================================
   MODIFIER — SMALL
   ----------------------------------------------------------------------------
   36px to match `.btn--sm`. Font size stays 16px: dropping it is what causes
   iOS to zoom the viewport on focus, and this modifier exists for density, not
   for smaller text.
   ========================================================================== */

.tr-field--sm .tr-field__control {
  min-height: 36px;
  padding-block: var(--space-2);
  padding-inline: var(--space-3);
}

/* ============================================================================
   CHECKBOX AND RADIO
   ----------------------------------------------------------------------------
   Native controls tinted with `accent-color`, matching `.filter-checkbox` in
   unified/filters.css — the one checkbox implementation in the theme that was
   already correct, because it keeps the UA widget and therefore keeps the
   canonical focus ring. Custom-drawn boxes (`.signin-modal__checkbox-custom`,
   `.toggle-switch`, `.theme-option`) stay specialised and are not replaced.
   ========================================================================== */

.tr-field--choice {
  flex-direction: row;
  align-items: center;
  gap: var(--space-3);
}

/* A-08d: `accent-color` reads --tiorai-field-accent, not --color-primary.
   #00B4D8 measured 2.46:1 on white and 2.25:1 on the sunken fill — below the
   3:1 non-text minimum on every surface a checkbox sits on in light, and the
   checked tint IS the state indicator, so this was a 1.4.11 failure rather
   than a decorative one. The token is #00829e in light (4.48 / 4.09) and stays
   #00B4D8 in dark (8.13 / 7.46), where darkening would make the state quieter.
   Full derivation on the token in theme-unified.css.

   Size is deliberately UNCHANGED. SC 2.5.8's 24x24 target applies to the
   clickable target, and for a choice field that is the wrapping
   `.tr-field--choice .tr-field__label`, not the box — so enlarging the box
   would be a visual change with layout risk and no accessibility gain. */
.tr-field__checkbox,
.tr-field__radio {
  flex-shrink: 0;
  width: 1rem;
  height: 1rem;
  margin: 0;
  accent-color: var(--tiorai-field-accent);
  cursor: pointer;
}

.tr-field__checkbox:disabled,
.tr-field__radio:disabled {
  cursor: not-allowed;
}

/* The label is the click target for a choice control, so it carries the cursor
   and the full remaining width. */
.tr-field--choice .tr-field__label {
  cursor: pointer;
  font-weight: var(--font-weight-normal);
}

.tr-field--choice.tr-field--disabled .tr-field__label {
  cursor: not-allowed;
}

/* ============================================================================
   FORM LAYOUT
   ----------------------------------------------------------------------------
   Two helpers only. Anything more belongs to the consuming template — the
   shared layer should not own page composition.
   ========================================================================== */

.tr-form__row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
  gap: var(--space-4);
}

.tr-form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* ============================================================================
   MOTION, CONTRAST, FORCED COLORS
   ----------------------------------------------------------------------------
   The global `prefers-reduced-motion` block already clamps every transition in
   the theme, and A-02 already maps `*:focus-visible` to the system Highlight
   under forced-colors, so neither is repeated here. What IS needed is the part
   those globals cannot express: under forced-colors the author border colour is
   discarded, so the error and success states would become indistinguishable
   from rest. They are re-expressed with system colours and a width change,
   which survives colour substitution.
   ========================================================================== */

@media (prefers-contrast: more) {
  .tr-field__control {
    border-width: 2px;
  }
}

@media (forced-colors: active) {
  .tr-field__control {
    border-color: ButtonBorder;
  }

  .tr-field--error .tr-field__control,
  .tr-field--success .tr-field__control {
    border-width: 2px;
    border-color: currentColor;
  }

  .tr-field__control:disabled,
  .tr-field--disabled .tr-field__control {
    color: GrayText;
    border-color: GrayText;
  }
}
