/* =============================================================================
   _form-field.css — canonical GENERIC FORM-FIELD component (the `.field` well
   for plain text inputs AND textareas). Extracted from comp-inputs.html /
   comp-textarea.html so the DS cards, the Modal compound (its dialog bodies
   host these fields), and any other surface share ONE source instead of the
   old per-surface copy.

   Scope: a `.field` that is NOT a search field (`.search .field`, in _field.css)
   nor a combobox (`.combo .field` / `.tb-select`, in _select.css). Those two
   override this base at higher specificity, so loading all three together is
   safe — this provides the well + states; they layer their own affordances on
   top.

   The single base covers both controls: a `<textarea>` inside the field is
   detected with `:has(textarea)` and switches to the multiline metrics
   (top-aligned icon, taller padding, the 14.5px reading size). Import AFTER
   colors_and_type.css (tokens + @layer order); dark mode rides the tokens.
   ============================================================================= */

@layer components {
  .field {
    /* --field-gap drives BOTH the flex gap AND the clear-button's negative
       margin when it collapses (so the reclaim always cancels the real gap).
       Search overrides it to 8px in _field.css. */
    --field-gap: 6px;
    display: flex; align-items: center; gap: var(--field-gap);
    /* positioning context for the clear-out ghost overlay (_form-field.js) */
    position: relative;
    /* Query container so the leading icon can hide itself when the field gets
       narrow (responsive space-saver). Named `field-q` so the @container rule
       below can't accidentally match another container. */
    container: field-q / inline-size;
    padding: 8px 12px;
    border: 1px solid var(--color-border-default); border-radius: 6px;
    background: var(--field-bg);
    font: 400 14px var(--font-sans); color: var(--color-fg-default);
    /* box-shadow joins the transition list so the focus ring fades rather than
       snapping. Hover stays snappy (100ms linear); the springier bloom is
       reserved for focus, below. Same two-tier pattern as comp-buttons.html. */
    transition: background-color 100ms linear,
                border-color 100ms linear,
                box-shadow 140ms ease-out;
  }

  /* ── Multiline (textarea) — top-aligned icon + taller reading metrics. ── */
  .field:has(textarea) {
    align-items: flex-start;
    gap: 8px;
    padding: 10px 12px;
    font-size: 14.5px;
  }

  /* ── Inner control reset (both <input> and <textarea>). ── */
  .field input,
  .field textarea {
    border: 0; outline: 0; flex: 1; width: 100%; min-width: 0;
    background: transparent; font: inherit; color: inherit;
  }
  .field textarea { resize: vertical; min-height: 80px; line-height: 1.5; }

  /* ── Leading icon — follows the interactive state: tertiary → secondary on
     hover → primary-500 on focus. `.field > i` scopes this to the leading
     glyph; nested icons (counters, buttons) opt out. ── */
  .field > i { color: var(--color-fg-tertiary); flex: none; transition: color 100ms linear; }
  .field:has(textarea) > i { padding-top: 2px; }
  .field:hover:not(.field--disabled):not(.field--error):not(.field--focus):not(:focus-within) > i,
  .field[data-state="hover"]:not(.field--disabled):not(.field--error):not(.field--focus):not(:focus-within) > i { color: var(--color-fg-secondary); }
  .field--focus > i,
  .field:focus-within > i { color: var(--gov-color-primary-500); }
  /* Error variant — red across hover / focus; default stays muted so the icon
     doesn't duplicate the red border at rest. */
  .field--error:hover:not(.field--disabled):not(.field--focus):not(:focus-within) > i,
  .field--error[data-state="hover"]:not(.field--disabled):not(.field--focus):not(:focus-within) > i { color: var(--gov-color-error-700); }
  .field--error.field--focus > i,
  .field--error:focus-within > i { color: var(--gov-color-error-500); }

  /* ── Hover — enabled-only highlight; tints the field bg like DSGCZ. ── */
  .field:hover:not(.field--disabled):not(.field--error):not(.field--focus):not(:focus-within),
  .field[data-state="hover"]:not(.field--disabled):not(.field--error):not(.field--focus):not(:focus-within) { background: var(--gov-color-primary-50); }
  /* Error hover — red tint (error-50) mirrors the blue hover; same token as the
     error notice background, so the two stay paired. */
  .field--error:hover:not(.field--disabled):not(.field--focus):not(:focus-within),
  .field--error[data-state="hover"]:not(.field--disabled):not(.field--focus):not(:focus-within) { background: var(--gov-color-error-50); }

  /* ── Focus lives on the WRAPPER (`:focus-within` OR the demo `.field--focus`).
     The inner control's global :focus-visible ring is suppressed to avoid
     doubling. The ring blooms (spacer + halo grow from 0-spread, expo-out). ── */
  .field--focus,
  .field:focus-within {
    border-color: var(--gov-color-primary-500); box-shadow: var(--shadow-focus);
    transition: background-color 100ms linear,
                border-color 140ms ease-out,
                box-shadow 220ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .field input:focus, .field input:focus-visible,
  .field textarea:focus, .field textarea:focus-visible { box-shadow: none; }

  .field--error { border-color: var(--gov-color-error-500); }
  /* Error focus — re-pin the red border + swap the ring to a red rgba so the
     validation state stays visible while editing (the blue ring would hide it). */
  .field--error.field--focus,
  .field--error:focus-within { border-color: var(--gov-color-error-500); box-shadow: var(--shadow-focus-error); }

  .field--disabled { background: var(--field-disabled-bg); border-style: var(--field-disabled-bd-style); color: var(--color-fg-tertiary); }
  .field--disabled input,
  .field--disabled textarea { color: var(--color-fg-tertiary); cursor: not-allowed; }
}

/* Narrow-field space-saver: hide the leading inset icon below 150px so the
   value keeps its room. Unlayered so the display:none beats the @layer reset
   `.bi { display:inline-flex }`. Only the leading `> i` is targeted. */
@container field-q (max-width: 150px) {
  .field > i { display: none; }
}

/* ── clear button (optional · real <button> — tab-reachable, AT-announced) ──
   Opt-in two ways: author `<button class="clear">` in the markup, or set
   `data-clearable` on the .field and _form-field.js injects it. A leading icon
   may coexist (icon · input · clear). Shown only while the field has a value;
   an empty field collapses the box AND cancels the flex gap (via --field-gap)
   so the input/placeholder reclaims the full width. The search field reuses
   THIS button (size overrides + --field-gap:8px in _field.css). UNLAYERED so
   its own :focus-visible ring beats the @layer base ring — same as the search
   clear was. ─────────────────────────────────────────────────────────────── */
.field .clear {
  appearance: none; -webkit-appearance: none;
  margin: 0; padding: 0; border: 0;
  background: transparent;
  width: 22px; height: 22px; flex: none;
  box-sizing: border-box; overflow: hidden;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--color-fg-tertiary);
  border-radius: 999px;
  cursor: pointer;
  user-select: none; -webkit-user-select: none;   /* controls never text-select */
  font: inherit; line-height: 1;
  opacity: 1;
  transform: scale(1);
  transform-origin: center;
  visibility: visible;
  transition: background-color 100ms linear,
              color 100ms linear,
              box-shadow 140ms ease-out,
              opacity 160ms ease-out,
              transform 200ms cubic-bezier(0.16, 1, 0.3, 1),
              width 220ms cubic-bezier(0.16, 1, 0.3, 1),
              margin 220ms cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 0s;
}
.field .clear > i { font-size: 14px; line-height: 1; }
.field[data-empty="true"] .clear {
  opacity: 0;
  transform: scale(0.6);
  visibility: hidden;
  pointer-events: none;
  /* reclaim the footprint: collapse the box AND cancel the flex gap so an
     empty field gives the input/placeholder the full width. Re-expands on the
     first keystroke. The negative margin tracks --field-gap. */
  width: 0;
  margin-left: calc(-1 * var(--field-gap));
  transition: background-color 100ms linear,
              color 100ms linear,
              box-shadow 140ms ease-out,
              opacity 120ms ease-in,
              transform 160ms cubic-bezier(0.4, 0, 1, 0.6),
              width 200ms cubic-bezier(0.4, 0, 1, 0.6),
              margin 200ms cubic-bezier(0.4, 0, 1, 0.6),
              visibility 0s linear 160ms;
}
.field .clear:hover,
.field .clear[data-state="hover"] {
  background: var(--gov-color-neutral-100);
  color: var(--color-fg-default);
}
.field .clear:active,
.field .clear[data-state="pressed"] {
  background: var(--gov-color-neutral-200);
  color: var(--color-fg-default);
  box-shadow: inset 0 1px 2px rgba(12,24,56,0.12);
}
.field .clear:focus-visible,
.field .clear.is-focus {
  outline: none;
  background: var(--color-bg-surface);
  color: var(--color-fg-default);
  box-shadow: var(--shadow-focus);
  transition: background-color 100ms linear,
              color 100ms linear,
              box-shadow 220ms cubic-bezier(0.16, 1, 0.3, 1),
              opacity 160ms ease-out,
              transform 200ms cubic-bezier(0.16, 1, 0.3, 1);
}
.field .clear:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background: transparent;
  box-shadow: none;
}

/* ── edge fade — soften the cut-off of an overflowing value / placeholder.
   _form-field.js toggles .fade-l / .fade-r on the input from its scroll +
   overflow state; the mask fades the text at whichever edge is clipped instead
   of a hard crop. The mask is ALWAYS applied with animatable widths (registered
   @property lengths, default 0 = no visible fade) so adding/removing a class
   GROWS/SHRINKS the fade rather than snapping it. Purely cosmetic (doesn't
   change the caret or hit-testing). A clipped PLACEHOLDER fades wider
   (.fade-soft) since no clear button reserves room on its right. Single-line
   only — `.field input` never matches a <textarea>. The search field inherits
   this verbatim. ──────────────────────────────────────────────────────────── */
@property --fade-l-w { syntax: "<length>"; inherits: false; initial-value: 0px; }
@property --fade-r-w { syntax: "<length>"; inherits: false; initial-value: 0px; }
.field input {
  --fade-mask-w: 16px;
  --fade-l-w: 0px; --fade-r-w: 0px;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l-w), #000 calc(100% - var(--fade-r-w)), transparent 100%);
          mask-image: linear-gradient(to right, transparent 0, #000 var(--fade-l-w), #000 calc(100% - var(--fade-r-w)), transparent 100%);
  transition: --fade-l-w 200ms ease, --fade-r-w 200ms ease;
}
.field input.fade-r { --fade-r-w: var(--fade-mask-w); }
.field input.fade-l { --fade-l-w: 16px; }
.field input.fade-soft { --fade-mask-w: 44px; }

/* ── dark mode — neutral hover/pressed re-tints for the clear button (the
   neutral tokens flip very dark; re-tint with translucent white over the dark
   well). --shadow-focus is already remapped in _dark.css. ─────────────────── */
body[data-theme="dark"] .field .clear:hover,
body[data-theme="dark"] .field .clear[data-state="hover"] {
  background: rgba(255,255,255,0.08);
  color: var(--color-fg-default);
}
body[data-theme="dark"] .field .clear:active,
body[data-theme="dark"] .field .clear[data-state="pressed"] {
  background: rgba(255,255,255,0.14);
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.45);
}
body[data-theme="dark"] .field .clear:focus-visible,
body[data-theme="dark"] .field .clear.is-focus {
  background: rgba(255,255,255,0.06);
  color: var(--color-fg-default);
}

/* ── field foot + character counter ──────────────────────────────────────────
   `.field__foot` is the row UNDER a field that pairs the help / error message
   (left, flexes + wraps) with a live `.field__count` (right, never wraps). The
   counter reads "<n> / <min>" with a slight hint of whether a length condition
   is met: muted by default, success colour + a check glyph once satisfied
   (`.is-ok`). Tabular figures so the width doesn't jitter as digits change. */
.field__foot { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.field__foot > :first-child { flex: 1 1 auto; min-width: 0; }
.field__count {
  flex: none; white-space: nowrap; font-size: 12px; line-height: 1.4;
  font-variant-numeric: tabular-nums; color: var(--color-fg-tertiary);
  display: inline-flex; align-items: center; gap: 4px;
  transition: color 160ms ease-out;
}
.field__count > i { font-size: 12px; }
.field__count.is-ok { color: var(--gov-color-success-700); }
