/* platform.css — the universal theme, served centrally by platform-api at
   /platform.css (one copy for every site, mirrors /analytics.js). ADR 008 pattern.

   THE design system is the jameslcowan system (Apple neutral palette, SF Pro
   stacks, --theme-* semantic layer with system light/dark + data-theme override,
   accent scale, spacing/radius/motion/blur/tracking). Its single source of truth
   is sites/jameslcowan/design/tokens.json; that repo's build generates
   static/tokens.css, and scripts/sync-platform-theme.mjs copies it verbatim into
   the marked block below. Never hand-edit inside the markers.

   Three cascade layers, low to high:
     platform.reset  — box model, margin, media, form normalization
     platform.tokens — the jameslcowan token set (synced) + platform additions
                        (z-index, safe-area) + a legacy compat block kept until
                        every site is converted off the old panax-era names.
     platform.interaction — the global transparent-interaction fixes (tap
                        highlight, touch delay, text-size-adjust, focus ring,
                        reduced motion) applied ONCE instead of per-element.
     platform.components — the universal control layer (ADR 015): every control
                        with its whole state matrix, skinned entirely by --ui-*
                        tokens. A menu, not a mandate: see docs/UI-LAYER.md.

   Everything here is layered. A site's own (unlayered) CSS always beats a layered
   rule, so this is a floor other stylesheets build on, never a ceiling that fights
   them. Sites override the accent slot (--accent-*) with their brand hue.
   Load it FIRST in <head>, before any site stylesheet. */

@layer platform.reset, platform.tokens, platform.interaction, platform.components;

/* ---------------------------------------------------------------- reset ---- */
@layer platform.reset {
  *, *::before, *::after { box-sizing: border-box; }

  html {
    -webkit-text-size-adjust: 100%;
    -moz-text-size-adjust: 100%;
    text-size-adjust: 100%;
    scrollbar-gutter: stable;
  }

  body { margin: 0; min-height: 100svh; }

  /* Remove default margins; sites set their own rhythm via the spacing scale. */
  h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd, ul, ol { margin: 0; }
  ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }

  /* Media behaves like a block that never overflows its column. */
  img, svg, video, canvas, audio, iframe, embed, object { display: block; max-width: 100%; }
  img, video { height: auto; }

  /* Forms inherit type; buttons look like buttons only when a site says so. */
  input, button, textarea, select { font: inherit; color: inherit; letter-spacing: inherit; }
  button { background: none; border: 0; padding: 0; cursor: pointer; }
  a { color: inherit; text-decoration: inherit; }

  /* Predictable text wrapping for headings and prose. */
  h1, h2, h3, h4 { text-wrap: balance; }
  p, li { text-wrap: pretty; }
}

/* --------------------------------------------------------------- tokens ---- */
@layer platform.tokens {
  /* BEGIN jameslcowan-tokens (synced by scripts/sync-platform-theme.mjs; do not hand-edit) */
/* Generated from design/tokens.json - do not edit manually */
/* Pantone 2026: System blue (vivid) + Apple neutral (custom) */

:root {
    color-scheme: light dark;

    /* Accent scale */
    --accent-50: #EBF4FF;
    --accent-100: #D6EAFF;
    --accent-200: #A8D4FF;
    --accent-300: #6BB5FF;
    --accent-400: #3399FF;
    --accent-500: #0A84FF;
    --accent-600: #006FE6;
    --accent-700: #0059C2;
    --accent-800: #004099;
    --accent-900: #002E6E;
    --accent-950: #001A42;

    /* Light theme */
    --light-bg: #FFFFFF;
    --light-bg-subtle: #F5F5F7;
    --light-bg-elevated: #FFFFFF;
    --light-bg-raised: #EFEFF4;
    --light-bg-surface: #E8E8ED;
    --light-bg-muted: #DCDCE2;
    --light-bg-soft: #C7C7CC;
    --light-bg-strong: #AEAEB2;
    --light-bg-emphasis: #8E8E93;
    --light-bg-trans-tint: #FFFFFF;
    --light-bg-trans-light: #F5F5F7;
    --light-bg-trans: #EFEFF4;
    --light-bg-trans-dark: #E8E8ED;
    --light-color: #1D1D1F;
    --light-color-dim: #6E6E73;
    --light-color-muted: #86868B;
    --light-border: #D2D2D7;
    --light-border-nav: #DFDFDF;
    --light-border-strong: #AEAEB2;
    --light-border-accent: #0A84FF;

    /* Dark theme */
    --dark-bg: #000000;
    --dark-bg-subtle: #1C1C1E;
    --dark-bg-elevated: #2C2C2E;
    --dark-bg-raised: #3A3A3C;
    --dark-bg-surface: #48484A;
    --dark-bg-muted: #636366;
    --dark-bg-soft: #8E8E93;
    --dark-bg-strong: #AEAEB2;
    --dark-bg-emphasis: #C7C7CC;
    --dark-bg-trans-tint: #000000;
    --dark-bg-trans-light: #1C1C1E;
    --dark-bg-trans: #2C2C2E;
    --dark-bg-trans-dark: #3A3A3C;
    --dark-color: #F5F5F7;
    --dark-color-dim: #98989D;
    --dark-color-muted: #AEAEB2;
    --dark-border: #38383A;
    --dark-border-nav: #202020;
    --dark-border-strong: #48484A;
    --dark-border-accent: #3399FF;

    /* Typography */
    --font-body: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", system-ui, sans-serif;
    --font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Helvetica Neue", system-ui, sans-serif;
    --font-mono: ui-monospace, "SF Mono", "Cascadia Code", "Segoe UI Mono", monospace;
    --text-xs: 0.75rem;
    --text-sm: 0.875rem;
    --text-base: 1rem;
    --text-lg: 1.0625rem;
    --text-xl: 1.25rem;
    --text-2xl: 1.5rem;
    --text-3xl: 1.875rem;
    --leading-tight: 1.2;
    --leading-normal: 1.47;
    --leading-relaxed: 1.6;
    --weight-normal: 400;
    --weight-medium: 500;
    --weight-semibold: 600;
    --weight-bold: 700;
    --tracking-tight: -0.022em;
    --tracking-normal: 0;
    --tracking-wide: 0.04em;
    --tracking-wider: 0.06em;
    --tracking-widest: 0.12em;

    /* Spacing */
    --space-xs: 0.25rem;
    --space-sm: 0.5rem;
    --space-md: 1rem;
    --space-lg: 1.5rem;
    --space-xl: 2rem;
    --space-2xl: 3rem;
    --space-3xl: 4rem;

    /* Radius */
    --radius-2xs: 2px;
    --radius-xs: 4px;
    --radius-sm: 6px;
    --radius-chip: 8px;
    --radius-md: 10px;
    --radius-lg: 14px;
    --radius-xl: 18px;
    --radius-2xl: 24px;
    --radius-tile: 22.37%;

    /* Motion */
    --motion-duration-short-1: 50ms;
    --motion-duration-short-2: 100ms;
    --motion-duration-short-3: 150ms;
    --motion-duration-short-4: 200ms;
    --motion-duration-medium-1: 250ms;
    --motion-duration-medium-2: 300ms;
    --motion-duration-medium-3: 350ms;
    --motion-duration-medium-4: 400ms;
    --motion-duration-long-1: 450ms;
    --motion-duration-long-2: 500ms;
    --motion-duration-long-3: 550ms;
    --motion-duration-long-4: 600ms;
    --motion-duration-extra-long-1: 700ms;
    --motion-duration-extra-long-2: 800ms;
    --motion-easing-standard: cubic-bezier(0.2, 0, 0, 1);
    --motion-easing-standard-decelerate: cubic-bezier(0, 0, 0, 1);
    --motion-easing-standard-accelerate: cubic-bezier(0.3, 0, 1, 1);
    --motion-easing-emphasized: cubic-bezier(0.2, 0, 0, 1);
    --motion-easing-emphasized-decelerate: cubic-bezier(0.05, 0.7, 0.1, 1);
    --motion-easing-emphasized-accelerate: cubic-bezier(0.3, 0, 0.8, 0.15);
    --motion-press: var(--motion-duration-short-2) var(--motion-easing-standard);
    --motion-hover: var(--motion-duration-short-3) var(--motion-easing-standard);
    --motion-state: var(--motion-duration-medium-1) var(--motion-easing-standard);
    --motion-enter: var(--motion-duration-short-4) var(--motion-easing-emphasized-decelerate);
    --motion-exit: var(--motion-duration-short-3) var(--motion-easing-emphasized-accelerate);
    --motion-sheet: var(--motion-duration-medium-4) var(--motion-easing-emphasized-decelerate);
    --motion-sheet-exit: var(--motion-duration-medium-1) var(--motion-easing-emphasized-accelerate);
    --motion-fast: var(--motion-hover);
    --motion-normal: var(--motion-state);
    --motion-slow: var(--motion-duration-medium-2) var(--motion-easing-standard);

    /* Blur */
    --blur-xs: 8px;
    --blur-sm: 12px;
    --blur-md: 20px;
    --blur-lg: 28px;
    --blur: 20px;

    /* Icon sizes (from iconify-icon.pl-icon and --pl-profile-section-icon-size) */
    --icon-size-inline: 1.25em;
    --icon-size-section: 1.75rem;
}

/* Active theme (html[data-theme] overrides system preference) */
:root {
    --theme-bg: var(--light-bg);
    --theme-bg-subtle: var(--light-bg-subtle);
    --theme-bg-elevated: var(--light-bg-elevated);
    --theme-bg-raised: var(--light-bg-raised);
    --theme-bg-surface: var(--light-bg-surface);
    --theme-bg-muted: var(--light-bg-muted);
    --theme-bg-soft: var(--light-bg-soft);
    --theme-bg-strong: var(--light-bg-strong);
    --theme-color: var(--light-color);
    --theme-color-dim: var(--light-color-dim);
    --theme-color-muted: var(--light-color-muted);
    --theme-border: var(--light-border);
    --theme-border-nav: var(--light-border-nav);
    --theme-border-strong: var(--light-border-strong);
    --theme-border-card: var(--light-border);
    --theme-accent-emphasis: var(--accent-500);
    --theme-focus-ring: rgba(10, 132, 255, 0.32);
    --theme-shadow-sm: rgba(11, 20, 31, 0.08);
    --theme-shadow-md: rgba(11, 20, 31, 0.12);
    --theme-shadow-lg: rgba(11, 20, 31, 0.16);
    --theme-nav-blur: var(--blur-lg);
    --theme-shell-bg: transparent;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --theme-bg: var(--dark-bg);
    --theme-bg-subtle: var(--dark-bg-subtle);
    --theme-bg-elevated: var(--dark-bg-elevated);
    --theme-bg-raised: var(--dark-bg-raised);
    --theme-bg-surface: var(--dark-bg-surface);
    --theme-bg-muted: var(--dark-bg-muted);
    --theme-bg-soft: var(--dark-bg-soft);
    --theme-bg-strong: var(--dark-bg-strong);
    --theme-color: var(--dark-color);
    --theme-color-dim: var(--dark-color-dim);
    --theme-color-muted: var(--dark-color-muted);
    --theme-border: var(--dark-border);
    --theme-border-nav: var(--dark-border-nav);
    --theme-border-strong: var(--dark-border-strong);
    --theme-border-card: var(--dark-border-accent);
    --theme-accent-emphasis: var(--accent-400);
    --theme-focus-ring: rgba(51, 153, 255, 0.42);
    --theme-shadow-sm: rgba(0, 0, 0, 0.35);
    --theme-shadow-md: rgba(0, 0, 0, 0.45);
    --theme-shadow-lg: rgba(0, 0, 0, 0.55);
    --theme-nav-blur: var(--blur-xs);
    --theme-shell-bg: var(--dark-bg);
  }
}

:root[data-theme="dark"] {
    --theme-bg: var(--dark-bg);
    --theme-bg-subtle: var(--dark-bg-subtle);
    --theme-bg-elevated: var(--dark-bg-elevated);
    --theme-bg-raised: var(--dark-bg-raised);
    --theme-bg-surface: var(--dark-bg-surface);
    --theme-bg-muted: var(--dark-bg-muted);
    --theme-bg-soft: var(--dark-bg-soft);
    --theme-bg-strong: var(--dark-bg-strong);
    --theme-color: var(--dark-color);
    --theme-color-dim: var(--dark-color-dim);
    --theme-color-muted: var(--dark-color-muted);
    --theme-border: var(--dark-border);
    --theme-border-nav: var(--dark-border-nav);
    --theme-border-strong: var(--dark-border-strong);
    --theme-border-card: var(--dark-border-accent);
    --theme-accent-emphasis: var(--accent-400);
    --theme-focus-ring: rgba(51, 153, 255, 0.42);
    --theme-shadow-sm: rgba(0, 0, 0, 0.35);
    --theme-shadow-md: rgba(0, 0, 0, 0.45);
    --theme-shadow-lg: rgba(0, 0, 0, 0.55);
    --theme-nav-blur: var(--blur-xs);
    --theme-shell-bg: var(--dark-bg);
}

:root[data-theme="light"] {
    --theme-bg: var(--light-bg);
    --theme-bg-subtle: var(--light-bg-subtle);
    --theme-bg-elevated: var(--light-bg-elevated);
    --theme-bg-raised: var(--light-bg-raised);
    --theme-bg-surface: var(--light-bg-surface);
    --theme-bg-muted: var(--light-bg-muted);
    --theme-bg-soft: var(--light-bg-soft);
    --theme-bg-strong: var(--light-bg-strong);
    --theme-color: var(--light-color);
    --theme-color-dim: var(--light-color-dim);
    --theme-color-muted: var(--light-color-muted);
    --theme-border: var(--light-border);
    --theme-border-nav: var(--light-border-nav);
    --theme-border-strong: var(--light-border-strong);
    --theme-border-card: var(--light-border);
    --theme-accent-emphasis: var(--accent-500);
    --theme-focus-ring: rgba(10, 132, 255, 0.32);
    --theme-shadow-sm: rgba(11, 20, 31, 0.08);
    --theme-shadow-md: rgba(11, 20, 31, 0.12);
    --theme-shadow-lg: rgba(11, 20, 31, 0.16);
    --theme-nav-blur: var(--blur-lg);
    --theme-shell-bg: transparent;
}
  /* END jameslcowan-tokens */

  /* Platform additions the jameslcowan set does not carry. */
  :root {
    /* Stacking order — shared so headers/menus/modals never collide */
    --z-base: 1;
    --z-header: 100;
    --z-menu: 200;
    --z-overlay: 500;
    --z-modal: 1000;
    --z-toast: 1500;

    /* Safe-area insets (notches / home indicators) */
    --safe-top: env(safe-area-inset-top, 0px);
    --safe-right: env(safe-area-inset-right, 0px);
    --safe-bottom: env(safe-area-inset-bottom, 0px);
    --safe-left: env(safe-area-inset-left, 0px);

    --tap-highlight: transparent;
  }

  /* LEGACY COMPAT — panax-era structural names still consumed by site CSS.
     Values unchanged from before the jameslcowan adoption so unconverted pages
     render identically. Remove each alias once no site references it. */
  :root {
    --radius: 12px;
    --radius2: 8px;
    --radius-pill: 999px;
    --space-1: 6px;
    --space-2: 10px;
    --space-3: 14px;
    --space-4: 20px;
    --space-5: 28px;
    --space-6: 36px;
    --space-7: 48px;
    --space-8: 64px;
    --text-md: 1.0625rem;
    --leading-body: 1.65;
    --prose-gap: var(--space-4);
    --prose-gap-lg: var(--space-5);
    --dur-fast: 120ms;
    --dur: 200ms;
    --dur-slow: 320ms;
    --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --focus-ring: var(--theme-focus-ring, color-mix(in srgb, currentColor 40%, transparent));
  }
}

/* ---------------------------------------------------------- interaction ---- */
@layer platform.interaction {
  /* The universal transparent-interaction fix, in ONE place instead of ~40
     per-element copies. No grey/blue flash on tap; no 300ms delay or
     double-tap zoom on controls; no accidental text selection dragging a button. */
  html { -webkit-tap-highlight-color: var(--tap-highlight, transparent); }

  a, button, label, summary, select,
  [role="button"], [role="tab"], [role="menuitem"], [tabindex] {
    -webkit-tap-highlight-color: var(--tap-highlight, transparent);
    touch-action: manipulation;
  }

  button, summary, [role="button"], [role="tab"] {
    -webkit-user-select: none;
    user-select: none;
  }

  /* Disabled controls read as disabled everywhere. */
  :disabled, [aria-disabled="true"] { cursor: not-allowed; }

  /* Keyboard focus is always visible; pointer focus never leaves a stuck ring. */
  :focus:not(:focus-visible) { outline: none; }
  :focus-visible {
    outline: 2px solid var(--theme-focus-ring, currentColor);
    outline-offset: 2px;
    border-radius: var(--radius-sm, 6px);
  }

  /* Respect users who ask for less motion. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.001ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.001ms !important;
      scroll-behavior: auto !important;
    }
  }
}

/* ----------------------------------------------------------- components ---- */
/* The universal control layer (ADR 015 and its amendment). Every control the
   house uses, defined ONCE with its whole state matrix: default, hover,
   active, focus-visible, disabled, loading, selected, invalid. Promoted from
   sites/nascen-ai/css/components.css (707 lines, verified across 436
   controls) and generalised: every value now comes from a --ui-* custom
   property with a literal fallback, so a site skins the whole layer by
   redefining tokens and never by overriding a rule. docs/UI-LAYER.md is the
   adoption guide and the token reference.

   Class names carry the `ui-` prefix on purpose. Sites already ship their own
   `.btn`, `.card`, `.row` and `.input`, and this file reaches all of them the
   moment it is served: a prefix makes adoption opt in per control and makes a
   collision impossible. State classes only ever appear compounded with a
   `ui-` class (`.ui-btn.is-loading`), never bare.

   Behaviour (menus, tabs, accordions, dialogs) lives in /components.js and
   keys off `data-ui-*` attributes. The CSS here stands alone; the JS needs the
   CSS only for the hidden/visible popover rule.

   Rules that hold for every control below:
   - one focus ring, var(--ui-ring) at var(--ui-ring-off), never removed for
     the mouse. A field's focus is its own edge instead, because a ring 2px
     outside a 1px edge reads as two rings with a gap.
   - hover changes the ground, never the size.
   - :active is a 1px optical press (a transform), so nothing reflows.
   - disabled is var(--ui-disabled) opacity plus pointer-events: none.
   - every transition runs var(--ui-t-hover) on var(--ui-ease), and the
     reduced-motion block at the end of the layer zeroes all of them.
   - the coarse-pointer block gives every target var(--ui-touch).
   - nothing here is mandatory. Every selector stays at class specificity,
     nothing uses !important, and the whole layer sits in a cascade layer, so a
     site's own unlayered CSS beats any rule below on any control. Take the
     buttons and not the fields, or take everything and redefine the scale. */

@layer platform.components {
  /* --- The token slots a site redefines. Defaults read the theme layer above,
     so an unskinned site already looks like the house; a site that sets its own
     --ui-* values (unlayered, so they win) reskins every control at once. --- */
  :root {
    /* Grounds, low to high. --ui-hover is the hover ground, --ui-pressed the
       held one; a control never changes size to show either. */
    --ui-ground: var(--theme-bg, #ffffff);
    --ui-rail: var(--theme-bg-subtle, #f5f5f7);
    --ui-raised: var(--theme-bg-elevated, #ffffff);
    --ui-hover: var(--theme-bg-raised, #efeff4);
    --ui-pressed: var(--theme-bg-surface, #e8e8ed);

    /* Ink. --ui-ink-inverse is the label on a solid (ink ground) control. */
    --ui-ink: var(--theme-color, #1d1d1f);
    --ui-ink-muted: var(--theme-color-dim, #6e6e73);
    --ui-ink-faint: var(--theme-color-muted, #86868b);
    --ui-ink-inverse: var(--theme-bg, #ffffff);

    /* Hairlines. */
    --ui-line: var(--theme-border, #d2d2d7);
    --ui-line-strong: var(--theme-border-strong, #aeaeb2);

    /* The one accent, used sparingly: the accent control, the selected ring,
       the focused field edge. */
    --ui-accent: var(--theme-accent-emphasis, #0a84ff);
    --ui-accent-hover: var(--accent-600, #006fe6);
    --ui-accent-ink: #ffffff;

    /* One focus ring for the whole layer. */
    --ui-ring-w: 2px;
    --ui-ring-off: 2px;
    --ui-ring-color: var(--theme-focus-ring, currentColor);
    --ui-ring: var(--ui-ring-w) solid var(--ui-ring-color);

    /* The control scale: three heights, one field height, one row height,
       one coarse-pointer floor. */
    --ui-h-sm: 28px;
    --ui-h-md: 32px;
    --ui-h-lg: 40px;
    --ui-field-h: var(--ui-h-lg);
    --ui-row-h: 36px;
    --ui-touch: 44px;

    /* The control band (Kleto Pulse ruling 2026-10-29, docs/DESIGN-TASTE.md):
       one control height per pointer, 40px with a mouse and 44px on touch
       (the coarse pointer block at the end of the layer lifts it), one
       radius and one gap for every band control, and a control edge that
       reaches 3:1 against the ground in both themes (light #86868b is 3.6:1
       on #fff and 3.3:1 on #f5f5f7; dark #7c7c80 is 5.1:1 on #000, 4.1:1 on
       #1c1c1e and 3.4:1 on #2c2c2e). The band shell .ui-bar maps the control
       scale onto these, so every house control inside it takes one height
       and one set of corners. */
    --ui-ctl-h: 40px;
    --ui-ctl-r: 8px;
    --ui-ctl-gap: 8px;
    --ui-ctl-line: #86868b;
    /* --ui-ctl-ring, optional: the band's focus ring colour; default the
       selection edge below. */

    /* One selected state for the band's chips and segments (same ruling): a
       tint of the selection edge, the edge drawn 2px, and an ink that reads
       4.5:1 on the tint. Three optional slots, read with fallbacks inside the
       rules, so a scope that sets only --ui-sel-edge (or only --ui-accent)
       gets a matching tint and ink:
         --ui-sel-edge    the edge; default --ui-accent
         --ui-sel-ground  the tint; default 12% of the edge
         --ui-sel-ink     the label; default the edge mixed 72% with --ui-ink
       (house light: #0f67c0 on #e2f0ff, 4.9:1; house dark: 8.5:1). */

    /* Shape. The defaults are the house ones (4px on a control, 8px on a
       card, 2px on a mark) because the token set assumes them; a site that
       wants 12px cards or fully round controls sets these three and the whole
       layer follows. */
    --ui-radius: var(--radius-xs, 4px);
    --ui-radius-card: var(--radius-chip, 8px);
    --ui-radius-mark: var(--radius-2xs, 2px);

    /* Rhythm inside a control. */
    --ui-gap: 8px;
    --ui-pad: 12px;
    --ui-pad-sm: 10px;
    --ui-pad-lg: 18px;
    --ui-pad-card: 16px;

    /* State constants. */
    --ui-press: 1px;
    --ui-disabled: .55;

    /* Type. The layer sets size, line height and weight only; the family is
       whatever the site set on body. */
    --ui-text: var(--text-sm, 14px);
    --ui-leading: 20px;
    --ui-text-sm: 13px;
    --ui-text-lg: 15px;
    --ui-text-label: var(--text-xs, 12px);
    --ui-leading-label: 16px;
    --ui-text-figure: var(--text-xl, 20px);
    --ui-leading-figure: 28px;
    --ui-weight: 500;

    /* Motion. --ui-t-hover is every state change, --ui-t-struct a structural
       move (a sheet arriving, a rail collapsing). */
    --ui-t-hover: var(--motion-duration-short-3, 150ms);
    --ui-t-struct: var(--motion-duration-short-4, 240ms);
    --ui-ease: var(--motion-easing-standard, cubic-bezier(.2, 0, 0, 1));

    /* Depth. A card's own shadow, a floating panel's, a modal sheet's. */
    --ui-shadow: 0 1px 2px var(--theme-shadow-sm, rgba(11, 20, 31, .08));
    --ui-shadow-float: 0 1px 2px var(--theme-shadow-sm, rgba(11, 20, 31, .08)),
      0 8px 24px var(--theme-shadow-md, rgba(11, 20, 31, .12));
    --ui-shadow-sheet: 0 8px 32px var(--theme-shadow-lg, rgba(11, 20, 31, .16));
    --ui-scrim: rgba(0, 0, 0, .4);

    /* Panel widths. */
    --ui-menu-w: 244px;
    --ui-sheet-w: 480px;

    /* Identity tints for the avatar square (four, plus the ink on each). */
    --ui-tint-1: #f3e2d6; --ui-tint-1-ink: #7a4526;
    --ui-tint-2: #e0e8dc; --ui-tint-2-ink: #3d5a36;
    --ui-tint-3: #dee6ee; --ui-tint-3-ink: #34506b;
    --ui-tint-4: #e7e0ee; --ui-tint-4-ink: #5a4472;

    /* Stacking inside the layer, on the platform ladder. */
    --ui-z-menu: var(--z-menu, 200);
    --ui-z-scrim: var(--z-overlay, 500);
    --ui-z-sheet: var(--z-modal, 1000);
  }

  /* The dark side of the tints and the scrim only; every other value above
     already follows the theme layer's light/dark swap. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      --ui-tint-1: #3d2c22; --ui-tint-1-ink: #ecc9b3;
      --ui-tint-2: #2a352a; --ui-tint-2-ink: #c5d9bd;
      --ui-tint-3: #25303c; --ui-tint-3-ink: #bfd0e2;
      --ui-tint-4: #31283a; --ui-tint-4-ink: #d3c5e0;
      --ui-scrim: rgba(0, 0, 0, .55);
      --ui-ctl-line: #7c7c80;
    }
  }
  :root[data-theme="dark"] {
    --ui-tint-1: #3d2c22; --ui-tint-1-ink: #ecc9b3;
    --ui-tint-2: #2a352a; --ui-tint-2-ink: #c5d9bd;
    --ui-tint-3: #25303c; --ui-tint-3-ink: #bfd0e2;
    --ui-tint-4: #31283a; --ui-tint-4-ink: #d3c5e0;
    --ui-scrim: rgba(0, 0, 0, .55);
    --ui-ctl-line: #7c7c80;
  }

  /* --- Motion the controls share. Prefixed names, so a site's own `spin` or
     `reveal` keyframes are never shadowed. --- */
  @keyframes ui-reveal { from { opacity: 0; transform: translateY(4px); } }
  @keyframes ui-spin { to { transform: rotate(360deg); } }
  @keyframes ui-skel { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }

  /* --- Visually hidden, still announced. --- */
  .ui-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* --- Button: solid, accent, line, text, on three heights --------------- */

  /* One solid button per surface, line for the alternative, text for the
     escape. The layer ships no red variant: a destructive action reads well as
     a text control that confirms inline, and a site that wants a danger button
     adds one in its own CSS. */
  .ui-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ui-gap, 8px);
    height: var(--ui-h-md, 32px);
    padding: 0 var(--ui-pad, 12px);
    border: 1px solid transparent;
    border-radius: var(--ui-radius, 4px);
    background: transparent;
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    font-weight: var(--ui-weight, 500);
    white-space: nowrap;
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-btn.sm { height: var(--ui-h-sm, 28px); padding: 0 var(--ui-pad-sm, 10px); font-size: var(--ui-text-sm, 13px); }
  .ui-btn.lg { height: var(--ui-h-lg, 40px); padding: 0 var(--ui-pad-lg, 18px); font-size: var(--ui-text-lg, 15px); }

  .ui-btn.solid { background: var(--ui-ink, #1d1d1f); color: var(--ui-ink-inverse, #ffffff); }
  .ui-btn.solid:hover, .ui-btn.solid.is-hover { background: var(--ui-ink-muted, #6e6e73); }
  .ui-btn.accent { background: var(--ui-accent, #0a84ff); color: var(--ui-accent-ink, #ffffff); }
  .ui-btn.accent:hover, .ui-btn.accent.is-hover { background: var(--ui-accent-hover, #006fe6); }
  .ui-btn.line { border-color: var(--ui-line-strong, #aeaeb2); color: var(--ui-ink, #1d1d1f); }
  .ui-btn.line:hover, .ui-btn.line.is-hover { background: var(--ui-hover); border-color: var(--ui-ink-muted, #6e6e73); }
  .ui-btn.text { color: var(--ui-ink-muted, #6e6e73); padding: 0 var(--ui-gap, 8px); }
  .ui-btn.text.sm { padding: 0 6px; }
  .ui-btn.text:hover, .ui-btn.text.is-hover { background: var(--ui-hover); color: var(--ui-ink, #1d1d1f); }

  .ui-btn:active:not(:disabled):not(.is-loading),
  .ui-btn.is-press { transform: translateY(var(--ui-press, 1px)); }
  .ui-btn:focus-visible, .ui-btn.is-focus { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  /* Selected: a line or text button used as a toggle, and the same ground
     while a button holds its menu open. */
  .ui-btn[aria-pressed="true"]:not(.solid):not(.accent),
  .ui-btn[aria-haspopup][aria-expanded="true"]:not(.solid):not(.accent),
  .ui-btn.is-selected:not(.solid):not(.accent) {
    background: var(--ui-hover);
    border-color: var(--ui-line-strong, #aeaeb2);
    color: var(--ui-ink, #1d1d1f);
  }
  /* Loading: the label stays, the control stops taking input, one spinner. */
  .ui-btn.is-loading { pointer-events: none; cursor: progress; }
  .ui-btn.is-loading::before {
    content: "";
    width: 14px;
    height: 14px;
    flex: none;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    opacity: .7;
    animation: ui-spin 700ms linear infinite;
  }
  .ui-btn:disabled, .ui-btn.is-disabled, .ui-btn[aria-disabled="true"] {
    opacity: var(--ui-disabled, .55);
    pointer-events: none;
  }

  /* --- Icon button: the same three heights, square ----------------------- */

  .ui-iconbtn {
    --ui-ib: var(--ui-h-md, 32px);
    width: var(--ui-ib);
    height: var(--ui-ib);
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--ui-radius, 4px);
    background: transparent;
    color: var(--ui-ink-muted, #6e6e73);
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease),
      opacity var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-iconbtn.sm { --ui-ib: var(--ui-h-sm, 28px); }
  .ui-iconbtn.lg { --ui-ib: var(--ui-h-lg, 40px); }
  .ui-iconbtn:hover, .ui-iconbtn.is-hover { background: var(--ui-hover); color: var(--ui-ink, #1d1d1f); }
  /* The solid and accent icon buttons are the committing control (a composer's
     send square, a sheet's confirm): ink by default, the accent once armed. */
  .ui-iconbtn.solid { background: var(--ui-ink, #1d1d1f); color: var(--ui-ink-inverse, #ffffff); }
  .ui-iconbtn.solid:hover, .ui-iconbtn.solid.is-hover { background: var(--ui-ink-muted, #6e6e73); }
  .ui-iconbtn.accent, .ui-iconbtn.solid.is-armed {
    background: var(--ui-accent, #0a84ff);
    color: var(--ui-accent-ink, #ffffff);
  }
  .ui-iconbtn.accent:hover, .ui-iconbtn.accent.is-hover,
  .ui-iconbtn.solid.is-armed:hover { background: var(--ui-accent-hover, #006fe6); }
  .ui-iconbtn:active:not(:disabled), .ui-iconbtn.is-press { transform: translateY(var(--ui-press, 1px)); }
  .ui-iconbtn:focus-visible, .ui-iconbtn.is-focus { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  /* A trigger holding its menu open keeps the hover ground. Only a menu
     trigger: aria-expanded also describes a rail, which is not pressed. */
  .ui-iconbtn[aria-haspopup][aria-expanded="true"]:not(.solid):not(.accent),
  .ui-iconbtn[aria-pressed="true"]:not(.solid):not(.accent),
  .ui-iconbtn.is-selected:not(.solid):not(.accent) { background: var(--ui-hover); color: var(--ui-ink, #1d1d1f); }
  .ui-iconbtn.is-loading { pointer-events: none; cursor: progress; }
  .ui-iconbtn:disabled, .ui-iconbtn.is-disabled, .ui-iconbtn[aria-disabled="true"] {
    opacity: var(--ui-disabled, .55);
    pointer-events: none;
  }
  /* Idle: a committing control with nothing to commit yet. Dimmer than
     disabled, because it is about to become live. */
  .ui-iconbtn.is-idle { opacity: .35; pointer-events: none; }

  /* --- Link ------------------------------------------------------------- */

  .ui-link {
    border: 0;
    padding: 0;
    background: transparent;
    font: inherit;
    color: var(--ui-ink, #1d1d1f);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--ui-line-strong, #aeaeb2);
    cursor: pointer;
    border-radius: var(--ui-radius-mark, 2px);
    transition:
      text-decoration-color var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-link:hover, .ui-link.is-hover { text-decoration-color: currentColor; }
  .ui-link:active, .ui-link.is-press { color: var(--ui-ink-muted, #6e6e73); }
  .ui-link:focus-visible, .ui-link.is-focus { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  /* The quiet link carries no rule until it is asked for. */
  .ui-link.quiet { color: var(--ui-ink-muted, #6e6e73); text-decoration: none; }
  .ui-link.quiet:hover {
    color: var(--ui-ink, #1d1d1f);
    text-decoration: underline;
    text-decoration-color: var(--ui-line-strong, #aeaeb2);
  }
  .ui-link.is-disabled, .ui-link[aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* --- Fields: input, textarea, select, unit, file, drop target ---------- */

  .ui-field { display: flex; flex-direction: column; gap: 6px; }
  .ui-label {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
  }

  .ui-input {
    height: var(--ui-field-h, 40px);
    width: 100%;
    padding: 0 var(--ui-pad, 12px);
    border: 1px solid var(--ui-line-strong, #aeaeb2);
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-raised);
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    transition:
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      box-shadow var(--ui-t-hover, 150ms) var(--ui-ease),
      background var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-input.sm { height: var(--ui-h-md, 32px); padding: 0 var(--ui-pad-sm, 10px); }
  .ui-input::placeholder { color: var(--ui-ink-faint, #86868b); }
  .ui-input:hover:not(:disabled):not([readonly]), .ui-input.is-hover { border-color: var(--ui-ink-muted, #6e6e73); }
  /* A field's focus is its own edge, stepped up to the accent and doubled in
     weight. A 2px ring outside a 1px edge reads as two rings with a gap. It is
     never taken away for the mouse: a browser treats a text field as
     focus-visible however it was reached, and both paths land here. */
  .ui-input:focus, .ui-input:focus-visible, .ui-input.is-focus {
    border-color: var(--ui-accent, #0a84ff);
    box-shadow: 0 0 0 1px var(--ui-accent, #0a84ff);
    outline: none;
  }
  /* Invalid is the full ink on the edge with the reason in words underneath. */
  .ui-input.is-invalid, .ui-input[aria-invalid="true"] {
    border-color: var(--ui-ink, #1d1d1f);
    box-shadow: 0 0 0 1px var(--ui-ink, #1d1d1f);
  }
  .ui-input[readonly] { color: var(--ui-ink-muted, #6e6e73); background: var(--ui-rail); cursor: default; }
  .ui-input:disabled { color: var(--ui-ink-muted, #6e6e73); opacity: var(--ui-disabled, .55); pointer-events: none; }

  textarea.ui-input {
    height: auto;
    min-height: var(--ui-field-h, 40px);
    padding: var(--ui-pad-sm, 10px) var(--ui-pad, 12px);
    line-height: var(--ui-leading, 20px);
    resize: vertical;
  }
  textarea.ui-input.sm { min-height: var(--ui-h-md, 32px); padding: 6px var(--ui-pad-sm, 10px); }
  select.ui-input {
    appearance: none;
    -webkit-appearance: none;
    background-image: none;
    padding-right: 32px;
    cursor: pointer;
  }
  .ui-select { position: relative; display: block; }
  .ui-select > svg {
    position: absolute;
    right: var(--ui-pad, 12px);
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    color: var(--ui-ink-muted, #6e6e73);
  }
  .ui-select:has(select:disabled) > svg { opacity: var(--ui-disabled, .55); }

  /* A unit sits inside the field's right edge and never takes a click. */
  .ui-unit { position: relative; display: block; }
  .ui-unit .ui-input { padding-right: 40px; }
  .ui-unit > span {
    position: absolute;
    right: var(--ui-pad, 12px);
    top: 0;
    height: var(--ui-field-h, 40px);
    display: inline-flex;
    align-items: center;
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text-sm, 13px);
    pointer-events: none;
  }
  .ui-unit:has(.ui-input.sm) > span { height: var(--ui-h-md, 32px); }

  /* The file control: a named button with the chosen file beside it (the real
     input stays visually hidden), and the drop target for an upload sheet. */
  .ui-file { display: flex; align-items: center; gap: var(--ui-pad, 12px); min-width: 0; }
  .ui-file__name {
    font-size: var(--ui-text-sm, 13px);
    line-height: var(--ui-leading, 20px);
    color: var(--ui-ink-muted, #6e6e73);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .ui-file:has(input:focus-visible) .ui-btn { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  .ui-file.is-disabled { opacity: var(--ui-disabled, .55); pointer-events: none; }

  .ui-drop {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--ui-pad-sm, 10px);
    min-height: 112px;
    padding: var(--ui-pad-card, 16px);
    border: 1px solid var(--ui-line-strong, #aeaeb2);
    border-radius: var(--ui-radius-card, 8px);
    background: var(--ui-rail);
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    text-align: center;
    cursor: pointer;
    transition:
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      background var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-drop:hover, .ui-drop.is-hover, .ui-drop.is-over {
    border-color: var(--ui-ink, #1d1d1f);
    background: var(--ui-pressed);
  }
  .ui-drop:active { background: var(--ui-hover); }
  .ui-drop:focus-visible, .ui-drop:focus-within, .ui-drop.is-focus {
    border-color: var(--ui-accent, #0a84ff);
    box-shadow: 0 0 0 1px var(--ui-accent, #0a84ff);
    outline: none;
  }
  .ui-drop .ui-hint {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-faint, #86868b);
  }
  .ui-drop.is-invalid {
    border-color: var(--ui-ink, #1d1d1f);
    box-shadow: 0 0 0 1px var(--ui-ink, #1d1d1f);
  }
  .ui-drop.is-disabled { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* --- Segmented control and tab track ---------------------------------- */

  /* .ui-seg is the ink-selected track (a filter toolbar, a unit switch).
     .ui-tabs is the raised-segment track (a header's tab strip, a term
     switch), and it is what a [role="tablist"] wears. Both carry --ui-seg-h,
     so sizing the control sizes every segment. */
  .ui-seg {
    --ui-seg-h: var(--ui-field-h, 40px);
    display: inline-flex;
    height: var(--ui-seg-h);
    border: 1px solid var(--ui-line-strong, #aeaeb2);
    border-radius: var(--ui-radius, 4px);
    overflow: hidden;
  }
  .ui-seg.sm { --ui-seg-h: var(--ui-h-md, 32px); }
  .ui-seg > button, .ui-seg > a {
    height: 100%;
    padding: 0 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-left: 1px solid var(--ui-line-strong, #aeaeb2);
    background: transparent;
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text, 14px);
    font-weight: var(--ui-weight, 500);
    white-space: nowrap;
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-seg.sm > button, .ui-seg.sm > a { padding: 0 var(--ui-pad, 12px); font-size: var(--ui-text-sm, 13px); }
  .ui-seg > :first-child { border-left: 0; }
  .ui-seg > button:hover, .ui-seg > a:hover, .ui-seg > .is-hover {
    background: var(--ui-hover);
    color: var(--ui-ink, #1d1d1f);
  }
  .ui-seg > button:active, .ui-seg > a:active { background: var(--ui-pressed); }
  /* A segment's ring sits inside the track, so the track's own edge is not
     doubled by a ring drawn outside it. */
  .ui-seg > :focus-visible, .ui-seg > .is-focus {
    outline: var(--ui-ring);
    outline-offset: calc(0px - var(--ui-ring-off, 2px));
  }
  .ui-seg > .is-on,
  .ui-seg > [aria-pressed="true"],
  .ui-seg > [aria-checked="true"],
  .ui-seg > [aria-selected="true"] { background: var(--ui-ink, #1d1d1f); color: var(--ui-ink-inverse, #ffffff); }
  .ui-seg > .is-on:hover, .ui-seg > [aria-selected="true"]:hover { background: var(--ui-ink, #1d1d1f); }
  .ui-seg > button:disabled, .ui-seg > [aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }

  .ui-tabs {
    --ui-seg-h: var(--ui-h-md, 32px);
    display: inline-flex;
    align-items: center;
    height: var(--ui-seg-h);
    padding: 2px;
    gap: 2px;
    flex: none;
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-rail);
  }
  .ui-tabs.lg { --ui-seg-h: var(--ui-field-h, 40px); }
  .ui-tabs > a, .ui-tabs > button, .ui-tabs > span {
    height: calc(var(--ui-seg-h) - 6px);
    padding: 0 var(--ui-pad, 12px);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    border-radius: var(--ui-radius, 4px);
    background: transparent;
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text-sm, 13px);
    line-height: var(--ui-leading, 20px);
    font-weight: var(--ui-weight, 500);
    white-space: nowrap;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-tabs > a, .ui-tabs > button { cursor: pointer; }
  .ui-tabs.lg > a, .ui-tabs.lg > button, .ui-tabs.lg > span { padding: 0 var(--ui-pad-lg, 18px); font-size: var(--ui-text, 14px); }
  .ui-tabs > a:hover, .ui-tabs > button:hover, .ui-tabs > .is-hover {
    color: var(--ui-ink, #1d1d1f);
    background: var(--ui-hover);
  }
  .ui-tabs > a:active, .ui-tabs > button:active { background: var(--ui-pressed); }
  .ui-tabs > :focus-visible, .ui-tabs > .is-focus {
    outline: var(--ui-ring);
    outline-offset: calc(0px - var(--ui-ring-off, 2px));
  }
  .ui-tabs > .is-on,
  .ui-tabs > [aria-checked="true"],
  .ui-tabs > [aria-selected="true"] {
    background: var(--ui-raised);
    color: var(--ui-ink, #1d1d1f);
    border-color: var(--ui-line, #d2d2d7);
    box-shadow: var(--ui-shadow);
  }
  .ui-tabs > .is-on:hover, .ui-tabs > [aria-selected="true"]:hover { background: var(--ui-raised); }
  .ui-tabs > button:disabled, .ui-tabs > [aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* A tab panel. /components.js toggles `hidden` on the panels it controls;
     the panel takes the ring when a page moves focus into it on selection. */
  .ui-panel { padding-top: var(--ui-pad, 12px); }
  .ui-panel:focus-visible { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }

  /* --- Choice rows: one answer (radio) and many (checkbox) -------------- */

  /* The whole row is the target, so a coarse pointer never has to hit a 16px
     mark. One answer gets a round mark, many answers a squared one (.multi),
     the familiar distinction; both marks read their shape from a token. */
  .ui-choice {
    display: flex;
    align-items: center;
    gap: var(--ui-pad, 12px);
    min-height: var(--ui-touch, 44px);
    padding: 0 var(--ui-pad, 12px);
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius, 4px);
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-choice:hover, .ui-choice.is-hover { background: var(--ui-rail); }
  .ui-choice:active, .ui-choice.is-press { transform: translateY(var(--ui-press, 1px)); }
  .ui-choice:has(input:focus-visible), .ui-choice:focus-visible, .ui-choice.is-focus {
    outline: var(--ui-ring);
    outline-offset: var(--ui-ring-off, 2px);
  }
  .ui-choice input { position: absolute; opacity: 0; width: 0; height: 0; }
  .ui-choice .ui-dot {
    width: 16px;
    height: 16px;
    flex: none;
    border: 1.5px solid var(--ui-line-strong, #aeaeb2);
    border-radius: 50%;
    display: grid;
    place-items: center;
    transition: border-color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-choice.is-on, .ui-choice:has(input:checked) { border-color: var(--ui-ink, #1d1d1f); }
  .ui-choice.is-on .ui-dot, .ui-choice:has(input:checked) .ui-dot { border-color: var(--ui-ink, #1d1d1f); }
  .ui-choice.is-on .ui-dot::after, .ui-choice:has(input:checked) .ui-dot::after {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ui-ink, #1d1d1f);
  }
  .ui-choice.multi .ui-dot, .ui-choice.multi .ui-dot::after { border-radius: var(--ui-radius-mark, 2px); }
  .ui-choice.is-invalid, .ui-choice[aria-invalid="true"] {
    border-color: var(--ui-ink, #1d1d1f);
    box-shadow: 0 0 0 1px var(--ui-ink, #1d1d1f);
  }
  .ui-choice.is-disabled, .ui-choice:has(input:disabled) { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* --- Switch: a toggle shaped by the radius tokens -------------------- */

  /* Ours, not promoted: nascen had no switch. Track and knob both take
     their shape from the radius tokens (the track --ui-radius, the knob
     --ui-radius-mark), so it arrives squared like the rest of the layer and
     becomes the familiar rounded one by changing those two. State reads from the
     knob's position and the track's ground, not from a colour. */
  .ui-switch {
    --ui-sw-w: 40px;
    --ui-sw-h: 22px;
    position: relative;
    display: inline-flex;
    align-items: center;
    width: var(--ui-sw-w);
    height: var(--ui-sw-h);
    flex: none;
    padding: 2px;
    border: 1px solid var(--ui-line-strong, #aeaeb2);
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-rail);
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      border-color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-switch::after {
    content: "";
    width: calc(var(--ui-sw-h) - 8px);
    height: calc(var(--ui-sw-h) - 8px);
    border-radius: var(--ui-radius-mark, 2px);
    background: var(--ui-ink-muted, #6e6e73);
    transform: translateX(0);
    transition:
      transform var(--ui-t-hover, 150ms) var(--ui-ease),
      background var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-switch:hover, .ui-switch.is-hover { background: var(--ui-hover); }
  .ui-switch:focus-visible, .ui-switch.is-focus { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  .ui-switch[aria-checked="true"], .ui-switch.is-on {
    background: var(--ui-ink, #1d1d1f);
    border-color: var(--ui-ink, #1d1d1f);
  }
  .ui-switch[aria-checked="true"]::after, .ui-switch.is-on::after {
    background: var(--ui-ink-inverse, #ffffff);
    transform: translateX(calc(var(--ui-sw-w) - var(--ui-sw-h) - 2px));
  }
  .ui-switch:disabled, .ui-switch[aria-disabled="true"], .ui-switch.is-disabled {
    opacity: var(--ui-disabled, .55);
    pointer-events: none;
  }

  /* --- Chip (a filter that toggles) and badge (a label that does not) --- */

  .ui-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: var(--ui-h-md, 32px);
    padding: 0 14px;
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-ground);
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text, 14px);
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease),
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-chip.sm { height: var(--ui-h-sm, 28px); padding: 0 var(--ui-pad-sm, 10px); font-size: var(--ui-text-sm, 13px); }
  .ui-chip.lg { height: var(--ui-h-lg, 40px); padding: 0 var(--ui-pad-lg, 18px); }
  .ui-chip:hover, .ui-chip.is-hover { background: var(--ui-rail); color: var(--ui-ink, #1d1d1f); }
  .ui-chip:active, .ui-chip.is-press { transform: translateY(var(--ui-press, 1px)); }
  .ui-chip:focus-visible, .ui-chip.is-focus { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  .ui-chip.is-on, .ui-chip[aria-pressed="true"], .ui-chip.is-selected {
    border-color: var(--ui-ink, #1d1d1f);
    color: var(--ui-ink, #1d1d1f);
    background: var(--ui-pressed);
  }
  .ui-chip:disabled, .ui-chip.is-disabled, .ui-chip[aria-disabled="true"] {
    opacity: var(--ui-disabled, .55);
    pointer-events: none;
  }

  /* A badge states a fact (a count, a status word). It is not a control, so it
     has no hover, no press and no focus. */
  .ui-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: var(--ui-h-sm, 28px);
    padding: 0 var(--ui-gap, 8px);
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-rail);
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    font-weight: var(--ui-weight, 500);
    white-space: nowrap;
  }
  .ui-badge.solid { background: var(--ui-ink, #1d1d1f); border-color: var(--ui-ink, #1d1d1f); color: var(--ui-ink-inverse, #ffffff); }
  .ui-badge.accent { background: var(--ui-accent, #0a84ff); border-color: var(--ui-accent, #0a84ff); color: var(--ui-accent-ink, #ffffff); }
  .ui-badge.quiet { background: transparent; }

  /* --- Rows: the rail row and the hairline list row --------------------- */

  /* A navigation row in a rail or a menu: one line, a hover ground, the ring
     drawn inside so it never overlaps the panel edge. */
  .ui-row {
    display: flex;
    align-items: center;
    gap: var(--ui-pad-sm, 10px);
    width: 100%;
    height: var(--ui-row-h, 36px);
    padding: 0 var(--ui-pad-sm, 10px);
    border: 0;
    border-radius: var(--ui-radius, 4px);
    background: transparent;
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    text-align: left;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    transition: background var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-row:hover, .ui-row.is-active, .ui-row.is-hover { background: var(--ui-hover); }
  .ui-row:active { background: var(--ui-pressed); }
  .ui-row:focus-visible, .ui-row.is-focus {
    outline: var(--ui-ring);
    outline-offset: calc(0px - var(--ui-ring-off, 2px));
  }
  .ui-row > .ui-label-text { flex: 1; overflow: hidden; text-overflow: ellipsis; }
  .ui-row .ui-meta {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
  }
  .ui-row.quiet { color: var(--ui-ink-muted, #6e6e73); }
  .ui-row.is-selected { background: var(--ui-pressed); }
  .ui-row.is-disabled, .ui-row[aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* The hairline list row: a text stack, a trailing status word, a chevron
     when the row opens something, and actions revealed on hover or focus.
     A list keeps its own columns and heights in its own CSS. */
  .ui-list-row {
    display: flex;
    align-items: center;
    gap: var(--ui-pad, 12px);
    width: 100%;
    min-height: var(--ui-h-lg, 40px);
    padding: var(--ui-gap, 8px);
    border: 0;
    border-radius: var(--ui-radius, 4px);
    background: transparent;
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    text-align: left;
    outline-offset: calc(0px - var(--ui-ring-off, 2px));
    transition: background var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  a.ui-list-row, button.ui-list-row, .ui-list-row[tabindex] { cursor: pointer; }
  .ui-list-row:hover, .ui-list-row.is-hover, .ui-list-row.is-open, .ui-list-row:focus-within {
    background: var(--ui-pressed);
  }
  .ui-list-row:focus-visible, .ui-list-row.is-focus { outline: var(--ui-ring); }
  .ui-list-row.is-selected { background: var(--ui-hover); }
  .ui-list-row.is-disabled, .ui-list-row[aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }
  .ui-list-row + .ui-list-row { border-top: 1px solid var(--ui-line, #d2d2d7); }

  .ui-list-row .ui-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  .ui-list-row .ui-title {
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ui-list-row .ui-desc, .ui-list-row .ui-meta {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ui-list-row .ui-status {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
    flex: none;
  }
  .ui-list-row .ui-thumb {
    width: var(--ui-h-lg, 40px);
    height: var(--ui-h-lg, 40px);
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-ground);
    color: var(--ui-ink-muted, #6e6e73);
  }
  .ui-list-row .ui-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .ui-chev { color: var(--ui-ink-faint, #86868b); flex: none; display: inline-flex; }

  /* The revealed actions: small text controls in the row's own ink. They stay
     in the DOM (and reachable by tab, which reveals them through
     :focus-within), so a keyboard never loses an action a mouse can see. */
  .ui-ops { display: none; gap: 2px; flex: none; }
  .ui-list-row:hover .ui-ops,
  .ui-list-row.is-hover .ui-ops,
  .ui-list-row.is-open .ui-ops,
  .ui-list-row:focus-within .ui-ops { display: inline-flex; }
  .ui-ops > button {
    height: var(--ui-h-sm, 28px);
    padding: 0 var(--ui-gap, 8px);
    border: 0;
    border-radius: var(--ui-radius, 4px);
    background: transparent;
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text-sm, 13px);
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-ops > button:hover, .ui-ops > button.is-hover { background: var(--ui-hover); color: var(--ui-ink, #1d1d1f); }
  .ui-ops > button:active { transform: translateY(var(--ui-press, 1px)); }
  .ui-ops > button:focus-visible { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  .ui-ops > button:disabled { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* --- Card ------------------------------------------------------------- */

  /* The base card is a surface: a hairline, 8px, the page ground. Only a card
     that leads somewhere (a link, a button, one a page marks tappable) takes
     hover, press and focus. */
  .ui-card {
    --ui-card-ground: var(--ui-ground);
    display: flex;
    flex-direction: column;
    gap: var(--ui-pad, 12px);
    padding: var(--ui-pad-card, 16px);
    background: var(--ui-card-ground);
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius-card, 8px);
    color: var(--ui-ink, #1d1d1f);
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      box-shadow var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  a.ui-card, button.ui-card, .ui-card[tabindex], .ui-card.is-tap { cursor: pointer; text-align: left; }
  a.ui-card:hover, button.ui-card:hover, .ui-card[tabindex]:hover, .ui-card.is-hover {
    --ui-card-ground: var(--ui-rail);
    border-color: var(--ui-line-strong, #aeaeb2);
    box-shadow: var(--ui-shadow);
  }
  a.ui-card:active, button.ui-card:active, .ui-card[tabindex]:active, .ui-card.is-press {
    transform: translateY(var(--ui-press, 1px));
  }
  .ui-card:focus-visible, .ui-card.is-focus { outline: var(--ui-ring); outline-offset: var(--ui-ring-off, 2px); }
  .ui-card.is-selected { border-color: var(--ui-accent, #0a84ff); }
  .ui-card.is-invalid { border-color: var(--ui-ink, #1d1d1f); }
  .ui-card.is-disabled, .ui-card[aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* What a card says: a title with a date at the right, a figure with its unit
     in the secondary ink. Anything below that (charts, lists) is the page's. */
  .ui-card .ui-card-top { display: flex; align-items: center; gap: 4px; }
  .ui-card .ui-card-title { font-size: var(--ui-text, 14px); line-height: var(--ui-leading, 20px); font-weight: var(--ui-weight, 500); }
  .ui-card .ui-card-when {
    margin-left: auto;
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
    font-variant-numeric: tabular-nums;
  }
  .ui-card .ui-card-value {
    font-size: var(--ui-text-figure, 20px);
    line-height: var(--ui-leading-figure, 28px);
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }
  .ui-card .ui-card-value .ui-unit-text {
    font-size: var(--ui-text-sm, 13px);
    font-weight: var(--ui-weight, 500);
    color: var(--ui-ink-muted, #6e6e73);
    margin-left: 3px;
  }
  .ui-card .ui-card-note {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
  }

  /* --- Dropdown menu (behaviour: /components.js, [data-ui-menu]) -------- */

  .ui-dropdown { position: relative; display: inline-flex; }
  /* The popover is the positioned wrapper the behaviour toggles; the menu is
     the panel. aria-hidden is the single source of truth for open state, so no
     class has to agree with it. */
  .ui-dropdown > [data-ui-popover] { position: absolute; top: calc(100% + 4px); left: 0; z-index: var(--ui-z-menu, 200); }
  .ui-dropdown > [data-ui-popover][data-align="end"] { left: auto; right: 0; }
  [data-ui-popover][aria-hidden="true"] { display: none; }
  .ui-menu {
    width: var(--ui-menu-w, 244px);
    max-width: calc(100vw - 2 * var(--ui-pad, 12px));
    padding: 6px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: var(--ui-raised);
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius-card, 8px);
    box-shadow: var(--ui-shadow-float);
    animation: ui-reveal var(--ui-t-hover, 150ms) var(--ui-ease) both;
  }
  .ui-menu .ui-menu-head {
    padding: 6px var(--ui-pad-sm, 10px) var(--ui-gap, 8px);
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .ui-menu .ui-menu-sep { height: 1px; background: var(--ui-line, #d2d2d7); margin: 4px; }
  /* A menu item is a rail row that also answers to the roving highlight the
     keyboard drives (aria-activedescendant + .is-active from /components.js),
     so mouse and keyboard show the same active item. */
  .ui-menu [role^="menuitem"] { position: relative; }
  .ui-menu [role^="menuitem"].is-active { background: var(--ui-hover); }
  .ui-menu [role^="menuitem"][aria-checked="true"]::after {
    content: "";
    width: 6px;
    height: 6px;
    flex: none;
    margin-left: auto;
    border-radius: var(--ui-radius-mark, 2px);
    background: var(--ui-ink, #1d1d1f);
  }
  .ui-menu [role^="menuitem"][aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }

  /* --- Accordion (behaviour: /components.js, [data-ui-accordion]) ------- */

  /* Native details and summary, so it works with the JS absent; the JS adds
     the exclusive-open rule and the disabled guard. */
  .ui-accordion { display: flex; flex-direction: column; }
  .ui-accordion > details { border-top: 1px solid var(--ui-line, #d2d2d7); }
  .ui-accordion > details:last-of-type { border-bottom: 1px solid var(--ui-line, #d2d2d7); }
  .ui-accordion > details > summary {
    display: flex;
    align-items: center;
    gap: var(--ui-pad, 12px);
    min-height: var(--ui-touch, 44px);
    padding: var(--ui-pad-sm, 10px) 0;
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    font-weight: var(--ui-weight, 500);
    cursor: pointer;
    list-style: none;
    transition: color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-accordion > details > summary::-webkit-details-marker { display: none; }
  .ui-accordion > details > summary::after {
    content: "";
    width: 8px;
    height: 8px;
    flex: none;
    margin-left: auto;
    border-right: 1.5px solid var(--ui-ink-faint, #86868b);
    border-bottom: 1.5px solid var(--ui-ink-faint, #86868b);
    transform: rotate(45deg) translateY(-2px);
    transition: transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-accordion > details[open] > summary::after { transform: rotate(225deg) translateY(-2px); }
  .ui-accordion > details > summary:hover, .ui-accordion > details > summary.is-hover { color: var(--ui-ink, #1d1d1f); }
  .ui-accordion > details > summary:hover::after { border-color: var(--ui-ink, #1d1d1f); }
  .ui-accordion > details > summary:focus-visible, .ui-accordion > details > summary.is-focus {
    outline: var(--ui-ring);
    outline-offset: var(--ui-ring-off, 2px);
  }
  .ui-accordion > details > .ui-accordion-body {
    padding: 0 0 var(--ui-pad-card, 16px);
    color: var(--ui-ink-muted, #6e6e73);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
  }
  .ui-accordion > details[aria-disabled="true"] > summary,
  .ui-accordion > details > summary[aria-disabled="true"] { opacity: var(--ui-disabled, .55); cursor: not-allowed; }

  /* --- Dialog and sheet (behaviour: /components.js, [data-ui-dialog]) --- */

  /* A native <dialog>, opened with showModal() by the behaviour module, so the
     browser supplies the top layer, the inert background and the backdrop. The
     phone form is the bottom sheet with an M3 handle; the foot always names
     the escape, so no close square is needed at that width. */
  .ui-sheet {
    width: var(--ui-sheet-w, 480px);
    max-width: calc(100vw - 2 * var(--ui-pad-card, 16px));
    max-height: calc(100vh - 48px);
    overflow-y: auto;
    padding: 20px 24px 24px;
    display: flex;
    flex-direction: column;
    gap: var(--ui-pad-card, 16px);
    margin: auto;
    background: var(--ui-raised);
    color: var(--ui-ink, #1d1d1f);
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius-card, 8px);
    box-shadow: var(--ui-shadow-sheet);
  }
  dialog.ui-sheet:not([open]) { display: none; }
  dialog.ui-sheet[open] { animation: ui-reveal var(--ui-t-struct, 240ms) var(--ui-ease) both; }
  dialog.ui-sheet::backdrop { background: var(--ui-scrim, rgba(0, 0, 0, .4)); }
  .ui-sheet:focus-visible { outline: none; }
  .ui-sheet .ui-sheet-handle { display: none; }
  .ui-sheet .ui-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ui-pad, 12px);
    min-height: var(--ui-h-md, 32px);
    flex: none;
  }
  .ui-sheet .ui-sheet-head h2 { font-size: 16px; line-height: 24px; font-weight: var(--ui-weight, 500); margin: 0; }
  .ui-sheet .ui-sheet-foot { display: flex; gap: var(--ui-gap, 8px); justify-content: flex-end; padding-top: 4px; }

  /* The scrim for anything that is not a native dialog (a mobile rail). */
  .ui-scrim {
    position: fixed;
    inset: 0;
    z-index: var(--ui-z-scrim, 500);
    background: var(--ui-scrim, rgba(0, 0, 0, .4));
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-scrim.is-on { opacity: 1; pointer-events: auto; }

  @media (max-width: 767px) {
    .ui-sheet {
      width: 100%;
      max-width: 100%;
      max-height: 88vh;
      margin: auto auto 0;
      gap: 14px;
      padding: var(--ui-gap, 8px) var(--ui-pad-card, 16px) calc(var(--ui-pad-card, 16px) + var(--safe-bottom, 0px));
      border-radius: var(--ui-radius-card, 8px) var(--ui-radius-card, 8px) 0 0;
      border-bottom: 0;
      box-shadow: none;
    }
    .ui-sheet .ui-sheet-head .ui-iconbtn { display: none; }
    .ui-sheet .ui-sheet-handle {
      display: block;
      width: 32px;
      height: 4px;
      flex: none;
      margin: 0 auto;
      border-radius: var(--ui-radius-mark, 2px);
      background: var(--ui-line-strong, #aeaeb2);
    }
    .ui-sheet .ui-sheet-foot .ui-btn { flex: 1; }
  }

  /* --- Forms ----------------------------------------------------------- */

  .ui-form { display: flex; flex-direction: column; gap: var(--ui-pad-card, 16px); }
  .ui-form-inline {
    margin: var(--ui-gap, 8px) 0 var(--ui-pad-card, 16px);
    padding: var(--ui-pad-card, 16px);
    display: flex;
    flex-direction: column;
    gap: 14px;
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius-card, 8px);
  }
  .ui-form .ui-form-foot, .ui-form-inline .ui-form-foot {
    display: flex;
    gap: var(--ui-gap, 8px);
    justify-content: flex-end;
    align-items: center;
  }
  .ui-form .ui-form-foot { margin-top: var(--ui-gap, 8px); }
  .ui-form .ui-form-foot .ui-grow { flex: 1; }
  .ui-form.is-busy, .ui-form-inline.is-busy { pointer-events: none; opacity: var(--ui-disabled, .55); }

  /* --- Status and help text -------------------------------------------- */

  /* One helper line under a field, one status line under a form. An error is
     the same line in the full ink, never red: the words carry the failure. */
  .ui-help, .ui-status {
    font-size: var(--ui-text-label, 12px);
    line-height: var(--ui-leading-label, 16px);
    color: var(--ui-ink-muted, #6e6e73);
  }
  .ui-help.is-error, .ui-status.is-error,
  .ui-help[role="alert"], .ui-status[role="alert"] { color: var(--ui-ink, #1d1d1f); }
  .ui-help:empty, .ui-status:empty { display: none; }
  .ui-status.is-busy { display: inline-flex; align-items: center; gap: var(--ui-gap, 8px); }

  /* --- Skeleton and spinner -------------------------------------------- */

  /* Loading is a shape breathing on the quiet ground, never a gradient sweep;
     under reduced motion it stands still. */
  .ui-skeleton {
    background: var(--ui-pressed);
    border-radius: var(--ui-radius, 4px);
    animation: ui-skel 1.2s var(--ui-ease) infinite;
  }
  .ui-skeleton.line { height: 12px; }
  .ui-skeleton.line.short { width: 40%; }
  .ui-skeleton.block { height: 72px; border-radius: var(--ui-radius-card, 8px); }
  .ui-skeleton.square { width: 28px; height: 28px; flex: none; }
  .ui-spinner {
    width: 16px;
    height: 16px;
    flex: none;
    border: 2px solid var(--ui-line-strong, #aeaeb2);
    border-top-color: var(--ui-ink, #1d1d1f);
    border-radius: 50%;
    animation: ui-spin 700ms linear infinite;
  }
  .ui-spinner.lg { width: 24px; height: 24px; }

  /* --- Ticker: a live number whose changed digits roll ------------------ */

  /* Owner ruling 2026-10-28 (docs/DESIGN-TASTE.md): the digits that change
     roll to their new value, the digits that stay do not move. Each digit is
     a fixed width column (tabular figures) that clips a strip of 0 to 9; the
     strip moves by transform only, so a change never lays anything out. A
     new value mid roll retargets the running roll from where it is.
     components.js builds the columns, runs each roll as a Web Animation
     (--ui-ticker-t, --ui-ticker-ease) and owns the screen reader text. No
     strip holds a compositor layer between rolls (2026-10-28, W5b).
     --ui-ticker-cols reserves columns, so a longer number grows into space
     it already holds; the digits sit at the end of that space. Under reduced
     motion the change is instant. */
  .ui-ticker {
    --ui-ticker-lh: 1.2em;
    --ui-ticker-t: 560ms;
    --ui-ticker-ease: cubic-bezier(.2, .8, .2, 1);
    display: inline-block;
    position: relative;
    height: var(--ui-ticker-lh);
    line-height: var(--ui-ticker-lh);
    vertical-align: top;
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
    overflow: hidden;
    contain: layout paint;
  }
  /* The face holds the reserved width and stacks its columns at its end,
     like an odometer: a new leading digit appears in reserved space on the
     left, and no column that was already there moves. */
  .ui-ticker__face {
    display: inline-flex;
    user-select: none;
    justify-content: flex-end;
    min-width: calc(var(--ui-ticker-cols, 0) * 1ch);
    height: var(--ui-ticker-lh);
  }
  /* The strip holds all ten digits, so the column is as wide as the widest
     one, and with tabular figures every digit is that wide. */
  .ui-ticker__col {
    display: inline-block;
    height: var(--ui-ticker-lh);
    overflow: hidden;
  }
  .ui-ticker__strip {
    display: block;
    white-space: pre;
    transform: translateY(0);
  }
  /* The ten digits as generated content: no text node, so the ticker's
     text (copy, find, innerText) is the screen reader copy alone. */
  .ui-ticker__strip::before { content: "0\A 1\A 2\A 3\A 4\A 5\A 6\A 7\A 8\A 9"; }
  .ui-ticker__ch::before { content: attr(data-ch); }
  .ui-ticker__ch { display: inline-block; height: var(--ui-ticker-lh); }

  /* --- Tooltip: what a number or a control means ------------------------ */

  /* Added 2026-10-28 for the Kleto Pulse summary ruling ("every number has a
     tooltip... reachable by hover, focus and tap, and it never shifts
     layout"). Markup: any element with data-ui-tip="Text"; a line break in
     the text starts a new line. components.js makes the element focusable,
     shows one shared bubble on hover (mouse), focus and tap, keeps it in the
     viewport, and hides it on Escape, a tap elsewhere or a scroll away. The
     bubble is position: fixed in <body>, so opening it moves nothing. */
  [data-ui-tip] { cursor: help; }
  [data-ui-tip]:focus-visible { outline: var(--ui-ring); outline-offset: var(--ui-ring-off); }
  .ui-tip {
    position: fixed;
    z-index: 1000;
    top: 0;
    left: 0;
    max-width: min(340px, calc(100vw - 16px));
    max-height: calc(100dvh - 16px);
    overflow-y: auto;
    padding: 8px 10px;
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-ink, #1d1d1f);
    color: var(--ui-ink-inverse, #ffffff);
    box-shadow: var(--ui-shadow-sheet);
    font-size: var(--ui-text-label, 12px);
    line-height: 1.5;
    white-space: pre-line;
    overflow-wrap: anywhere;
    pointer-events: none;
  }
  .ui-tip[hidden] { display: none; }
  .ui-tip.is-pinned { pointer-events: auto; }

  /* --- Avatar: the identity square -------------------------------------- */

  .ui-avatar {
    width: 28px;
    height: 28px;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
    border-radius: var(--ui-radius, 4px);
    background: var(--ui-tint-1);
    color: var(--ui-tint-1-ink);
    font-size: var(--ui-text-sm, 13px);
    font-weight: 600;
    user-select: none;
  }
  .ui-avatar.t2 { background: var(--ui-tint-2); color: var(--ui-tint-2-ink); }
  .ui-avatar.t3 { background: var(--ui-tint-3); color: var(--ui-tint-3-ink); }
  .ui-avatar.t4 { background: var(--ui-tint-4); color: var(--ui-tint-4-ink); }
  .ui-avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; display: block; }
  /* Selection is an accent ring with a 2px gap of whatever ground the square
     sits on: the focus ring's grammar in the accent. */
  .ui-avatar.is-selected { box-shadow: 0 0 0 2px var(--ui-accent, #0a84ff); }
  .ui-avatar.sm { width: 24px; height: 24px; font-size: var(--ui-text-label, 12px); }
  .ui-avatar.lg { width: 64px; height: 64px; font-size: 26px; }
  .ui-avatar.is-disabled { opacity: var(--ui-disabled, .55); }

  /* --- Control band: the shell ------------------------------------------ */

  /* Kleto Pulse control band rulings of 2026-10-29 (docs/DESIGN-TASTE.md).
     .ui-bar is one row of controls that never wraps. It maps the control
     scale onto the --ui-ctl-* tokens, so every house control inside it
     (button, icon button, field, chip, segment) takes the band height and
     the band corners, and nothing in the row sits at its own height.
     The bar is a size container named ui-bar, so its children compose by
     the bar's own width, not the viewport's: a phone-sized bar (under
     600px) visually hides every .ui-bar__label, which keeps the control's
     accessible name, and squares the button that held it (so that button
     carries an icon beside the label). Slots:
     .ui-bar__grow takes the free width (a search field), .ui-bar__end holds
     the trailing controls. The bar takes its width from its parent, so in a
     shrink-to-fit parent (inline-flex, a float) give it a width. */
  .ui-bar {
    --ui-h-md: var(--ui-ctl-h, 40px);
    --ui-h-lg: var(--ui-ctl-h, 40px);
    --ui-field-h: var(--ui-ctl-h, 40px);
    --ui-radius: var(--ui-ctl-r, 8px);
    container: ui-bar / inline-size;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--ui-ctl-gap, 8px);
    min-width: 0;
    min-height: var(--ui-ctl-h, 40px);
  }
  .ui-bar > .ui-bar__grow { flex: 1 1 0; min-width: 0; }
  .ui-bar > .ui-bar__end {
    display: flex;
    flex: none;
    align-items: center;
    gap: var(--ui-ctl-gap, 8px);
    margin-inline-start: auto;
  }
  @container ui-bar (max-width: 599px) {
    .ui-bar__label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    .ui-bar :is(button, a):has(> .ui-bar__label) {
      width: var(--ui-ctl-h, 40px);
      padding-inline: 0;
      justify-content: center;
    }
  }

  /* --- Control band: search field (behaviour: /components.js, [data-ui-search]) */

  /* A label around an input of type search. The input reserves the icon's
     slot on its left and the clear button's slot on its right, so the icon
     and the button the script adds land in space already held: no shift.
     The clear button is the control height square, so it is 44px on touch.
     The field's focus is its own edge doubled in the accent, as for every
     house field; forced colours get a real outline instead, because they
     drop box-shadow. 16px text on a coarse pointer stops iOS zooming in. */
  .ui-search {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
    min-width: 0;
    height: var(--ui-ctl-h, 40px);
    color: var(--ui-ink-muted, #6e6e73);
  }
  .ui-search > input {
    width: 100%;
    height: 100%;
    min-width: 0;
    margin: 0;
    padding: 0 var(--ui-ctl-h, 40px) 0 36px;
    border: 1px solid var(--ui-ctl-line, #86868b);
    border-radius: var(--ui-ctl-r, 8px);
    background: var(--ui-raised);
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    -webkit-appearance: none;
    appearance: none;
    transition:
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      box-shadow var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-search > input::placeholder { color: var(--ui-ink-muted, #6e6e73); opacity: 1; }
  .ui-search > input::-webkit-search-cancel-button,
  .ui-search > input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; display: none; }
  .ui-search > input:hover:not(:disabled) { border-color: var(--ui-ink-muted, #6e6e73); }
  .ui-search > input:focus, .ui-search > input.is-focus {
    border-color: var(--ui-accent, #0a84ff);
    box-shadow: 0 0 0 1px var(--ui-accent, #0a84ff);
    outline: none;
  }
  .ui-search > input:disabled { opacity: var(--ui-disabled, .55); pointer-events: none; }
  .ui-search__icon {
    position: absolute;
    left: 12px;
    top: 50%;
    width: 16px;
    height: 16px;
    transform: translateY(-50%);
    pointer-events: none;
  }
  .ui-search__clear {
    position: absolute;
    top: 0;
    right: 0;
    width: var(--ui-ctl-h, 40px);
    height: 100%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: var(--ui-ctl-r, 8px);
    background: transparent;
    color: var(--ui-ink-muted, #6e6e73);
    cursor: pointer;
    transition: color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-search__clear[hidden] { display: none; }
  .ui-search__clear:hover { color: var(--ui-ink, #1d1d1f); }
  /* The shortcut hint: a fine pointer only, while the field is empty and
     not focused, and never when the shortcuts are off. */
  .ui-search__key {
    position: absolute;
    right: 10px;
    top: 50%;
    display: none;
    align-items: center;
    justify-content: center;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    transform: translateY(-50%);
    border: 1px solid var(--ui-line-strong, #aeaeb2);
    border-radius: var(--ui-radius-mark, 2px);
    font: inherit;
    font-size: var(--ui-text-label, 12px);
    line-height: 1;
    color: var(--ui-ink-muted, #6e6e73);
    pointer-events: none;
  }
  @media (pointer: fine) {
    .ui-search:not(.has-value):not(:focus-within) > .ui-search__key { display: inline-flex; }
  }
  :root[data-ui-search-keys="off"] .ui-search > .ui-search__key { display: none; }
  @media (pointer: coarse) {
    .ui-search > input { font-size: 16px; }
  }
  @media (forced-colors: active) {
    .ui-search > input:focus { outline: 2px solid Highlight; outline-offset: 0; }
  }

  /* --- Control band: chip row (behaviour: /components.js, [data-ui-chips]) */

  /* One choice from many in one row that scrolls inside itself and never
     wraps or scrolls the page sideways. The row is the scroll box; 4px of
     padding with a matching negative margin leaves room for the focus ring
     the clip would cut. The script sets data-overflow-start and
     data-overflow-end while there is more to scroll on that side, which
     opens a 32px edge fade, and data-overflow while the row overflows at
     all. Chips sit at the control height with the 3:1 control edge; a label
     past --ui-chip-max (16ch) ends in an ellipsis and the script puts the
     full name in a tooltip. The count is a reserved, tabular slot, so a
     count that gains a digit does not push its neighbours. */
  :is(.ui-chips, [data-ui-chips]) {
    --ui-fade-s: 0px;
    --ui-fade-e: 0px;
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: var(--ui-ctl-gap, 8px);
    min-width: 0;
    max-width: calc(100% + 8px);
    padding: 4px;
    margin: -4px;
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-padding-inline: 36px;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--ui-fade-s), #000 calc(100% - var(--ui-fade-e)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--ui-fade-s), #000 calc(100% - var(--ui-fade-e)), transparent 100%);
  }
  :is(.ui-chips, [data-ui-chips])::-webkit-scrollbar { display: none; }
  :is(.ui-chips, [data-ui-chips])[data-overflow-start] { --ui-fade-s: 32px; }
  :is(.ui-chips, [data-ui-chips])[data-overflow-end] { --ui-fade-e: 32px; }
  .ui-bar > :is(.ui-chips, [data-ui-chips]) { flex: 1 1 auto; }
  :is(.ui-chips, [data-ui-chips]) > .ui-chip {
    flex: none;
    height: var(--ui-ctl-h, 40px);
    padding: 0 var(--ui-pad, 12px);
    border-color: var(--ui-ctl-line, #86868b);
    border-radius: var(--ui-ctl-r, 8px);
    background: var(--ui-raised);
    white-space: nowrap;
  }
  :is(.ui-chips, [data-ui-chips]) > .ui-chip:hover,
  :is(.ui-chips, [data-ui-chips]) > .ui-chip.is-hover { background: var(--ui-hover); }
  .ui-chip__label {
    min-width: 0;
    max-width: var(--ui-chip-max, 16ch);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  /* A trailing action ("+", "All groups") is a chip that is not a choice.
     An icon-only action is a square with a dashed edge: an add, not a
     group. */
  :is(.ui-chips, [data-ui-chips]) > .ui-chip.ui-chip--action { color: var(--ui-ink-muted, #6e6e73); }
  :is(.ui-chips, [data-ui-chips]) > .ui-chip.ui-chip--action:has(> svg:only-child) {
    width: var(--ui-ctl-h, 40px);
    padding: 0;
    justify-content: center;
    border-style: dashed;
  }

  /* An inline count: tabular digits in a reserved width, muted at rest and
     the selected ink on a selected control. */
  .ui-count {
    display: inline-block;
    min-width: var(--ui-count-w, 2ch);
    font-size: var(--ui-text-sm, 13px);
    font-variant-numeric: tabular-nums;
    color: var(--ui-ink-muted, #6e6e73);
  }

  /* The band's one selected state (ruling 2026-10-29): a tint of the
     selection edge, the edge drawn 2px as a 1px border plus a 1px inset
     shadow (so the size never changes), and an ink that reads 4.5:1 on the
     tint. The doubled edge is the cue that is not colour. Forced colours
     drop the shadow, so there the chip is painted Highlight with
     HighlightText. A toggle chip (aria-pressed) takes the same state inside
     a .ui-bar or a sheet, so the band's filter chips match its scope chips;
     elsewhere the toggle chip keeps its ink state. */
  .ui-chip[role="radio"][aria-checked="true"],
  :is(.ui-bar, [data-ui-sheet]) .ui-chip[aria-pressed="true"] {
    border-color: var(--ui-sel-edge, var(--ui-accent, #0a84ff));
    box-shadow: inset 0 0 0 1px var(--ui-sel-edge, var(--ui-accent, #0a84ff));
    background: var(--ui-sel-ground, color-mix(in srgb, var(--ui-sel-edge, var(--ui-accent, #0a84ff)) 12%, transparent));
    color: var(--ui-sel-ink, color-mix(in srgb, var(--ui-sel-edge, var(--ui-accent, #0a84ff)) 72%, var(--ui-ink, #1d1d1f)));
  }
  .ui-chip[aria-checked="true"] > .ui-count,
  .ui-chip[aria-pressed="true"] > .ui-count { color: inherit; }
  @media (forced-colors: active) {
    .ui-chip[role="radio"][aria-checked="true"],
    :is(.ui-bar, [data-ui-sheet]) .ui-chip[aria-pressed="true"],
    .ui-chip[aria-pressed="true"], .ui-chip.is-on, .ui-chip.is-selected {
      forced-color-adjust: none;
      border-color: Highlight;
      box-shadow: none;
      background: Highlight;
      color: HighlightText;
    }
  }

  /* --- Control band: one focus ring ------------------------------------ */

  /* The layer's ring reads --ui-ring, which :root resolves once, so a scope
     that sets --ui-ring-color never reaches it, and the house default ring
     is a translucent blue under 3:1 on a dark ground. Band controls draw
     their own ring, 2px solid and 2px out, in --ui-ctl-ring (default the
     selection edge, which defaults to the accent: 3.6:1 on #fff, 7.1:1 on
     #000), read at the control so a scope can set it. The clear button
     inside the search field keeps its ring inside the field. */
  .ui-bar .ui-btn:focus-visible, .ui-bar .ui-btn.is-focus,
  .ui-bar .ui-iconbtn:focus-visible, .ui-bar .ui-iconbtn.is-focus,
  :is(.ui-chips, [data-ui-chips]) > .ui-chip:focus-visible,
  :is(.ui-chips, [data-ui-chips]) > .ui-chip.is-focus {
    outline: var(--ui-ring-w, 2px) solid var(--ui-ctl-ring, var(--ui-sel-edge, var(--ui-accent, #0a84ff)));
    outline-offset: var(--ui-ring-off, 2px);
  }
  .ui-search__clear:focus-visible {
    outline: var(--ui-ring-w, 2px) solid var(--ui-ctl-ring, var(--ui-sel-edge, var(--ui-accent, #0a84ff)));
    outline-offset: -6px;
  }

  /* --- Control band: value button (behaviour: /components.js, [data-ui-menu]) */

  /* A button that names its current value ("Most requests") and opens the
     house menu: the trigger of a .ui-dropdown. An optional leading icon, the
     value, and a chevron drawn in CSS (two borders, so forced colours keep
     it). In a phone-sized .ui-bar a value marked .ui-bar__label folds away
     and the button becomes its icon, still named by the hidden value. */
  .ui-menubtn {
    display: inline-flex;
    align-items: center;
    gap: var(--ui-gap, 8px);
    height: var(--ui-ctl-h, 40px);
    min-width: 0;
    max-width: 100%;
    padding: 0 12px;
    border: 1px solid var(--ui-ctl-line, #86868b);
    border-radius: var(--ui-ctl-r, 8px);
    background: var(--ui-raised);
    color: var(--ui-ink, #1d1d1f);
    font-size: var(--ui-text, 14px);
    line-height: var(--ui-leading, 20px);
    font-weight: var(--ui-weight, 500);
    white-space: nowrap;
    cursor: pointer;
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      transform var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-menubtn > svg { flex: none; color: var(--ui-ink-muted, #6e6e73); }
  .ui-menubtn__value { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .ui-menubtn::after {
    content: "";
    flex: none;
    width: 6px;
    height: 6px;
    margin: -3px 2px 0;
    border-right: 1.5px solid var(--ui-ink-muted, #6e6e73);
    border-bottom: 1.5px solid var(--ui-ink-muted, #6e6e73);
    transform: rotate(45deg);
  }
  .ui-menubtn[aria-expanded="true"]::after { margin-top: 3px; transform: rotate(-135deg); }
  .ui-menubtn:hover, .ui-menubtn.is-hover, .ui-menubtn[aria-expanded="true"] { background: var(--ui-hover); }
  .ui-menubtn:active { transform: translateY(var(--ui-press, 1px)); }
  .ui-menubtn:focus-visible, .ui-menubtn.is-focus {
    outline: var(--ui-ring-w, 2px) solid var(--ui-ctl-ring, var(--ui-sel-edge, var(--ui-accent, #0a84ff)));
    outline-offset: var(--ui-ring-off, 2px);
  }
  .ui-menubtn:disabled, .ui-menubtn[aria-disabled="true"] { opacity: var(--ui-disabled, .55); pointer-events: none; }
  @container ui-bar (max-width: 599px) {
    .ui-menubtn:has(> .ui-bar__label)::after { display: none; }
  }
  /* The menu's tick and highlight are grounds, which forced colours drop:
     paint them in system colours there. */
  @media (forced-colors: active) {
    .ui-menu [role^="menuitem"][aria-checked="true"]::after { forced-color-adjust: none; background: CanvasText; }
    .ui-menu [role^="menuitem"].is-active { outline: 2px solid Highlight; outline-offset: -2px; }
  }

  /* --- Control band: segmented control (behaviour: /components.js, [data-ui-seg]) */

  /* The radio group form of .ui-seg, opted into with data-ui-seg (the plain
     .ui-seg keeps its ink-selected look for the surfaces that use it). An
     8px track on the rail ground with the 3:1 control edge and 2px of
     padding; the chosen segment is a 6px thumb inside it, in the band's one
     selected state (tint, 2px accent edge, accent ink), not a solid fill.
     Segments share the width equally and never shrink below their label,
     so a row too tight for its labels wants the icon form. Each segment's hit area reaches over
     the track's padding, so the whole 44px track is the target on touch.
     The icon form (.is-icons, or automatic in a .ui-bar under 340px wide,
     a 360px phone) hides each .ui-seg__label visually, shows its
     .ui-seg__icon and squares the segments; the script then gives each
     segment its label as a tooltip. */
  .ui-seg[data-ui-seg] {
    --ui-seg-h: var(--ui-ctl-h, 40px);
    display: inline-grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 2px;
    padding: 2px;
    height: var(--ui-seg-h);
    border: 1px solid var(--ui-ctl-line, #86868b);
    border-radius: var(--ui-ctl-r, 8px);
    background: var(--ui-rail);
    overflow: visible;
  }
  .ui-seg[data-ui-seg] > button, .ui-seg[data-ui-seg] > a {
    position: relative;
    height: auto;
    gap: 6px;
    padding: 0 var(--ui-pad, 12px);
    border: 1px solid transparent;
    border-radius: calc(var(--ui-ctl-r, 8px) - 2px);
    background: transparent;
    color: var(--ui-ink-muted, #6e6e73);
    transition:
      background var(--ui-t-hover, 150ms) var(--ui-ease),
      border-color var(--ui-t-hover, 150ms) var(--ui-ease),
      color var(--ui-t-hover, 150ms) var(--ui-ease);
  }
  .ui-seg[data-ui-seg] > button::before, .ui-seg[data-ui-seg] > a::before {
    content: "";
    position: absolute;
    inset: -4px -1px;
  }
  .ui-seg[data-ui-seg] > :is(button, a):not([aria-checked="true"]):not([aria-pressed="true"]):not(.is-on):is(:hover, .is-hover) {
    background: var(--ui-hover);
    color: var(--ui-ink, #1d1d1f);
  }
  .ui-seg[data-ui-seg] > [aria-checked="true"],
  .ui-seg[data-ui-seg] > [aria-pressed="true"],
  .ui-seg[data-ui-seg] > .is-on {
    border-color: var(--ui-sel-edge, var(--ui-accent, #0a84ff));
    box-shadow: inset 0 0 0 1px var(--ui-sel-edge, var(--ui-accent, #0a84ff));
    background: var(--ui-sel-ground, color-mix(in srgb, var(--ui-sel-edge, var(--ui-accent, #0a84ff)) 12%, transparent));
    color: var(--ui-sel-ink, color-mix(in srgb, var(--ui-sel-edge, var(--ui-accent, #0a84ff)) 72%, var(--ui-ink, #1d1d1f)));
  }
  .ui-seg[data-ui-seg] > :focus-visible, .ui-seg[data-ui-seg] > .is-focus {
    outline: var(--ui-ring-w, 2px) solid var(--ui-ctl-ring, var(--ui-sel-edge, var(--ui-accent, #0a84ff)));
    outline-offset: 1px;
  }
  .ui-seg__icon { display: none; flex: none; }
  .ui-seg__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  .ui-seg.is-icons > * > .ui-seg__icon { display: block; }
  .ui-seg.is-icons > * > .ui-seg__label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .ui-seg.is-icons > button, .ui-seg.is-icons > a { width: calc(var(--ui-seg-h) - 6px); padding: 0; }
  @container ui-bar (max-width: 339px) {
    .ui-seg[data-ui-seg]:has(.ui-seg__icon) > * > .ui-seg__icon { display: block; }
    .ui-seg[data-ui-seg]:has(.ui-seg__icon) > * > .ui-seg__label {
      position: absolute;
      width: 1px;
      height: 1px;
      overflow: hidden;
      clip-path: inset(50%);
      white-space: nowrap;
    }
    .ui-seg[data-ui-seg]:has(.ui-seg__icon) > :is(button, a) { width: calc(var(--ui-seg-h) - 6px); padding: 0; }
  }
  /* Forced colours drop grounds and shadows, which is all the ink form and
     the thumb form show a choice with: paint every chosen segment
     Highlight there. */
  @media (forced-colors: active) {
    .ui-seg > :is(.is-on, [aria-pressed="true"], [aria-checked="true"], [aria-selected="true"]),
    .ui-seg[data-ui-seg] > :is(.is-on, [aria-pressed="true"], [aria-checked="true"]) {
      forced-color-adjust: none;
      border-color: Highlight;
      box-shadow: none;
      background: Highlight;
      color: HighlightText;
    }
  }

  /* --- Control band: sheet or popover (behaviour: /components.js, dialog[data-ui-sheet]) */

  /* The house dialog with data-ui-sheet. The script sets data-mode at each
     open: "sheet" is a bottom sheet with a handle, at any width (a touch
     tablet included), raised above an on-screen keyboard by
     --ui-sheet-kb; "pop" is a popover the script places beside its opener,
     with a clear backdrop that still closes it on a tap; "center" is the
     plain modal. The head's close button stays visible in every mode
     (swipe is never the only way out). Content can put a search field above
     a .ui-sheet-body, which is the part that scrolls. A radio row in the
     sheet shows its choice with the selected ink and a tick that forced
     colours keep. */
  dialog.ui-sheet[data-ui-sheet] .ui-sheet-head .ui-iconbtn { display: inline-flex; }
  dialog.ui-sheet[data-ui-sheet]:has(> .ui-sheet-body) { overflow: hidden; }
  dialog.ui-sheet[data-ui-sheet] > :not(.ui-sheet-body) { flex: none; }
  .ui-sheet > .ui-sheet-body {
    flex: 1 1 auto;
    min-height: 0;
    margin: 0 -4px;
    padding: 2px 4px;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  dialog.ui-sheet[data-ui-sheet][data-mode="pop"] {
    position: fixed;
    inset: auto;
    width: var(--ui-pop-w, 320px);
    max-width: calc(100vw - 32px);
    max-height: calc(100dvh - 32px);
    margin: 0;
    padding: 12px;
    gap: 12px;
    border: 1px solid var(--ui-line, #d2d2d7);
    border-radius: var(--ui-radius-card, 8px);
    box-shadow: var(--ui-shadow-float);
  }
  dialog.ui-sheet[data-ui-sheet][data-mode="pop"]::backdrop { background: transparent; }
  dialog.ui-sheet[data-ui-sheet][data-mode="pop"] .ui-sheet-handle { display: none; }
  dialog.ui-sheet[data-ui-sheet][data-mode="pop"] .ui-sheet-head h2 { font-size: var(--ui-text, 14px); line-height: var(--ui-leading, 20px); }
  dialog.ui-sheet[data-ui-sheet][data-mode="sheet"] {
    width: 100%;
    max-width: 100%;
    max-height: calc(88dvh - var(--ui-sheet-kb, 0px));
    margin: auto auto var(--ui-sheet-kb, 0px);
    padding: var(--ui-gap, 8px) var(--ui-pad-card, 16px) calc(var(--ui-pad-card, 16px) + var(--safe-bottom, 0px));
    gap: var(--ui-pad, 12px);
    border-radius: var(--ui-radius-card, 8px) var(--ui-radius-card, 8px) 0 0;
    border-bottom: 0;
    box-shadow: none;
  }
  dialog.ui-sheet[data-ui-sheet][data-mode="sheet"] .ui-sheet-handle {
    display: block;
    width: 32px;
    height: 4px;
    flex: none;
    margin: 0 auto;
    border-radius: var(--ui-radius-mark, 2px);
    background: var(--ui-line-strong, #aeaeb2);
  }
  [data-ui-sheet] .ui-sheet-body .ui-row { height: var(--ui-ctl-h, 40px); }
  [data-ui-sheet] .ui-sheet-body .ui-row > .ui-count { margin-inline-start: auto; text-align: end; }
  [data-ui-sheet] .ui-row[role="radio"][aria-checked="true"] {
    color: var(--ui-sel-ink, color-mix(in srgb, var(--ui-sel-edge, var(--ui-accent, #0a84ff)) 72%, var(--ui-ink, #1d1d1f)));
  }
  [data-ui-sheet] .ui-row[role="radio"][aria-checked="true"] > .ui-count { color: inherit; }
  [data-ui-sheet] .ui-row[role="radio"]::after {
    content: "";
    width: 6px;
    height: 6px;
    flex: none;
    border-radius: var(--ui-radius-mark, 2px);
  }
  [data-ui-sheet] .ui-row[role="radio"][aria-checked="true"]::after { background: currentColor; }
  @media (forced-colors: active) {
    [data-ui-sheet] .ui-row[role="radio"][aria-checked="true"]::after { forced-color-adjust: none; background: CanvasText; }
  }

  /* --- Control band: count badge on a button ---------------------------- */

  /* A count on a button (Filter with 2 active) sits on its top right corner
     as an overlay, on an icon button and a labelled one alike, so a count
     that appears, grows or goes never moves anything. An empty badge is not
     drawn. Ink on the ink ground by default, which reads in both themes;
     --ui-badge-ground and --ui-badge-ink retint it (Pulse: its accent). The
     badge is a picture of the count: mark it aria-hidden and put the count
     in the button's name (aria-label="Filter, 2 active"). */
  :is(.ui-iconbtn, .ui-btn, .ui-menubtn):has(> .ui-badge) { position: relative; }
  :is(.ui-iconbtn, .ui-btn, .ui-menubtn) > .ui-badge {
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 20px;
    height: 20px;
    padding: 0 4px;
    justify-content: center;
    border: 2px solid var(--ui-ground);
    border-radius: 10px;
    background: var(--ui-badge-ground, var(--ui-ink, #1d1d1f));
    color: var(--ui-badge-ink, var(--ui-ink-inverse, #ffffff));
    font-size: 11px;
    line-height: 16px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    pointer-events: none;
  }
  :is(.ui-iconbtn, .ui-btn, .ui-menubtn) > .ui-badge:empty { display: none; }

  /* In a band every bordered control shares the 3:1 control edge, and an
     icon button is a bordered square like its neighbours, not a bare
     glyph, so the row reads as one set of controls. */
  .ui-bar .ui-btn.line { border-color: var(--ui-ctl-line, #86868b); background: var(--ui-raised); }
  .ui-bar .ui-iconbtn:not(.solid):not(.accent) {
    border: 1px solid var(--ui-ctl-line, #86868b);
    background: var(--ui-raised);
    color: var(--ui-ink, #1d1d1f);
  }
  .ui-bar .ui-btn.line:hover, .ui-bar .ui-iconbtn:not(.solid):not(.accent):hover { background: var(--ui-hover); }

  /* --- Touch: every target at least --ui-touch on a coarse pointer ----- */

  @media (pointer: coarse) {
    /* The band's control height follows the pointer, not the width, so a
       touch tablet gets the 44px of a phone (ruling 2026-10-29). */
    :root { --ui-ctl-h: var(--ui-touch, 44px); }
    .ui-btn, .ui-chip { min-height: var(--ui-touch, 44px); }
    .ui-iconbtn { min-width: var(--ui-touch, 44px); min-height: var(--ui-touch, 44px); }
    .ui-ops > button { min-height: var(--ui-touch, 44px); padding: 0 var(--ui-pad, 12px); }
    .ui-row, .ui-list-row { min-height: var(--ui-touch, 44px); }
    .ui-row { height: auto; }
    .ui-input, .ui-input.sm { min-height: var(--ui-touch, 44px); }
    .ui-switch { --ui-sw-h: 26px; --ui-sw-w: 48px; }
    /* A segmented control's segments are adjacent, so the track is the target
       and every segment fills it. */
    .ui-seg, .ui-seg.sm, .ui-tabs, .ui-tabs.lg { --ui-seg-h: var(--ui-touch, 44px); }
    .ui-link { padding: 2px 0; }
  }

  /* --- Reduced motion -------------------------------------------------- */

  /* Every duration above is var(--ui-t-hover) or var(--ui-t-struct), and the
     platform.interaction layer collapses all of them (transition, animation
     and scroll) under prefers-reduced-motion for the whole page, so the layer
     needs no !important of its own. A site serving this layer without
     platform.css sets --ui-t-hover and --ui-t-struct to 0ms in its own
     reduced-motion block; a transitionend still fires either way. */
  @media (prefers-reduced-motion: reduce) {
    :root { --ui-t-hover: 0.001ms; --ui-t-struct: 0.001ms; }
  }
}
