@scope (field-ui) {
  :where(:scope) {
    /* ── Tokens ── */
    --field-gap:             var(--a-space-2);
    --field-label-color:     var(--a-fg);
    --field-label-size:      var(--a-ui-sm);
    --field-label-weight:    var(--a-weight-medium);
    --field-required-color:  var(--a-danger-strong);
    --field-trailing-color:  var(--a-fg-subtle);
    --field-trailing-size:   var(--a-ui-sm);
    --field-hint-color:      var(--a-fg-muted);
    --field-hint-size:       var(--a-ui-sm);
    --field-error-color:     var(--a-danger-strong);
    --field-error-size:      var(--a-ui-sm);

    /* In inline mode, the label column auto-sizes by default (each
       field's label column is independent of its siblings). Consumers
       that want shared label-column alignment across stacked inline
       fields can raise this floor — e.g. 12rem in a multi-field form. */
    --field-label-inline-min: 0;
  }

  /* ── Base — single grid; children placed by [slot] attribute via
       named grid-areas. No row wrappers; no DOM reparenting. The
       template adapts via `:has()` to which slots are present so
       empty tracks don't leak column-gap. ── */
  :scope {
    box-sizing: border-box;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "label"
      "control"
      "message";
    column-gap: var(--field-gap);
    align-items: center;
  }

  /* A stacked field is a full-width form row — fill the container (form column,
     grid cell, or flex parent) rather than sizing to content. Inline fields stay
     intrinsic so compact filter bars keep their side-by-side layout. (bug-34) */
  :scope:not([inline]) {
    width: 100%;
  }

  /* Gate `row-gap` on actual message presence — otherwise the grid
     reserves --field-gap (~8px) between the control row and a
     zero-content message row, making field-ui[inline] visibly taller
     (~7-8 px) than non-field-ui siblings in a `<row-ui align="center">`.
     FB-54 (v0.6.32) — closes the trap for compact filter bars that
     mix field-ui with bare primitives. */
  :scope:has([slot="hint"], [slot="error"]) {
    row-gap: var(--field-gap);
  }

  /* Stacked + (trailing or action) → 2-col */
  :scope:has(> :is([slot="trailing"], [slot="action"])) {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "label   trailing"
      "control action"
      "message message";
  }

  /* Stacked + no label, no trailing → drop the empty top row. */
  :scope:not([label]):not(:has([slot="trailing"])) {
    grid-template-areas:
      "control"
      "message";
  }
  :scope:not([label]):not(:has([slot="trailing"])):has([slot="action"]) {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "control action"
      "message message";
  }

  /* Hide the label cell when the label attr is absent. */
  :scope:not([label]) [slot="label"] { display: none; }

  /* ── Slot styling ── */
  :scope [slot="label"] {
    grid-area: label;
    color: var(--field-label-color);
    font-size: var(--field-label-size);
    font-weight: var(--field-label-weight);
    cursor: pointer;
    min-width: 0;
  }
  :scope [slot="label"] > [data-field-required] {
    color: var(--field-required-color);
    margin-inline-start: 0.15em;
    font-weight: var(--a-weight-bold);
  }
  :scope [slot="trailing"] {
    grid-area: trailing;
    justify-self: end;
    color: var(--field-trailing-color);
    font-size: var(--field-trailing-size);
  }
  :scope > :not([slot]) {
    grid-area: control;
    min-width: 0;
  }
  :scope [slot="action"] {
    grid-area: action;
    justify-self: end;
  }
  :scope [slot="hint"],
  :scope [slot="error"] {
    grid-area: message;
    line-height: 1.3;
  }
  :scope [slot="hint"] {
    color: var(--field-hint-color);
    font-size: var(--field-hint-size);
  }
  :scope [slot="error"] {
    color: var(--field-error-color);
    font-size: var(--field-error-size);
    font-weight: var(--a-weight-medium);
  }

  /* ── Mode: inline — content slots on row 1, message row below
       (aligned with the control column so hint/error sit under the
       input, not under the label). Templates branch by which
       optional slots are present so we don't carry zero-width
       tracks + their gaps. ── */
  :scope[inline] {
    grid-template-columns: minmax(var(--field-label-inline-min), auto) minmax(0, 1fr);
    grid-template-areas:
      "label   control"
      ".       message";
  }
  /* Trailing sits at the control's trailing edge (after the input), not between
     label and control — inline trailing is a control affordance (⌘K hint,
     result count), so it reads at the row's end, mirroring input-ui's own
     trailing slot. (bug-18) */
  :scope[inline]:has([slot="trailing"]):not(:has([slot="action"])) {
    grid-template-columns: minmax(var(--field-label-inline-min), auto) minmax(0, 1fr) auto;
    grid-template-areas:
      "label   control trailing"
      ".       message message";
  }
  :scope[inline]:not(:has([slot="trailing"])):has([slot="action"]) {
    grid-template-columns: minmax(var(--field-label-inline-min), auto) minmax(0, 1fr) auto;
    grid-template-areas:
      "label   control action"
      ".       message message";
  }
  :scope[inline]:has([slot="trailing"]):has([slot="action"]) {
    grid-template-columns: minmax(var(--field-label-inline-min), auto) minmax(0, 1fr) auto auto;
    grid-template-areas:
      "label   control trailing action"
      ".       message message message";
  }
  :scope[inline]:not([label]):not(:has([slot="trailing"])):not(:has([slot="action"])) {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "control"
      "message";
  }
  :scope[inline]:not([label]):not(:has([slot="trailing"])):has([slot="action"]) {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "control action"
      "message message";
  }

  /* In inline mode, push compact toggle controls (switch / check /
     radio) to the row's end edge — settings rows then render label-
     left, control-right regardless of label length. Wide controls
     (input / textarea / select) keep their default stretch behavior
     so they fill the trailing column. Already in HEAD as `356a39f`
     against the row-wrapper model; the second selector here keeps
     the rule alive once the v2 flat-DOM refactor (§18) lands. */
  :scope[inline]:has(:is(switch-ui, check-ui, radio-ui)) > [data-row="control"] > :not([slot="action"]),
  :scope[inline]:has(> :is(switch-ui, check-ui, radio-ui)) > :not([slot]) {
    justify-self: end;
  }
  /* ── Prop: align — label alignment within its grid area ── */
  /* Default (start): label text is left-aligned (justify-self: start). */
  /* align="end": label text is right-aligned within the label cell.   */
  /* Useful in multi-field forms where labels share a visual column     */
  /* and trailing-flush alignment aids scanability (right-to-input).   */
  :scope[align="end"] [slot="label"] {
    justify-self: end;
    text-align: end;
  }

}
