/* ============================================
   XmField — abstract form-field chrome.

   The base renders label / control wrapper / helper-or-error row; the
   subclass drops only its concrete control into the control wrapper
   (AD-7). One shared control height per `size` so a field lines up beside
   a button of the same size (NFR-22). Surface is the card tier
   (inverse-surface) with inverse-on-surface ink (AD-13). Severity is the
   error icon + copy in the message row — never an error color, never a red
   field (AD-11 / rule 3a).

   Sizes mirror the button height ramp so fields snap to buttons:
     .field--xs   22px control
     .field--sm   26px control
     .field--md   32px control   (default)
     .field--lg   42px control

   BEM block: `field`. Registered in scripts/check-bem.sh STRICT_BLOCKS.
   ============================================ */

.field {
  display: flex;
  flex-direction: column;
  gap: 0;
  width: 100%;
}

/* ---------- Label row ---------- */
.field__label {
  display: inline-flex;
  align-items: center;
  gap: var(--s-1);
  color: var(--md-sys-color-inverse-on-surface);
  font:
    var(--md-sys-typescale-label-large-weight)
    var(--md-sys-typescale-label-large-size) /
    var(--md-sys-typescale-label-large-line-height)
    var(--md-sys-typescale-label-large-font);
  cursor: default;
  margin-bottom: var(--s-2);
}
.field__label-text {
  letter-spacing: 0;
}
.field__required {
  color: var(--md-sys-color-primary);
  font-weight: 700;
  line-height: 1;
}

/* ---------- Control wrapper ----------
   The wrapper carries the shared chrome: surface, border, radius, height,
   focus ring. The concrete control (slotted or shadow-rendered by the
   subclass) sits inside and inherits ink + type. */
.field__control {
  position: relative;
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--md-sys-color-outline-variant);
  border-radius: var(--md-sys-shape-corner-button);
  background: var(--md-sys-color-inverse-surface);
  color: var(--md-sys-color-inverse-on-surface);
  font:
    var(--md-sys-typescale-body-large-weight)
    var(--md-sys-typescale-body-large-size) /
    var(--md-sys-typescale-body-large-line-height)
    var(--md-sys-typescale-body-large-font);
  transition:
    border-color var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard),
    box-shadow var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard),
    background var(--md-sys-motion-duration-short3) var(--md-sys-motion-easing-standard);
}
.field__control:hover {
  border-color: var(--md-sys-color-outline);
}

/* Focus — ring is the canonical visualization; outline suppressed. The wrapper
   reacts to KEYBOARD focus of the inner control via :has(:focus-visible) so the
   coral ring shows on tab-in but not on a plain pointer click (matches the
   :focus-visible intent in the spec while still reacting to the slotted/native
   control nested inside the wrapper). */
.field__control:has(:focus-visible) {
  outline: none;
  border-color: var(--md-sys-color-primary);
  box-shadow: var(--xm-state-focus-ring);
}

/* Style whatever native control a subclass slots OR shadow-renders so the
   chrome owns the box and the control is a transparent, borderless,
   full-bleed inner. Two selector sets are required: ::slotted() matches a
   light-DOM `slot="control"` child; the scoped descendant set matches a
   control a subclass renders into shadow DOM via renderControl(). Without
   the descendant set a UA-default input fill shows through (a dark box on
   the light card surface) — the surface/ink foot-gun. */
.field__control ::slotted(input),
.field__control ::slotted(textarea),
.field__control ::slotted(select),
.field__control input,
.field__control textarea,
.field__control select {
  flex: 1;
  min-width: 0;
  width: 100%;
  appearance: none;
  border: none;
  outline: none;
  background: transparent;
  color: inherit;
  font: inherit;
  padding: 0 var(--xm-field-control-pad-x, var(--s-3));
  height: 100%;
  box-sizing: border-box;
}
.field__control ::slotted(input)::placeholder,
.field__control ::slotted(textarea)::placeholder,
.field__control input::placeholder,
.field__control textarea::placeholder {
  color: var(--xm-color-inverse-on-surface-muted);
}

/* ---------- Sizes — shared control height ---------- */
.field--xs .field__control { min-height: 22px; }
.field--sm .field__control { min-height: 26px; }
.field--md .field__control { min-height: 32px; }
.field--lg .field__control { min-height: 42px; }

.field--xs .field__control ::slotted(input),
.field--xs .field__control ::slotted(select) { padding: 0 var(--s-2); }

/* ---------- Loading ----------
   The control region is swapped for the spinner; the wrapper keeps its box
   so the field height does not jump (AD-9a). */
.field__loading {
  display: inline-flex;
  align-items: center;
  justify-content: flex-start;
  width: 100%;
  height: 100%;
  padding: 0 var(--s-3);
  color: var(--xm-color-inverse-on-surface-muted);
}
.field--loading .field__control {
  cursor: progress;
}

/* ---------- Disabled ----------
   Shared reduced emphasis, cursor, reverted hover; never an error color. */
.field--disabled {
  cursor: not-allowed;
}
/* No opacity on the control: it would dim the VALUE text (which inherits
   .field__control's color) below AA. Swap the ink to the disabled token so
   the value stays legible; the box is muted by its border + container. */
.field--disabled .field__control {
  cursor: not-allowed;
  color: var(--xm-color-inverse-on-surface-disabled);
  box-shadow: none;
}
.field--disabled .field__control:hover {
  border-color: var(--md-sys-color-outline-variant);
}
.field--disabled .field__control ::slotted(input)::placeholder,
.field--disabled .field__control ::slotted(textarea)::placeholder,
.field--disabled .field__control input::placeholder,
.field--disabled .field__control textarea::placeholder {
  color: var(--xm-color-inverse-on-surface-disabled);
}
.field--disabled .field__label {
  color: var(--xm-color-inverse-on-surface-disabled);
}

/* ---------- Readonly ----------
   Value visible but not editable; still focusable. Stays on the card
   (inverse-surface) family — a faint inverse-on-surface wash over the same
   surface reads as inert without swapping in the desk surface family. */
.field--readonly .field__control {
  background: color-mix(
    in oklab,
    var(--md-sys-color-inverse-on-surface) 5%,
    var(--md-sys-color-inverse-surface)
  );
  border-color: var(--md-sys-color-outline-variant);
}
.field--readonly .field__control:hover {
  border-color: var(--md-sys-color-outline-variant);
}

/* ---------- Helper / error message row ----------
   Severity is icon + copy. The error keeps the same ink as the helper —
   the icon and string carry the severity, not a color (AD-11 / rule 3a). */
.field__message {
  display: flex;
  align-items: flex-start;
  gap: var(--s-1);
  min-height: 1em;
  margin-top: var(--s-2);
  font:
    var(--md-sys-typescale-body-small-weight)
    var(--md-sys-typescale-body-small-size) /
    var(--md-sys-typescale-body-small-line-height)
    var(--md-sys-typescale-body-small-font);
}
.field__message--helper {
  color: var(--xm-color-inverse-on-surface-muted);
}
.field__message--error {
  color: var(--md-sys-color-inverse-on-surface);
}
.field__message--empty {
  min-height: 0;
  margin-top: 0;
}
.field__error-icon {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--md-sys-color-inverse-on-surface);
}
.field__message-text {
  flex: 1;
  min-width: 0;
  text-wrap: pretty;
}
