/*! mica — custom elements, native behavior, nearly no JavaScript. */

/* Layer order. User's unlayered CSS always wins over all of it. */
@layer mica.tokens, mica.preset, mica.elements, mica.layout;

/* ------------------------------------------------------------------ *
 * Tokens — mica's only opinion. Spacing and size scales.
 * ------------------------------------------------------------------ */
@layer mica.tokens {
  :root {
    --space-2xs: 0.25rem;
    --space-xs: 0.5rem;
    --space-sm: 0.75rem;
    --space-md: 1rem;
    --space-lg: 1.5rem;
    --space-xl: 2.5rem;
    --space-2xl: 4rem;

    --size-xs: 10rem;
    --size-sm: 15rem;
    --size-md: 20rem;
    --size-lg: 30rem;
    --size-xl: 40rem;

    --measure: 60ch;

    --font-body: system-ui, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    --line-height: 1.5;

    /* square is the design language; rounding back on is a theme:
       :root { --radius-sm: .25rem; --radius-md: .375rem; --radius-lg: .75rem } */
    --radius-sm: 0px;
    --radius-md: 0px;
    --radius-lg: 0px;
    --radius-full: 9999px;

    /* control heights, shadcn scale: sm 32 / md 36 / lg 40 */
    --control-height-sm: 2rem;
    --control-height: 2.25rem;
    --control-height-lg: 2.5rem;

    --focus-ring-width: 2px;
    --focus-ring-offset: 2px;
    --focus-ring-color: var(--color-accent);

    /* checkmark glyph — SVG URIs can't read custom properties, and
       light-dark() only accepts <color>s: feeding it url()s is invalid
       (Chromium computes it to none; iOS Safari drops the glyph at
       paint). So: light glyph by default, dark swapped in by the media
       query after this block. The pair tracks on-primary (white /
       near-black, since primary inverts in dark).
       Forcing a scheme against the OS? Also pin the glyph:
         :root { color-scheme: dark; --check-glyph: var(--check-glyph-dark); } */
    --check-glyph-light: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23fff' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
    --check-glyph-dark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath fill='none' stroke='%23151922' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round' d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E");
    --check-glyph: var(--check-glyph-light);

    /* -------------------------------------------------------------- *
     * Color. Two knobs — change the theme by changing the knobs:
     *   --hue: the accent hue (OKLCH degrees)
     *   --chroma: accent intensity
     * Neutrals are faintly tinted toward the accent hue.
     * Components only ever use semantic roles, never raw values.
     * -------------------------------------------------------------- */
    color-scheme: light dark;

    --hue: 263;
    --chroma: 0.21;
    --neutral-chroma: 0;

    /* status hues — knobs, same as the brand hue. statuses deliberately
       do NOT ride --chroma: danger must stay urgent in a muted theme. */
    --danger-hue: 25;
    --success-hue: 150;
    --warn-hue: 48; /* orange — amber reads as mud at solid lightness */
    --status-chroma: 0.18;

    /* Numbered scales — theme-relative (Radix convention): a step means
       the same *thing* in both modes. 1 = app bg … 6–8 = borders …
       9 = solid … 12 = high-contrast text. light-dark() is baked into
       every step, so code written against steps gets dark mode free. */
    --neutral-1:  light-dark(oklch(100%  var(--neutral-chroma) var(--hue)), oklch(14% var(--neutral-chroma) var(--hue)));
    --neutral-2:  light-dark(oklch(97.3% var(--neutral-chroma) var(--hue)), oklch(17% var(--neutral-chroma) var(--hue)));
    --neutral-3:  light-dark(oklch(95.2% var(--neutral-chroma) var(--hue)), oklch(20% var(--neutral-chroma) var(--hue)));
    --neutral-4:  light-dark(oklch(93%   var(--neutral-chroma) var(--hue)), oklch(23% var(--neutral-chroma) var(--hue)));
    --neutral-5:  light-dark(oklch(91%   var(--neutral-chroma) var(--hue)), oklch(26% var(--neutral-chroma) var(--hue)));
    --neutral-6:  light-dark(oklch(89%   var(--neutral-chroma) var(--hue)), oklch(30% var(--neutral-chroma) var(--hue)));
    --neutral-7:  light-dark(oklch(82%   var(--neutral-chroma) var(--hue)), oklch(37% var(--neutral-chroma) var(--hue)));
    --neutral-8:  light-dark(oklch(74%   var(--neutral-chroma) var(--hue)), oklch(46% var(--neutral-chroma) var(--hue)));
    --neutral-9:  light-dark(oklch(55%   var(--neutral-chroma) var(--hue)), oklch(58% var(--neutral-chroma) var(--hue)));
    --neutral-10: light-dark(oklch(50%   var(--neutral-chroma) var(--hue)), oklch(64% var(--neutral-chroma) var(--hue)));
    --neutral-11: light-dark(oklch(44%   var(--neutral-chroma) var(--hue)), oklch(71% var(--neutral-chroma) var(--hue)));
    --neutral-12: light-dark(oklch(18%   var(--neutral-chroma) var(--hue)), oklch(96% var(--neutral-chroma) var(--hue)));

    /* accent scale: chroma tapers at the extremes, peaks at the solid */
    --accent-1:  light-dark(oklch(99%   calc(var(--chroma) * 0.1) var(--hue)), oklch(19% calc(var(--chroma) * 0.15) var(--hue)));
    --accent-2:  light-dark(oklch(97.5% calc(var(--chroma) * 0.15) var(--hue)), oklch(23% calc(var(--chroma) * 0.2) var(--hue)));
    --accent-3:  light-dark(oklch(95%   calc(var(--chroma) * 0.25) var(--hue)), oklch(27% calc(var(--chroma) * 0.35) var(--hue)));
    --accent-4:  light-dark(oklch(92%   calc(var(--chroma) * 0.35) var(--hue)), oklch(31% calc(var(--chroma) * 0.5) var(--hue)));
    --accent-5:  light-dark(oklch(89%   calc(var(--chroma) * 0.45) var(--hue)), oklch(35% calc(var(--chroma) * 0.6) var(--hue)));
    --accent-6:  light-dark(oklch(85%   calc(var(--chroma) * 0.55) var(--hue)), oklch(39% calc(var(--chroma) * 0.7) var(--hue)));
    --accent-7:  light-dark(oklch(79%   calc(var(--chroma) * 0.65) var(--hue)), oklch(46% calc(var(--chroma) * 0.8) var(--hue)));
    --accent-8:  light-dark(oklch(71%   calc(var(--chroma) * 0.8) var(--hue)), oklch(58% calc(var(--chroma) * 0.9) var(--hue)));
    --accent-9:  light-dark(oklch(55%   var(--chroma) var(--hue)), oklch(70% var(--chroma) var(--hue)));
    --accent-10: light-dark(oklch(50%   var(--chroma) var(--hue)), oklch(75% var(--chroma) var(--hue)));
    --accent-11: light-dark(oklch(46%   calc(var(--chroma) * 0.9) var(--hue)), oklch(80% calc(var(--chroma) * 0.9) var(--hue)));
    --accent-12: light-dark(oklch(24%   calc(var(--chroma) * 0.5) var(--hue)), oklch(94% calc(var(--chroma) * 0.3) var(--hue)));

    /* Semantic roles — what components actually use. Defined in terms
       of scale steps: knobs → scales → roles. */
    --color-surface: var(--neutral-1);
    --color-surface-raised: var(--neutral-2);
    /* top-layer panels (dialog, menus, toasts): white in light like the
       page; one step elevated in dark, where shadows can't carry lift */
    --color-surface-overlay: light-dark(var(--neutral-1), var(--neutral-2));
    --color-text: var(--neutral-12);
    --color-text-muted: var(--neutral-11);
    --color-border: var(--neutral-6);
    --color-border-strong: var(--neutral-8);
    --color-accent: var(--accent-9);
    --color-on-accent: light-dark(
      oklch(99% 0.01 var(--hue)),
      oklch(18% 0.02 var(--hue)));

    /* Primary — the action color, decoupled from accent (shadcn-school:
       near-black light, near-white dark; the accent appears only where
       attention is earned: focus, selection, checkmarks). Hover/active
       are derived, so ANY value assigned to --color-primary gets correct
       states free. Colorful primary is a two-line theme:
       :root { --color-primary: var(--accent-9);
               --color-on-primary: var(--color-on-accent); } */
    --color-primary: var(--neutral-12);
    --color-on-primary: var(--neutral-1);
    --color-primary-hover: color-mix(in oklch, var(--color-primary) 90%, var(--color-surface));
    --color-primary-active: color-mix(in oklch, var(--color-primary) 80%, var(--color-surface));

    /* Status roles — the step-3/7/9/11 analogs, no full scales.
       Off-role shades: raw oklch(L var(--status-chroma) var(--*-hue)). */
    --color-danger-surface: light-dark(oklch(95% calc(var(--status-chroma) * 0.25) var(--danger-hue)), oklch(27% calc(var(--status-chroma) * 0.35) var(--danger-hue)));
    --color-danger-border:  light-dark(oklch(79% calc(var(--status-chroma) * 0.65) var(--danger-hue)), oklch(46% calc(var(--status-chroma) * 0.8) var(--danger-hue)));
    --color-danger:         light-dark(oklch(52% var(--status-chroma) var(--danger-hue)), oklch(70% var(--status-chroma) var(--danger-hue)));
    --color-on-danger:      light-dark(oklch(99% 0.01 var(--danger-hue)), oklch(18% 0.02 var(--danger-hue)));
    --color-danger-text:    light-dark(oklch(44% calc(var(--status-chroma) * 0.9) var(--danger-hue)), oklch(80% calc(var(--status-chroma) * 0.9) var(--danger-hue)));

    --color-success-surface: light-dark(oklch(95% calc(var(--status-chroma) * 0.25) var(--success-hue)), oklch(27% calc(var(--status-chroma) * 0.35) var(--success-hue)));
    --color-success-border:  light-dark(oklch(79% calc(var(--status-chroma) * 0.65) var(--success-hue)), oklch(46% calc(var(--status-chroma) * 0.8) var(--success-hue)));
    --color-success:         light-dark(oklch(52% var(--status-chroma) var(--success-hue)), oklch(70% var(--status-chroma) var(--success-hue)));
    --color-on-success:      light-dark(oklch(99% 0.01 var(--success-hue)), oklch(18% 0.02 var(--success-hue)));
    --color-success-text:    light-dark(oklch(44% calc(var(--status-chroma) * 0.9) var(--success-hue)), oklch(80% calc(var(--status-chroma) * 0.9) var(--success-hue)));

    /* warn solid runs lighter than its siblings — amber needs the
       lightness to read as warning, and takes dark text in both modes */
    --color-warn-surface: light-dark(oklch(95% calc(var(--status-chroma) * 0.25) var(--warn-hue)), oklch(27% calc(var(--status-chroma) * 0.35) var(--warn-hue)));
    --color-warn-border:  light-dark(oklch(79% calc(var(--status-chroma) * 0.65) var(--warn-hue)), oklch(46% calc(var(--status-chroma) * 0.8) var(--warn-hue)));
    --color-warn:         light-dark(oklch(78% var(--status-chroma) var(--warn-hue)), oklch(76% var(--status-chroma) var(--warn-hue)));
    --color-on-warn:      oklch(20% 0.03 var(--warn-hue));
    --color-warn-text:    light-dark(oklch(42% calc(var(--status-chroma) * 0.9) var(--warn-hue)), oklch(80% calc(var(--status-chroma) * 0.9) var(--warn-hue)));
  }

  /* scheme swap for image-valued tokens — the one place light-dark()
     can't help (colors only). See --check-glyph above. */
  @media (prefers-color-scheme: dark) {
    :root {
      --check-glyph: var(--check-glyph-dark);
    }
  }
}

/* ------------------------------------------------------------------ *
 * Preset — constructive defaults, Kelp-school not Preflight-school:
 * raw HTML should look finished, not like nothing. Zero-specificity
 * (:where) inside a layer: doubly overridable.
 * ------------------------------------------------------------------ */
@layer mica.preset {
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  html {
    -webkit-text-size-adjust: none;
    text-size-adjust: none;
  }

  :where(body) {
    margin: 0;
    min-block-size: 100svh;
    font-family: var(--font-body);
    line-height: var(--line-height);
    background: var(--color-surface);
    color: var(--color-text);
    accent-color: var(--color-accent);
  }

  /* Vertical rhythm: flow content spaces itself; last child never
     pushes against its container. Stacks override all of this with gap. */
  :where(p, ol, ul, dl, dd, blockquote, pre, table, figure, fieldset,
         details, address, audio, video, iframe) {
    margin-block: 0 var(--flow-space, var(--space-md));
  }
  :where(p, ol, ul, dl, dd, blockquote, pre, table, figure, fieldset,
         details, address, audio, video, iframe):last-child {
    margin-block-end: 0;
  }

  :where(h1, h2, h3, h4, h5, h6) {
    margin-block: 0 var(--space-sm);
    line-height: 1.2;
    text-wrap: balance;
  }
  /* Headings that follow content get breathing room above. */
  :where(h2, h3, h4, h5, h6):where(:not(:first-child)) {
    margin-block-start: var(--space-xl);
  }

  :where(p) {
    text-wrap: pretty;
  }

  /* Media behaves like content, not like text. */
  :where(img, svg, video, canvas, picture) {
    display: block;
    max-inline-size: 100%;
    block-size: auto;
  }

  /* Form controls inherit typography instead of shipping 2009's. */
  :where(button, input, select, textarea) {
    font: inherit;
  }

  :where(pre, code, kbd, samp) {
    font-family: var(--font-mono);
  }

  /* opt in to animating to/from intrinsic sizes (details, etc.) */
  :where(html) {
    interpolate-size: allow-keywords;
  }

  /* Tokenized focus ring — the theming story for focus, day one. */
  :where(:focus-visible) {
    outline: var(--focus-ring-width) solid var(--focus-ring-color);
    outline-offset: var(--focus-ring-offset);
  }

  @media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
    /* `*` never matches pseudo-elements — ::backdrop needs its own rule
       (and per convention gets its own block: an unrecognized selector
       in the list above would kill the whole kill-switch). Found via
       snapshot flakiness: the backdrop fade survived reduced-motion. */
    *::backdrop {
      animation-duration: 0.01ms !important;
      transition-duration: 0.01ms !important;
    }
  }
}

/* ------------------------------------------------------------------ *
 * Elements — Tier 1. Native elements styled directly; behavior is the
 * browser's. Variants are classes: any collision with app CSS resolves
 * in the user's favor because these live in a layer.
 * ------------------------------------------------------------------ */
@layer mica.elements {
  /* --- button (and things dressed as buttons) ---------------------- */
  :where(button, .btn,
         input:is([type="button"], [type="submit"], [type="reset"])) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-xs);
    block-size: var(--control-height);
    padding: 0 var(--space-md);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    background: var(--neutral-2);
    color: var(--color-text);
    font-size: 0.875rem;
    font-weight: 500;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s;

    &:hover:not(:disabled, [aria-disabled="true"]) {
      background: var(--neutral-3);
    }
    &:active:not(:disabled, [aria-disabled="true"]) {
      background: var(--neutral-4);
    }

    /* variants */
    &:where(.primary) {
      background: var(--color-primary);
      border-color: transparent;
      color: var(--color-on-primary);

      &:hover:not(:disabled, [aria-disabled="true"]) {
        background: var(--color-primary-hover);
      }
      &:active:not(:disabled, [aria-disabled="true"]) {
        background: var(--color-primary-active);
      }

      /* disabled = same fill at the base rule's reduced opacity — a
         color-mix toward gray was tried and reads as an enabled
         secondary button when primary is near-black */
    }

    &:where(.danger) {
      background: var(--color-danger);
      border-color: transparent;
      color: var(--color-on-danger);

      &:hover:not(:disabled, [aria-disabled="true"]) {
        background: color-mix(in oklch, var(--color-danger) 88%, var(--neutral-12));
      }
      &:active:not(:disabled, [aria-disabled="true"]) {
        background: color-mix(in oklch, var(--color-danger) 76%, var(--neutral-12));
      }
      &:where(:disabled, [aria-disabled="true"]) {
        background: color-mix(in oklch, var(--color-danger) 40%, var(--neutral-6));
      }
    }

    /* deliberately zero rest affordance — a ghost is invisible until
       hover/focus. use .ghost for toolbars and icon rows, not as the
       lone action on a page. */
    &:where(.ghost) {
      background: transparent;
      border-color: transparent;

      &:hover:not(:disabled, [aria-disabled="true"]) {
        background: var(--neutral-3);
      }
      &:active:not(:disabled, [aria-disabled="true"]) {
        background: var(--neutral-4);
      }
    }

    /* sizes (shadcn: h-8 px-3 / h-10 px-6, text-sm throughout) */
    &:where(.small) {
      block-size: var(--control-height-sm);
      padding-inline: var(--space-sm);
    }
    &:where(.large) {
      block-size: var(--control-height-lg);
      padding-inline: var(--space-lg);
    }

    &:where(:disabled, [aria-disabled="true"]) {
      cursor: not-allowed;
      opacity: 0.55;
    }
  }

  /* --- fields: text inputs, textarea, select ----------------------- *
   * One control family with buttons: same radius, same border weight.
   * Focus ring comes from the preset; text fields match :focus-visible
   * even on mouse focus, per spec.
   * ------------------------------------------------------------------ */
  :where(input, textarea, select):where(:not([type="checkbox"],
         [type="radio"], [type="range"], [type="color"], [type="file"],
         [type="button"], [type="submit"], [type="reset"],
         [type="image"])) {
    inline-size: 100%;
    padding: var(--space-xs) var(--space-sm);
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
    line-height: 1.375; /* textarea rhythm; single-line heights are fixed below */

    &::placeholder {
      color: var(--color-text-muted);
      opacity: 1;
    }

    &:where(:disabled) {
      cursor: not-allowed;
      opacity: 0.55;
      background: var(--neutral-2);
    }

    /* validation — only after the user has interacted (:user-invalid),
       never on pristine fields. First consumer of the status roles. */
    &:where(:user-invalid) {
      border-color: var(--color-danger);
      --focus-ring-color: var(--color-danger);
    }
  }

  /* single-line controls share the fixed control height; text centers
     in the content box (16px field text stays — iOS zoom rule) */
  :where(select:not([multiple], [size]),
         input:not([type="checkbox"], [type="radio"], [type="range"],
           [type="color"], [type="file"], [type="button"],
           [type="submit"], [type="reset"], [type="image"])) {
    block-size: var(--control-height);
    padding-block: 0;
  }

  /* --- customizable select, where the platform allows -------------- *
   * appearance: base-select opts into styleable pickers (Chromium).
   * Elsewhere this whole rule drops (unknown pseudo-element) and the
   * native picker remains — progressive enhancement, zero JS.
   * Both declarations are required for the opt-in, and grouping them
   * means they fail together: never a half-opted-in select.
   * ------------------------------------------------------------------ */
  :where(select),
  ::picker(select) {
    appearance: base-select;
  }

  @supports (appearance: base-select) {
    /* the trigger is a flex row; children default to stretch, which
       top-aligns the value text inside the fixed control height */
    :where(select) {
      align-items: center;
    }

    ::picker(select) {
      border: 1px solid var(--color-border);
      border-radius: var(--radius-md);
      background: var(--color-surface-overlay);
      padding: var(--space-2xs);
      margin-block: var(--space-2xs);
      /* dark surfaces need heavier shadows to register at all */
      box-shadow: 0 8px 24px light-dark(oklch(0 0 0 / 0.15), oklch(0 0 0 / 0.5));
      /* positioning stays the UA's (position-try-order: most-block-size):
         it can open upward on near-ties, but it guarantees no clipping.
         Forcing block-end + flip-block was tried and clips off-viewport
         in tight space — don't fight the picker's positioning. */
      opacity: 1;
      translate: 0 0;
    }

    /* enter animates; close is instant (shadcn behavior) — the
       transition only exists while open, so unmatching :open drops it */
    :where(select):open::picker(select) {
      transition:
        opacity 0.12s,
        translate 0.12s,
        overlay 0.12s allow-discrete,
        display 0.12s allow-discrete;
    }

    @starting-style {
      ::picker(select) {
        opacity: 0;
        translate: 0 -4px;
      }
    }

    :where(select) :where(option) {
      padding: var(--space-xs) var(--space-sm);
      border-radius: var(--radius-sm);
      cursor: pointer;
    }
    :where(select) :where(option):where(:hover, :focus-visible) {
      background: var(--neutral-3);
      outline: none; /* hover-follows-focus pattern; ring is redundant here */
    }
    :where(select) :where(option):checked {
      font-weight: 500;
    }
    :where(select) :where(option)::checkmark {
      color: var(--color-accent);
    }

    :where(select)::picker-icon {
      color: var(--color-text-muted);
      transition: rotate 0.15s;
    }
    :where(select):open::picker-icon {
      rotate: 180deg;
    }

    /* opt-in via select.js: the picker overlays the trigger with the
       selected option kept in place (macOS-native / Base UI behavior).
       JS supplies only --m-sel-index; all geometry lives here.
       offset = index × row (1lh + option padding) + picker padding/border;
       max() keeps the top on-screen for selections deep in long lists. */
    :where(select.picker-aligned)::picker(select) {
      position-area: none;
      margin-block: 0;
      inset-inline-start: calc(anchor(left) - var(--space-2xs) - 1px);
      min-inline-size: calc(anchor-size(width) + 2 * (var(--space-2xs) + 1px));
      inset-block-start: max(
        var(--space-2xs),
        calc(
          anchor(top) - var(--m-sel-index, 0) * (1lh + 2 * var(--space-xs)) -
            var(--space-2xs) - 1px
        )
      );
      /* unfold in place, don't slide */
      translate: 0 0;
    }
  }

  /* textarea grows with content — zero JS */
  :where(textarea) {
    field-sizing: content;
    min-block-size: 3lh;
    resize: vertical;
  }

  /* --- form grouping: label, fieldset, legend ---------------------- *
   * Plain by default (shadcn-school): a fieldset is a titled group,
   * not a bordered box. The legend reads as a small section heading.
   * Space contents with m-vstack or rhythm.
   * ------------------------------------------------------------------ */
  :where(label) {
    font-weight: 500;
  }

  :where(fieldset) {
    border: 0;
    padding: 0;
    margin: 0;
    min-inline-size: 0; /* fieldsets otherwise refuse to shrink */
  }

  :where(legend) {
    font-weight: 600;
    padding: 0;
    margin-block-end: var(--space-md);
  }

  /* a disabled fieldset dims everything it silences */
  :where(fieldset:disabled) {
    color: var(--color-text-muted);

    & :where(label) {
      color: var(--color-text-muted);
      cursor: not-allowed;
    }
  }

  /* --- checkbox, radio, switch: drawn, semantics stay native ------- */
  :where(input:is([type="checkbox"], [type="radio"])) {
    appearance: none;
    inline-size: 1.25rem;
    block-size: 1.25rem;
    margin: 0;
    flex-shrink: 0;
    vertical-align: -0.25em;
    border: 1px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    background: var(--color-surface) center / contain no-repeat;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s,
      background-position 0.15s;

    &:checked {
      border-color: var(--color-primary);
      background-color: var(--color-primary);
    }

    &:disabled {
      cursor: not-allowed;
      opacity: 0.55;
    }
  }

  :where(input[type="checkbox"]:not(.switch)) {
    &:checked {
      background-image: var(--check-glyph);
    }

    /* indeterminate dash — a gradient, no glyph needed */
    &:indeterminate {
      border-color: var(--color-primary);
      background-color: var(--color-primary);
      background-image: linear-gradient(
        var(--color-on-primary),
        var(--color-on-primary)
      );
      background-size: 55% 2px;
      background-position: center;
      background-repeat: no-repeat;
    }
  }

  /* radio: "square target" — a solid pip in every state. muted pip
     unchecked, bright pip on the primary fill when checked. the round
     radio is a theme:
       input[type="radio"] {
         border-radius: var(--radius-full);
         background-image: radial-gradient(circle closest-side,
           var(--color-on-primary) 40%, transparent 46%);
       } */
  :where(input[type="radio"]) {
    background-image: linear-gradient(
      var(--color-border-strong),
      var(--color-border-strong)
    );
    background-position: center;
    background-size: 8px 8px;
    background-repeat: no-repeat;

    &:checked {
      background-image: linear-gradient(
        var(--color-on-primary),
        var(--color-on-primary)
      );
    }
  }

  /* switch: a checkbox wearing a track; the thumb is a positioned
     gradient (pseudo-elements don't render on inputs). square, per the
     design language — a solid linear-gradient block */
  :where(input[type="checkbox"].switch) {
    inline-size: 2.25rem;
    border-color: transparent;
    border-radius: var(--radius-sm);
    background-color: var(--neutral-7);
    background-image: linear-gradient(white, white);
    background-size: 1rem calc(100% - 4px);
    background-position: 2px 50%;
    background-repeat: no-repeat;

    &:checked {
      background-color: var(--color-primary);
      background-position: calc(100% - 2px) 50%;
      background-image: linear-gradient(
        var(--color-on-primary),
        var(--color-on-primary)
      );
    }
  }

  /* disabled + on: desaturate toward neutral, same recipe as disabled
     .primary buttons — inert, not just faded (placed after the switch
     block so it wins its background-color at equal specificity) */
  :where(input:is([type="checkbox"], [type="radio"])):disabled:is(
      :checked,
      :indeterminate
    ) {
    border-color: transparent;
    background-color: color-mix(in oklch, var(--color-primary) 40%, var(--neutral-6));
  }

  /* labels that wrap a check control become the click target */
  :where(label:has(> input:is([type="checkbox"], [type="radio"]))) {
    display: inline-flex;
    align-items: center;
    gap: var(--space-xs);
    cursor: pointer;
  }

  /* a disabled control dims its whole label, not just itself */
  :where(label:has(> input:disabled)) {
    color: var(--color-text-muted);
    cursor: not-allowed;
  }

  /* --- content: tables, lists, code, quotes ------------------------ */
  :where(table) {
    inline-size: 100%;
    border-collapse: collapse;
    font-variant-numeric: tabular-nums;
  }
  :where(caption) {
    caption-side: bottom;
    margin-block-start: var(--space-xs);
    color: var(--color-text-muted);
    font-size: 0.875rem;
    text-align: start;
  }
  :where(th) {
    text-align: start;
    font-weight: 500;
    color: var(--color-text-muted);
  }
  :where(th, td) {
    padding: var(--space-sm);
    border-block-end: 1px solid var(--color-border);
  }
  :where(tbody tr:last-child) :where(th, td) {
    border-block-end: 0;
  }
  :where(tbody tr):hover {
    background: var(--color-surface-raised);
  }

  :where(ul, ol) {
    padding-inline-start: var(--space-lg);
  }
  :where(li) {
    margin-block: var(--space-2xs);
  }
  :where(li)::marker {
    color: var(--color-text-muted);
  }
  :where(dt) {
    font-weight: 500;
  }
  :where(dd) {
    margin-inline-start: 0;
  }

  :where(code, kbd, samp) {
    font-size: 0.875em;
  }
  :where(:not(pre) > code) {
    background: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    padding: 0.125em 0.375em;
    border-radius: var(--radius-sm);
  }
  :where(pre) {
    background: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    padding: var(--space-md);
    overflow-x: auto;
  }
  :where(kbd) {
    background: var(--color-surface);
    border: 1px solid var(--color-border-strong);
    border-block-end-width: 2px;
    padding: 0.125em 0.375em;
    border-radius: var(--radius-sm);
    font-size: 0.8em;
  }

  :where(blockquote) {
    border-inline-start: 2px solid var(--color-border-strong);
    padding-inline-start: var(--space-md);
    color: var(--color-text-muted);
  }

  :where(hr) {
    border: 0;
    border-block-start: 1px solid var(--color-border-strong);
    margin-block: var(--space-lg);
  }

  /* --- progress & meter: thin bars (shadcn-school) ------------------ *
   * vendor pseudos must stay in separate rules — an unknown selector
   * in a list invalidates the whole rule.
   * ------------------------------------------------------------------ */
  :where(progress, meter) {
    appearance: none;
    inline-size: 100%;
    block-size: 0.5rem;
    border: 0;
    border-radius: var(--radius-sm);
    overflow: hidden;
  }

  :where(progress) {
    background: color-mix(in oklch, var(--color-primary) 20%, transparent);
  }
  :where(progress)::-webkit-progress-bar {
    background: transparent;
  }
  :where(progress)::-webkit-progress-value {
    background: var(--color-primary);
  }
  :where(progress)::-moz-progress-bar {
    background: var(--color-primary);
  }

  /* meter grades through the status roles */
  :where(meter) {
    background: var(--neutral-4);
  }
  :where(meter)::-webkit-meter-bar {
    background: transparent;
    border: 0;
    block-size: 0.5rem;
  }
  :where(meter)::-webkit-meter-optimum-value {
    background: var(--color-success);
  }
  :where(meter)::-webkit-meter-suboptimum-value {
    background: var(--color-warn);
  }
  :where(meter)::-webkit-meter-even-less-good-value {
    background: var(--color-danger);
  }
  :where(meter):-moz-meter-optimum::-moz-meter-bar {
    background: var(--color-success);
  }
  :where(meter):-moz-meter-sub-optimum::-moz-meter-bar {
    background: var(--color-warn);
  }
  :where(meter):-moz-meter-sub-sub-optimum::-moz-meter-bar {
    background: var(--color-danger);
  }

  /* --- dialog: modal + drawer --------------------------------------- *
   * The browser brings focus trapping, Esc, top layer, ::backdrop, and
   * form[method=dialog] closing. Invoker commands (commandfor/command)
   * make open/close zero-JS. Exit animations: transition-behavior
   * allow-discrete + @starting-style.
   * ------------------------------------------------------------------ */
  :where(dialog) {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface-overlay);
    color: var(--color-text);
    /* sections carry their own padding; the dialog is a bare frame */
    padding: 0;
    overflow: hidden;
    inline-size: calc(100vw - 2rem);
    max-inline-size: var(--size-lg);
    max-block-size: calc(100svh - 2rem);
    box-shadow: 0 16px 48px light-dark(oklch(0 0 0 / 0.2), oklch(0 0 0 / 0.6));
    opacity: 0;
    scale: 0.95;
    transition:
      opacity 0.15s,
      scale 0.15s,
      translate 0.15s,
      overlay 0.15s allow-discrete,
      display 0.15s allow-discrete;
  }
  /* composition: dialog > header (title + description) / body / footer.
     layout lives on [open] — unconditional display would defeat the
     UA's display:none on closed dialogs */
  /* Composition is UNCONDITIONAL so it survives the exit: the dialog
     stays rendered (display transitions via allow-discrete) for 0.15s
     after [open] is removed. Structure gated on [open] snaps back to
     defaults the instant [open] goes — flex-direction reverts to row,
     children lose their sizing — and the content visibly deforms while
     fading out. Only `display` stays conditional (unconditional flex
     would defeat the UA's display:none on a closed dialog); opacity and
     scale are the open visual state that transitions on exit. */
  :where(dialog) {
    flex-direction: column;

    & > :where(*) {
      margin-block: 0; /* the dialog owns spacing, like a stack */
    }

    /* the body: whatever sits between header and footer. one wrapper
       element; it pads itself and is the scrollable region.
       flex-basis must stay `auto` (not `flex: 1`'s 0%): iOS Safari sizes
       the dialog's fit-content height from the basis, so 0% collapses
       the body to a one-line scroll strip (iOS 26.0; Chromium and newer
       WebKit use the content contribution either way). */
    & > :where(:not(header, footer, button.close)) {
      padding: var(--space-md);
      flex: 1 1 auto;
      min-block-size: 0;
      overflow-y: auto;
      /* break the scroll chain: at the region's scroll limit iOS
         otherwise hands the gesture to the page behind the modal —
         the root's overflow:hidden can't stop a chained scroll. */
      overscroll-behavior: contain;
      font-size: 0.875rem; /* body must not outweigh the 14px title */
    }

    /* when the dialog hits its max height, only the body compresses */
    & > :where(header, footer) {
      flex-shrink: 0;
    }
  }

  :where(dialog[open]) {
    display: flex;
    opacity: 1;
    scale: 1;
  }

  /* corner close button — <button class="close" commandfor="…"
     command="close" aria-label="Close">✕</button> (or wrap it in a
     method=dialog form). absolute is safe: the dialog is positioned.
     shared with toasts (popovertarget/popovertargetaction=hide there). */
  :where(dialog, [popover].toast) > :where(button.close) {
    position: absolute;
    inset-block-start: var(--space-sm);
    inset-inline-end: var(--space-sm);
    padding: var(--space-2xs);
    border: 0;
    background: transparent;
    color: var(--color-text-muted);
    line-height: 1;

    &:hover:not(:disabled, [aria-disabled="true"]) {
      background: var(--neutral-3);
      color: var(--color-text);
    }
    &:active:not(:disabled, [aria-disabled="true"]) {
      background: var(--neutral-4);
    }
  }

  :where(dialog) > :where(header) {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
    padding: var(--space-md);
    border-block-end: 1px solid var(--color-border);

    /* long titles must not run under the corner X */
    :where(dialog:has(> button.close)) > & {
      padding-inline-end: calc(var(--space-md) + var(--space-xl));
    }

    & > :where(*) {
      margin-block: 0;
    }

    & :where(h1, h2, h3, h4) {
      font-size: 0.875rem;
      font-weight: 500;
    }

    & :where(p) {
      color: var(--color-text-muted);
      font-size: 0.75rem;
      line-height: 1.6;
    }
  }

  :where(dialog) > :where(footer) {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-sm);
    justify-content: flex-end;
    padding: var(--space-md);
    border-block-start: 1px solid var(--color-border);

    & > :where(*) {
      margin-block: 0;
    }

    /* a method=dialog form is the idiomatic close mechanism — let its
       buttons participate in the footer's row directly */
    & > :where(form[method="dialog" i]) {
      display: contents;
    }
  }
  @starting-style {
    :where(dialog[open]) {
      opacity: 0;
      scale: 0.95;
    }
  }

  :where(dialog)::backdrop {
    /* explicit oklch alpha, not the `transparent` keyword: iOS won't
       interpolate oklch()→transparent and cuts the scrim instantly,
       desyncing it from the sheet. same-space endpoints animate. */
    background: oklch(0 0 0 / 0);
    /* size to the LARGE viewport, don't trust inset: shipped iOS sizes
       the top layer to the small viewport, so an inset-based backdrop
       ends above the real bottom edge when the toolbar collapses —
       page content peeked through the strip. (Radix/vaul overlays are
       ordinary fixed divs and don't have this problem; ::backdrop
       lives in the top layer, so we overpaint instead.) */
    inline-size: 100lvw;
    block-size: 100lvh;
    transition:
      background-color 0.15s,
      overlay 0.15s allow-discrete,
      display 0.15s allow-discrete;
  }
  :where(dialog[open])::backdrop {
    background: oklch(0 0 0 / 0.4);
  }
  @starting-style {
    :where(dialog[open])::backdrop {
      background: oklch(0 0 0 / 0);
    }
  }

  /* drawer (shadcn sheet anatomy): inline-end panel, fade + 2.5rem
     nudge rather than a full slide; no section dividers; footer pinned
     to the bottom with stacked actions */
  :where(dialog.drawer) {
    margin-inline: auto 0;
    margin-block: 0;
    /* full height WITHOUT an explicit block-size: the UA gives
       dialog:modal `position: fixed; inset-block: 0` — leaving height
       auto stretches between both edges, and on iOS that bottom edge
       tracks toolbar collapse like any bottom-fixed bar. Explicit
       100svh left a gap behind the collapsed toolbar; 100dvh lagged
       in the top layer on shipped iOS. Don't reintroduce either. */
    block-size: auto;
    max-block-size: none;
    inline-size: 75vw;
    max-inline-size: 24rem; /* sheet's max-w-sm */
    border: 0;
    border-inline-start: 1px solid var(--color-border);
    font-size: 0.75rem;
    line-height: 1.6;
    scale: 1;
    translate: 2.5rem 0;
  }
  :where(dialog.drawer[open]) {
    translate: 0 0;
  }
  /* unconditional (survives the exit, like the rest of composition) */
  :where(dialog.drawer) > :where(:not(header, footer, button.close)) {
    font-size: inherit; /* panel runs 12/relaxed throughout */
  }
  @starting-style {
    :where(dialog.drawer[open]) {
      translate: 2.5rem 0;
    }
  }

  :where(dialog.drawer) > :where(header) {
    border-block-end: 0;
  }
  :where(dialog.drawer) > :where(footer) {
    margin-block-start: auto;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-xs);
    border-block-start: 0;
  }

  /* small screens: the drawer becomes a bottom sheet — shadcn's own
     mobile drawer (vaul/radix) is exactly this. Anchored to the bottom
     edge via auto top margin: bottom attachment is the one thing iOS
     tracks reliably through toolbar collapse (same mechanism as bottom
     nav bars). Explicit 100svh gapped behind the collapsed toolbar and
     100dvh lagged in the top layer — don't reintroduce heights here. */
  @media (max-width: 40rem) {
    :where(dialog.drawer) {
      inset-block: auto 0;       /* release the UA's top anchor… */
      margin-block: 0;           /* …so height resolves from content */
      block-size: auto;          /* content height, like vaul */
      max-block-size: calc(100dvh - 3rem);
      inline-size: 100vw;
      max-inline-size: none;
      border-inline-start: 0;
      border-block-start: 1px solid var(--color-border);
      /* Two box-shadows, both painted by the SHEET element so they
         persist through the exit (display:allow-discrete keeps the box
         rendered) — unlike ::backdrop, which iOS drops from the top
         layer the instant you close, cutting the scrim. So the dim
         rides the sheet here and stays in sync with the slide:
           1) bleed slab below the edge (vaul's trick) — fills any strip
              iOS exposes under the top layer when the toolbar collapses;
           2) full-screen scrim via 100vmax spread — the visible dim,
              its alpha animating with the sheet. 100vmax keeps coverage
              even as the shadow rides the sliding sheet down. */
      box-shadow:
        0 4rem 0 0 var(--color-surface-overlay),
        0 0 0 100vmax oklch(0 0 0 / 0);
      transition:
        box-shadow 0.15s, translate 0.15s,
        overlay 0.15s allow-discrete, display 0.15s allow-discrete;
      /* the sheet SLIDES fully off the bottom and stays opaque — only
         the scrim fades. A small nudge + opacity fade read as a
         dissolve-in-place, wrong for a bottom sheet. */
      translate: 0 100%;
      opacity: 1;
    }
    :where(dialog.drawer[open]) {
      translate: 0 0;
      box-shadow:
        0 4rem 0 0 var(--color-surface-overlay),
        0 0 0 100vmax oklch(0 0 0 / 0.4);
    }
    @starting-style {
      :where(dialog.drawer[open]) {
        translate: 0 100%;
        box-shadow:
          0 4rem 0 0 var(--color-surface-overlay),
          0 0 0 100vmax oklch(0 0 0 / 0);
      }
    }
    /* the box-shadow scrim replaces ::backdrop here; keep the native
       backdrop clear so they don't double up or flash on the iOS cut. */
    :where(dialog.drawer[open])::backdrop {
      background: oklch(0 0 0 / 0);
    }

    /* grab handle — only when drawer.js is loaded (an affordance
       without its behavior would fake interactivity; TIERS.md).
       the handle is the first flex item of the open sheet. */
    :where(:root[data-drawer-gestures]) :where(dialog.drawer[open])::before {
      content: "";
      align-self: center;
      flex-shrink: 0;
      inline-size: 2.5rem;
      block-size: 0.25rem;
      margin-block-start: var(--space-sm);
      background: var(--color-border-strong);
      border-radius: var(--radius-full);
    }
    :where(:root[data-drawer-gestures]) :where(dialog.drawer) > :where(header) {
      touch-action: none; /* the drag surface must own its touches */
    }
  }

  /* scroll lock: the page must not scroll behind any modal dialog.
     CSS-only best effort (→ no JS tier violation); iOS honors
     overflow:hidden on the root for modern versions. */
  :where(html):has(:where(dialog:modal)) {
    overflow: hidden;
    overscroll-behavior: none;
  }

  /* --- accordion / disclosure: details + summary -------------------- *
   * shadcn accordion look; exclusivity is <details name="group">.
   * open/close animation via ::details-content + interpolate-size,
   * graceful (instant) where unsupported.
   * ------------------------------------------------------------------ */
  :where(details) {
    border-block-end: 1px solid var(--color-border);
  }

  :where(summary) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-sm);
    padding-block: var(--space-md);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    list-style: none;

    &:hover {
      text-decoration: underline;
    }

    /* chevron — masked so it rides currentColor-ish tokens */
    &::after {
      content: "";
      inline-size: 1rem;
      block-size: 1rem;
      flex-shrink: 0;
      background: var(--color-text-muted);
      mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' d='m6 9 6 6 6-6'/%3E%3C/svg%3E")
        center / contain no-repeat;
      transition: rotate 0.2s;
    }
  }
  :where(summary)::-webkit-details-marker {
    display: none;
  }
  :where(details[open]) > :where(summary)::after {
    rotate: 180deg;
  }

  /* content: everything after the summary */
  :where(details) > :where(:not(summary)) {
    margin-block: 0 var(--space-md);
    font-size: 0.875rem;
  }

  :where(details)::details-content {
    block-size: 0;
    overflow-y: clip;
    transition:
      block-size 0.2s,
      content-visibility 0.2s allow-discrete;
  }
  :where(details[open])::details-content {
    block-size: auto;
  }

  /* --- popover: panels + menus -------------------------------------- *
   * toggle/light-dismiss/Esc are the browser's (popovertarget). anchored
   * to the invoker where anchor positioning exists; elsewhere the UA's
   * centered fallback applies. items close declaratively via
   * popovertarget/command — zero JS throughout.
   * ------------------------------------------------------------------ */
  :where([popover]) {
    border: 1px solid color-mix(in oklch, var(--color-text) 12%, transparent);
    border-radius: var(--radius-md);
    background: var(--color-surface-overlay);
    color: var(--color-text);
    padding: var(--space-sm);
    inline-size: max-content;
    max-inline-size: min(18rem, calc(100vw - 2rem));
    font-size: 0.75rem;
    line-height: 1.6;
    box-shadow: 0 8px 24px light-dark(oklch(0 0 0 / 0.15), oklch(0 0 0 / 0.5));
    opacity: 1;
    scale: 1;
  }
  /* enter animates; close is instant (house rule for transient layers).
     Gated on `overlay` support: iOS lacks it, and its top-layer
     transitions are broken — a popover with an @starting-style entry
     freezes at the start (opacity:0 = invisible) with no reflow to kick
     it, and there's no CSS way to force one. Where overlay is
     unsupported, skip the entrance so popovers render at their resting
     opacity:1 instead of vanishing. (This is why toasts "didn't work"
     on iOS.) */
  @supports (overlay: auto) {
    :where([popover]:popover-open) {
      transition:
        opacity 0.1s,
        scale 0.1s,
        overlay 0.1s allow-discrete,
        display 0.1s allow-discrete;
    }
    @starting-style {
      :where([popover]:popover-open) {
        opacity: 0;
        scale: 0.95;
      }
    }
  }
  /* anchor to the invoker where the platform can */
  @supports (position-area: block-end) {
    :where([popover]) {
      position-area: block-end span-inline-end;
      position-try-fallbacks: flip-block, flip-inline;
      margin: var(--space-2xs);
    }
  }

  /* menu: a popover of buttons/links; separators are plain hrs */
  :where([popover].menu) {
    padding: var(--space-2xs);
    min-inline-size: 8rem;

    & > :where(button, a) {
      display: flex;
      align-items: center;
      justify-content: flex-start;
      gap: var(--space-xs);
      inline-size: 100%;
      block-size: auto;
      padding: var(--space-xs);
      border: 0;
      border-radius: var(--radius-sm);
      background: transparent;
      color: var(--color-text);
      font-size: 0.75rem;
      font-weight: 400;
      text-align: start;
      text-decoration: none;
      cursor: default;

      /* :not() guards mirror the base button states — an override of
         state must match the state's specificity (house rule) */
      &:hover:not(:disabled, [aria-disabled="true"]),
      &:focus-visible:not(:disabled, [aria-disabled="true"]) {
        background: var(--color-accent);
        color: var(--color-on-accent);
        outline: none; /* the highlight is the focus indicator here */
      }
      &:active:not(:disabled, [aria-disabled="true"]) {
        background: var(--color-accent);
        color: var(--color-on-accent);
      }

      &.danger {
        color: var(--color-danger-text);

        &:hover:not(:disabled, [aria-disabled="true"]),
        &:active:not(:disabled, [aria-disabled="true"]),
        &:focus-visible:not(:disabled, [aria-disabled="true"]) {
          background: var(--color-danger-surface);
          color: var(--color-danger-text);
        }
      }

      &:disabled {
        opacity: 0.5;
        background: transparent;
        color: var(--color-text);
      }
    }

    & > :where(hr) {
      margin-block: var(--space-2xs);
      margin-inline: calc(-1 * var(--space-2xs));
      border-block-start-color: var(--color-border);
    }
  }

  /* --- combobox: m-combobox + combobox.js (Tier 2) ------------------ *
   * without the module: a native datalist — functional autocomplete.
   * with it: styled listbox, filtering, active-descendant keys.
   * positioned within the wrapper (no top layer) so it works everywhere.
   * ------------------------------------------------------------------ */
  :where(m-combobox) {
    display: block;
    position: relative;
  }

  :where(m-combobox) > :where([role="listbox"]) {
    position: absolute;
    inset-block-start: calc(100% + var(--space-2xs));
    inset-inline: 0;
    z-index: 50;
    max-block-size: 15rem;
    overflow-y: auto;
    padding: var(--space-2xs);
    border: 1px solid color-mix(in oklch, var(--color-text) 12%, transparent);
    border-radius: var(--radius-md);
    background: var(--color-surface-overlay);
    box-shadow: 0 8px 24px light-dark(oklch(0 0 0 / 0.15), oklch(0 0 0 / 0.5));
  }

  :where(m-combobox) :where([role="option"]) {
    padding: var(--space-xs);
    border-radius: var(--radius-sm);
    font-size: 0.875rem;
    cursor: default;

    &:hover,
    &[aria-selected="true"] {
      background: var(--color-accent);
      color: var(--color-on-accent);
    }
  }

  /* --- tabs: m-tabs + tabs.js (Tier 2) ------------------------------ *
   * without the module: inert nav, all panels visible in order —
   * complete content. with it: tablist semantics + roving focus.
   * shadcn segmented look: muted track, surface active tab.
   * ------------------------------------------------------------------ */
  :where(m-tabs) {
    display: block;
  }

  :where(m-tabs) > :where(nav) {
    display: inline-flex;
    gap: var(--space-2xs);
    padding: var(--space-2xs);
    background: var(--neutral-3);
    border-radius: var(--radius-md);

    & > :where(button) {
      block-size: var(--control-height-sm);
      padding-inline: var(--space-sm);
      border: 0;
      border-radius: var(--radius-sm);
      background: transparent;
      color: var(--color-text-muted);

      /* :not() guards mirror the base button states (house rule) */
      &:hover:not(:disabled, [aria-disabled="true"]),
      &:active:not(:disabled, [aria-disabled="true"]) {
        background: transparent;
        color: var(--color-text);
      }

      &[aria-selected="true"] {
        background: var(--color-surface);
        color: var(--color-text);
        box-shadow: 0 1px 2px oklch(0 0 0 / 0.08);

        &:hover:not(:disabled, [aria-disabled="true"]),
        &:active:not(:disabled, [aria-disabled="true"]) {
          background: var(--color-surface);
          color: var(--color-text);
        }
      }
    }
  }

  :where(m-tabs) > :where(section) {
    padding-block: var(--space-md);
  }

  /* --- toast: display only (queueing/auto-dismiss are Tier 2) ------- *
   * <div popover="manual" class="toast" role="status"> — manual so it
   * survives light dismiss and other popovers; role=status announces.
   * ------------------------------------------------------------------ */
  :where([popover].toast) {
    position-area: none; /* not anchored — pinned to the corner */
    /* --m-toast-offset comes from toast.js when stacking; 0 without it */
    inset: auto var(--space-lg) calc(var(--space-lg) + var(--m-toast-offset, 0px)) auto;
    margin: 0;
    inline-size: min(22rem, calc(100vw - 2rem));
    max-inline-size: none;
    padding: var(--space-md);
    padding-inline-end: var(--space-2xl); /* room for the corner X */
    translate: 0 0;

    & :where(b) {
      display: block;
      font-weight: 500;
    }
    & :where(span, p) {
      color: var(--color-text-muted);
    }

    /* status edge variants */
    &:where(.success) {
      border-inline-start: 2px solid var(--color-success);
    }
    &:where(.danger) {
      border-inline-start: 2px solid var(--color-danger);
    }
    &:where(.warn) {
      border-inline-start: 2px solid var(--color-warn);
    }
  }
  /* restacking motion always (inset-block-end); the entrance slide/fade
     only where overlay transitions work — see the base popover note. */
  :where([popover].toast:popover-open) {
    transition: inset-block-end 0.15s;
  }
  @supports (overlay: auto) {
    :where([popover].toast:popover-open) {
      transition:
        opacity 0.1s,
        translate 0.1s,
        inset-block-end 0.15s,
        overlay 0.1s allow-discrete,
        display 0.1s allow-discrete;
    }
    @starting-style {
      :where([popover].toast:popover-open) {
        translate: 0 0.75rem;
      }
    }
  }

  /* --- tooltip: css-only, [data-tip] -------------------------------- *
   * inverted chip (primary on on-primary), shows on hover/focus after a
   * beat, hides instantly. data-tip is visual — pair it with aria-label
   * or visible text for AT.
   * ------------------------------------------------------------------ */
  :where([data-tip]) {
    position: relative;
  }
  :where([data-tip])::after {
    content: attr(data-tip);
    position: absolute;
    inset-block-end: calc(100% + var(--space-xs));
    inset-inline-start: 50%;
    translate: -50% 2px;
    inline-size: max-content;
    max-inline-size: 18rem;
    padding: 0.375rem var(--space-sm);
    border-radius: var(--radius-sm);
    background: var(--color-primary);
    color: var(--color-on-primary);
    font-size: 0.75rem;
    font-weight: 400;
    line-height: 1.4;
    opacity: 0;
    pointer-events: none;
    transition:
      opacity 0.1s,
      translate 0.1s;
    z-index: 10;
  }
  :where([data-tip]:hover, [data-tip]:focus-visible)::after {
    opacity: 1;
    translate: -50% 0;
    transition-delay: 0.3s;
  }

  /* --- validation errors ------------------------------------------- *
   * Tier 1 (no JS): a generic <m-error> after a field shows when the
   * field is :user-invalid. Submit attempts fall back to native bubbles.
   * Tier 2 (mica/field.js): <m-field> suppresses bubbles and activates
   * per-cause <m-error match="..."> messages. Rules below are all
   * zero-specificity; source order resolves them.
   * ------------------------------------------------------------------ */
  :where(m-field) {
    display: flex;
    flex-direction: column;
    gap: var(--space-2xs);
  }

  /* every rule here is zero-specificity: source order alone resolves */
  :where(m-error) {
    display: none;
    font-size: 0.875rem;
    color: var(--color-danger-text);
  }

  /* Tier-1 fallback: generic (matchless) error after user interaction */
  :where(input:user-invalid, textarea:user-invalid, select:user-invalid)
    ~ :where(m-error:not([match])) {
    display: block;
  }

  /* Tier 2: once m-field upgrades, the module owns visibility */
  :where(m-field:defined) :where(m-error) {
    display: none;
  }
  :where(m-error[active]) {
    display: block;
  }
}

/* ------------------------------------------------------------------ *
 * Layout primitives (Tier 0)
 *
 * Mechanism: attributes only set custom properties; rules read custom
 * properties. `gap="lg"` and `style="--gap: 2.5rem"` are the same thing.
 * ------------------------------------------------------------------ */
@layer mica.layout {
  /* Custom elements default to display:inline; give ours a sane base. */
  m-vstack,
  m-hstack,
  m-zstack,
  m-center,
  m-box,
  m-grid,
  m-sidecar,
  m-switcher,
  m-reel,
  m-frame,
  m-cover {
    display: block;
    box-sizing: border-box;
  }

  /* Shared attribute vocabulary -> custom properties */
  :is(m-vstack, m-hstack, m-grid, m-sidecar, m-switcher, m-reel, m-cover) {
    &[gap="none"] { --gap: 0; }
    &[gap="2xs"]  { --gap: var(--space-2xs); }
    &[gap="xs"]   { --gap: var(--space-xs); }
    &[gap="sm"]   { --gap: var(--space-sm); }
    &[gap="md"]   { --gap: var(--space-md); }
    &[gap="lg"]   { --gap: var(--space-lg); }
    &[gap="xl"]   { --gap: var(--space-xl); }
    &[gap="2xl"]  { --gap: var(--space-2xl); }
  }

  :is(m-vstack, m-hstack) {
    &[align="start"]   { --align: flex-start; }
    &[align="center"]  { --align: center; }
    &[align="end"]     { --align: flex-end; }
    &[align="stretch"] { --align: stretch; }
  }

  /* --- m-vstack: vertical flow with consistent gaps ---------------- */
  m-vstack {
    display: flex;
    flex-direction: column;
    align-items: var(--align, stretch);
    gap: var(--gap, var(--space-md));

    /* the stack owns spacing; preset rhythm yields to gap */
    & > * { margin-block: 0; }
  }

  /* --- m-hstack: one horizontal row; `wrap` to allow wrapping ------ */
  m-hstack {
    display: flex;
    align-items: var(--align, center);
    justify-content: var(--justify, flex-start);
    gap: var(--gap, var(--space-md));

    &[wrap] { flex-wrap: wrap; }

    & > * { margin-block: 0; }

    &[justify="start"]   { --justify: flex-start; }
    &[justify="center"]  { --justify: center; }
    &[justify="end"]     { --justify: flex-end; }
    &[justify="between"] { --justify: space-between; }
  }

  /* --- m-zstack: layering; children occupy the same grid cell ------ */
  m-zstack {
    display: grid;
    align-items: var(--align, stretch);
    justify-items: var(--justify, stretch);

    &[align="start"]   { --align: start; }
    &[align="center"]  { --align: center; }
    &[align="end"]     { --align: end; }
    &[align="stretch"] { --align: stretch; }

    &[justify="start"]   { --justify: start; }
    &[justify="center"]  { --justify: center; }
    &[justify="end"]     { --justify: end; }
    &[justify="stretch"] { --justify: stretch; }

    /* every layer shares one cell; paint order is DOM order.
       per-layer placement is just user CSS: place-self on the child. */
    & > * {
      grid-area: 1 / 1;
    }
  }

  /* --- m-center: intrinsic horizontal centering with a measure ----- */
  m-center {
    max-inline-size: var(--max, var(--measure));
    margin-inline: auto;
    padding-inline: var(--gutter, 0);

    &[max="xs"] { --max: var(--size-xs); }
    &[max="sm"] { --max: var(--size-sm); }
    &[max="md"] { --max: var(--size-md); }
    &[max="lg"] { --max: var(--size-lg); }
    &[max="xl"] { --max: var(--size-xl); }

    &[gutter="sm"] { --gutter: var(--space-sm); }
    &[gutter="md"] { --gutter: var(--space-md); }
    &[gutter="lg"] { --gutter: var(--space-lg); }
  }

  /* --- m-box: padding, an inside for content ----------------------- */
  m-box {
    padding: var(--pad, var(--space-md));

    &[pad="none"] { --pad: 0; }
    &[pad="xs"]   { --pad: var(--space-xs); }
    &[pad="sm"]   { --pad: var(--space-sm); }
    &[pad="md"]   { --pad: var(--space-md); }
    &[pad="lg"]   { --pad: var(--space-lg); }
    &[pad="xl"]   { --pad: var(--space-xl); }
  }

  /* --- m-grid: responsive grid, no media queries ------------------- */
  m-grid {
    display: grid;
    grid-template-columns: repeat(
      auto-fit,
      minmax(min(var(--min, var(--size-sm)), 100%), 1fr)
    );
    gap: var(--gap, var(--space-md));

    &[min="xs"] { --min: var(--size-xs); }
    &[min="sm"] { --min: var(--size-sm); }
    &[min="md"] { --min: var(--size-md); }
    &[min="lg"] { --min: var(--size-lg); }
  }

  /* --- m-sidecar: fixed + flexible pair, stacks when narrow -------- */
  m-sidecar {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap, var(--space-md));

    /* the sidecar is the first child by default; `side="end"` flips it */
    & > :first-child {
      flex-basis: var(--side-width, var(--size-xs));
      flex-grow: 1;
    }
    & > :last-child {
      flex-basis: 0;
      flex-grow: 999;
      min-inline-size: var(--content-min, 50%);
    }

    /* side="end": the *last* child is the sidecar (no visual reordering) */
    &[side="end"] > :first-child {
      flex-basis: 0;
      flex-grow: 999;
      min-inline-size: var(--content-min, 50%);
    }
    &[side="end"] > :last-child {
      flex-basis: var(--side-width, var(--size-xs));
      flex-grow: 1;
      min-inline-size: auto;
    }

    &[side-width="xs"] { --side-width: var(--size-xs); }
    &[side-width="sm"] { --side-width: var(--size-sm); }
    &[side-width="md"] { --side-width: var(--size-md); }
  }

  /* --- m-switcher: row that switches to a column below a threshold - */
  m-switcher {
    display: flex;
    flex-wrap: wrap;
    gap: var(--gap, var(--space-md));

    & > * {
      flex-basis: calc((var(--threshold, var(--size-lg)) - 100%) * 999);
      flex-grow: 1;
    }

    &[threshold="sm"] { --threshold: var(--size-sm); }
    &[threshold="md"] { --threshold: var(--size-md); }
    &[threshold="lg"] { --threshold: var(--size-lg); }
    &[threshold="xl"] { --threshold: var(--size-xl); }
  }

  /* --- m-reel: horizontal scroll with snap ------------------------- */
  m-reel {
    display: flex;
    gap: var(--gap, var(--space-md));
    overflow-x: auto;
    scroll-snap-type: inline proximity;

    & > * {
      flex-shrink: 0;
      scroll-snap-align: start;
    }

    &[snap="none"]      { scroll-snap-type: none; }
    &[snap="mandatory"] { scroll-snap-type: inline mandatory; }
  }

  /* --- m-frame: hold an aspect ratio, crop media to fit ------------ */
  m-frame {
    aspect-ratio: var(--ratio, 16 / 9);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;

    &[ratio="square"] { --ratio: 1; }
    &[ratio="16:9"]   { --ratio: 16 / 9; }
    &[ratio="4:3"]    { --ratio: 4 / 3; }
    &[ratio="3:2"]    { --ratio: 3 / 2; }
    &[ratio="2:1"]    { --ratio: 2 / 1; }
    &[ratio="9:16"]   { --ratio: 9 / 16; }

    /* media fills and crops; anything else just gets centered */
    & > :is(img, video),
    & > picture > img {
      inline-size: 100%;
      block-size: 100%;
      object-fit: cover;
    }
  }

  /* --- m-cover: fill the viewport, center the principal element ---- *
   * The principal element (h1 by default, or mark one child with
   * .principal) is vertically centered; children before it pin to the
   * top, children after it pin to the bottom.                         */
  m-cover {
    display: flex;
    flex-direction: column;
    min-block-size: var(--min-height, 100svh);
    gap: var(--gap, var(--space-md));
    padding: var(--pad, var(--space-md));

    &[pad="none"] { --pad: 0; }
    &[pad="sm"]   { --pad: var(--space-sm); }
    &[pad="md"]   { --pad: var(--space-md); }
    &[pad="lg"]   { --pad: var(--space-lg); }
    &[pad="xl"]   { --pad: var(--space-xl); }

    & > * { margin-block: 0; }

    & > :is(h1, .principal) { margin-block: auto; }

    /* an explicit .principal wins over the h1 default */
    &:has(> .principal) > h1:not(.principal) { margin-block: 0; }
  }
}
