/* ═══════════════════════════════════════════════════════════════════════════
   KNOX VISION V5 · knox-v5.css
   Master design system for Pulse, Knox Artiste Accounts and Grid.
   Locked 23-08-2026. Extracted from design-system-v5.html, never hand edited.
   Re-extract from the master when the master changes.

   USE
     <link rel="stylesheet" href="knox-v5.css">
     <script src="knox-v5.js" defer></script>
     <html data-theme="dark" data-btn="key" data-finish="machined"
           data-rail="carved" data-topbar="full" data-tabs="underline" data-dock="icons">

   Fonts, in <head>:
     <link rel="preconnect" href="https://fonts.googleapis.com">
     <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
     <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Special+Gothic+Expanded+One&family=Archivo:wdth,wght@62..125,300..800&family=Figtree:wght@300..900&family=Urbanist:wght@300..800&family=Geist+Mono:wght@400;500;600&display=swap">

   No-flash theme script, inline in <head> BEFORE this stylesheet:
     <script>(function(){try{var t=localStorage.getItem("knox-v5-theme");
     if(t==="light"||t==="dark")document.documentElement.setAttribute("data-theme",t);}catch(e){}})();</script>

   THEMING IS A SEED CHANGE, NEVER A FORK. To re-dress an app, override the five
   seeds and nothing else. Any new accent must sit at least 45 degrees from the
   status hue in OKLCH, or a brand button and a form error read the same.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════
   KNOX VISION V5 LAB
   An audition console. Nothing here is locked. Knox tunes, exports the block,
   pastes it back, and V5 gets built from the values he chose.
   Built 23-08-2026 on V4 (12 Jun 2026), through Adam.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── LAYER 0 · THE SEEDS ─────────────────────────────────────────────────────
   V4 had five. V5 has SIX: accent is split out from status, because a
   red-branded client currently cannot have a visible error state and a
   gold-branded one cannot have a warning. Hard rule: accent and status sit at
   least 45 degrees apart in hue.                                             */
:root {
  --seed-ink:      #0D0D0D;   /* LOCKED 23-08-2026 */   /* dark ground */
  --seed-paper:    #E6E6E6;   /* LOCKED */   /* light ground */
  --seed-accent-hi:#A82938;   /* LOCKED */   /* brand gradient, high */
  --seed-accent-lo:#1F0007;   /* LOCKED */   /* brand gradient, low  */
  --seed-status:   #FF7033;   /* LOCKED, Knox's own value, restored 23-08-2026 after he saw the
                                amber alternative and rejected it. KNOWN TRADEOFF: this sits only
                                22 degrees from the brand red in OKLCH, so danger and accent are
                                not separable by hue. Destructive actions must therefore be told
                                apart by POSITION, LABEL and ICON, never by colour alone, and
                                anything financial in Knox Artiste Accounts needs an explicit
                                confirm step. Do not "fix" this back to amber. */   /* danger anchor. warn / ok / info rotate off it */

  /* ── LAYER 0b · SHAPE, SPACE, MOTION, TYPE SCALE ──
     All four are dials in the console. V4 hard-coded every one of them. */
  --u: 5px;                                    /* spacing unit, density-driven */
  --r: 20px;                                   /* THE radius. Everything derives. */
  --r-xs: calc(var(--r) * .4);                 /* marks, inputs, menu rows */
  --r-lg: calc(var(--r) * 1.4);                /* cards, modals, the app frame */
  --r-pill: 999px;
  --dur: 1;                                    /* motion multiplier */
  --ctrl-h: 36px;                              /* ONE height for anything on a control row */
  /* THE CONTROL BAR LAW. Everything on one horizontal bar shares this height AND
     this corner. A pill next to a rounded rectangle reads as two systems, which
     is what the composer looked like. The only exception is a glyph key: a plus
     or a minus is meant to read as a small square button. */
  --bar-r: var(--btn-r);
  --ts: 1;                                     /* type scale multiplier */
  --elev: 1.1;                                   /* elevation strength multiplier */

  --sp-1: calc(var(--u) * 1);   --sp-2: calc(var(--u) * 2);
  --sp-3: calc(var(--u) * 3);   --sp-4: calc(var(--u) * 4);
  --sp-5: calc(var(--u) * 6);   --sp-6: calc(var(--u) * 8);
  --sp-7: calc(var(--u) * 12);  --sp-8: calc(var(--u) * 16);

  /* Z-scale. V4 hard-coded 0,1,2,50,60,70,1000 with no system. */
  --z-base: 1; --z-chrome: 20; --z-sticky: 30; --z-drop: 40;
  --z-overlay: 60; --z-modal: 70; --z-toast: 80; --z-console: 90;

  /* Type scale. Seven steps, and only TWO of them live below body. V5's first
     pass ran six sizes between 9px and 13.5px, which is not a hierarchy.
     Below body, difference comes from weight, case and colour. */
  --fs-caps:    calc(11px * var(--ts));   /* every uppercase label, eyebrow, table head, status, count */
  --fs-sm:      calc(13px * var(--ts));   /* meta, help, dense control text, card titles */
  --fs-md:      calc(15px * var(--ts));   /* body */
  --fs-lg:      calc(19px * var(--ts));   /* lede, card title, secondary metric */
  --fs-xl:      calc(26px * var(--ts));   /* section heading */
  --fs-2xl:     calc(34px * var(--ts));   /* metric */
  --fs-display: clamp(calc(30px * var(--ts)), 5.2vw, calc(64px * var(--ts)));

  /* Line heights and tracking, tokenised. V4 set both inline, everywhere. */
  --lh-display: .96; --lh-head: 1.14; --lh-body: 1.6; --lh-ui: 1.35;
  --ls-display: -.02em; --ls-head: .01em; --ls-body: 0; --ls-caps: .12em;

  /* Motion. Two house curves plus a press and a spring. Adam caps this at three
     in new work; V4's four are grandfathered and kept because the press curve
     is load-bearing on every key. */
  --ease-entrance: cubic-bezier(.23,1,.32,1);
  --ease-hover:    cubic-bezier(.33,1,.68,1);
  --ease-press:    cubic-bezier(.22,.61,.36,1);
  --ease-spring:   cubic-bezier(.34,1.56,.64,1);
  --d-press: calc(70ms  * var(--dur));
  --d-fast:  calc(140ms * var(--dur));
  --d-base:  calc(280ms * var(--dur));
  --d-slow:  calc(400ms * var(--dur));

  /* Type roles. Hero is LOCKED to Special Gothic Expanded One (Knox, 23-08).
     The other four are console dials. */
  --f-hero:  'Special Gothic Expanded One', 'Arial Black', sans-serif;
  --f-head:  'Special Gothic Expanded One', 'Arial Black', sans-serif;
  --f-ctrl:  'Archivo', system-ui, sans-serif;
  --f-body:  'Figtree', system-ui, sans-serif;
  --f-num:   'Urbanist', system-ui, sans-serif;
  --f-brand: 'Special Gothic Expanded One', 'Arial Black', sans-serif;  /* eyebrows, stamps, brand moments */
  --f-ui:    var(--f-ctrl);
  --f:       var(--f-body);
  --fm:      'Geist Mono', ui-monospace, monospace;
  --num-wt:  700;    /* proportional faces need 700; monos need 500 */

  --noise: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");

  --grad-accent: linear-gradient(160deg in oklab, var(--seed-accent-hi) 0%,
    color-mix(in oklab, var(--seed-accent-hi) 45%, var(--seed-accent-lo)) 48%,
    var(--seed-accent-lo) 100%);
}

/* ── LAYER 1 · DARK DERIVATION ───────────────────────────────────────────── */
:root, [data-theme="dark"] {
  color-scheme: dark;

  --bg:            var(--seed-ink);
  --surface:       color-mix(in oklch, var(--seed-ink) 96%, white);
  --surface-deep:  color-mix(in oklch, var(--seed-ink) 85%, var(--seed-accent-lo));
  --surface-raise: color-mix(in oklch, var(--seed-ink) 91%, white);
  --ctrl:          color-mix(in oklch, var(--seed-ink) 80%, white);
  --ctrl-hi:       color-mix(in oklch, var(--seed-ink) 78%, white);
  --hover:         color-mix(in oklch, var(--seed-ink) 91%, white);
  --grad-ground: linear-gradient(172deg, color-mix(in oklch, var(--seed-ink) 93%, white) 0%, var(--surface) 40%, var(--surface-deep) 100%);

  --text:    color-mix(in oklch, var(--seed-ink) 12%, white);
  --muted:   color-mix(in oklch, var(--seed-ink) 42%, white);
  --subtle:  color-mix(in oklch, var(--seed-ink) 58%, white);
  --disabled:color-mix(in oklch, var(--seed-ink) 72%, white);

  --line: rgba(255,255,255,.08);
  --line-strong: rgba(255,255,255,.15);
  --rim: rgba(255,255,255,.15);

  --accent-text: color-mix(in oklch, var(--seed-accent-hi) 45%, white);
  /* OUTLINES AND LINK TEXT ONLY. Half white, so it is bright enough to read as a
     ring on the dark ground. Never use it as a fill: it washes the brand out. */
  --accent-live: color-mix(in oklch, var(--seed-accent-hi) 50%, white);
  /* THE fill for anything that means "this is on". */
  --accent-lit:  color-mix(in oklch, var(--seed-accent-hi) 96%, white);
  /* A SECOND fill tone, for a chart segment or a track that has to sit beside the
     first. Derived from the accent's own dark end, so it stays in the family. */
  --accent-deep: color-mix(in oklab, var(--seed-accent-hi) 55%, var(--seed-accent-lo));
  /* ── THE CHART PALETTE ──
     The ONLY place in the system that carries more than one hue. A legend has to
     be matchable to a wedge, and three tints of one red are not.

     THESE ARE ANCHORED, NOT ROTATED, and the reason is measured. The first cut
     derived both secondaries by rotating the accent's own hue, which is elegant
     and wrong: rotating an arbitrary accent by a fixed angle eventually lands in
     the purple band, and across the eleven lab systems it did, twice. It also
     multiplied the accent's CHROMA, so a deliberately near-neutral system got
     three greys that no legend could be matched to.

     So the two secondaries sit at fixed teal and blue, with their own chroma,
     and only chart-1 follows the brand. --chart-2-h and --chart-3-h are the
     escape hatch: a system whose accent is ITSELF teal or blue re-points them
     rather than forking the component.

     --chart-1-h is the third hatch and only a near-neutral system needs it: when
     the accent has no meaningful hue of its own the browser hands back whatever
     residue is left in it, which drifted into purple on one system and swung 100
     degrees between the two modes on another. Pinning the HUE keeps lightness
     and chroma still adapting per mode, which a flat colour override throws
     away. */
  --chart-2-h: 174;
  --chart-3-h: 254;
  /* max() on the chroma is the floor that stops a neutral system going grey. */
  --chart-1: oklch(from var(--accent-lit) clamp(.44, l, .68) max(c, .085) var(--chart-1-h, h));
  --chart-2: oklch(.56 .115 var(--chart-2-h));
  --chart-3: oklch(.55 .125 var(--chart-3-h));
  --accent-tint: color-mix(in srgb, var(--seed-accent-hi) 14%, transparent);
  /* Text on an accent fill. Knox asked whether this white was the paper colour
     or one picked by eye: it was neither, five different times. It now comes
     from --seed-paper, so change the paper and every label on every accent fill
     follows. Nothing on an accent is a literal white again. */
  --on-accent:   color-mix(in srgb, var(--seed-paper) 58%, white);

  /* Status family. V4 had ONE status colour. All four derive from one seed by
     hue rotation, so it stays a single dial. */
  --danger:  var(--seed-status);
  --warning: oklch(from var(--seed-status) calc(l + .11) calc(c * .82) 78);
  --success: oklch(from var(--seed-status) calc(l + .08) calc(c * .60) 152);
  --info:    oklch(from var(--seed-status) calc(l + .06) calc(c * .55) 236);
  /* Readable on the dark ground: lift lightness, drop chroma so it does not glare. */
  --danger-text:  oklch(from var(--danger)  clamp(.72, calc(l + .16), .93) calc(c * .82) h);
  --warning-text: oklch(from var(--warning) clamp(.72, calc(l + .10), .93) calc(c * .82) h);
  --success-text: oklch(from var(--success) clamp(.72, calc(l + .10), .93) calc(c * .82) h);
  --info-text:    oklch(from var(--info)    clamp(.72, calc(l + .10), .93) calc(c * .82) h);
  --alert: var(--danger); --err: var(--danger);

  /* Elevation, six tiers. V4 had two shadows and no scale.
     Dark mode: the SURFACE lifts, the shadow only occludes. */
  --e0: none;
  --e1: inset 0 1px 0 0 rgba(255,255,255,calc(.05 * var(--elev))),
        0 0 0 1px rgba(255,255,255,calc(.05 * var(--elev)));
  --e2: inset 0 1px 0 0 rgba(255,255,255,calc(.06 * var(--elev))),
        0 0 0 1px rgba(255,255,255,calc(.06 * var(--elev))),
        0 2px 8px -4px rgba(0,0,0,calc(.50 * var(--elev)));
  --e3: inset 0 1px 0 0 rgba(255,255,255,calc(.07 * var(--elev))),
        0 0 0 1px rgba(255,255,255,calc(.08 * var(--elev))),
        0 8px 24px -12px rgba(0,0,0,calc(.60 * var(--elev))),
        0 16px 40px -20px rgba(0,0,0,calc(.50 * var(--elev)));
  --e4: inset 0 1px 0 0 rgba(255,255,255,calc(.08 * var(--elev))),
        0 0 0 1px rgba(255,255,255,calc(.09 * var(--elev))),
        0 16px 40px -16px rgba(0,0,0,calc(.70 * var(--elev))),
        0 40px 80px -32px rgba(0,0,0,calc(.60 * var(--elev)));
  --e5: var(--e4), 0 80px 140px -48px rgba(0,0,0,calc(.55 * var(--elev)));
  --e-sunk: inset 0 2px 6px rgba(0,0,0,calc(.55 * var(--elev))), inset 0 -1px 0 rgba(255,255,255,.05);
  --sh: var(--e3); --sh-sm: var(--e2); --well: var(--e-sunk);
  --extrude: 0 7px 16px -5px rgba(0,0,0,calc(.55 * var(--elev))), 0 1px 0 rgba(255,255,255,.08) inset;

  --edge-ring: linear-gradient(155deg, rgba(255,255,255,.20) 0%, rgba(255,255,255,.06) 30%, rgba(255,255,255,0) 52%, rgba(0,0,0,.16) 100%);
  --glass-bg:     color-mix(in srgb, var(--surface) 42%, transparent);
  --glass-border: rgba(255,255,255,.13);
  --glass-hi:     rgba(255,255,255,.17);
  --scrim: color-mix(in srgb, var(--seed-ink) 62%, transparent);
}

/* ── LAYER 1b · LIGHT DERIVATION ─────────────────────────────────────────── */
[data-theme="light"] {
  color-scheme: light;
  /* V4 hard-coded a second status hex here (#B64D14). That broke the moment the
     console wrote --seed-status inline, because an inline value on <html> beats
     a [data-theme] rule and the light correction silently stopped applying.
     Light mode now DERIVES its status from the same seed, so re-theming reaches
     both modes and there is only ever one status dial. */

  --bg:            var(--seed-paper);
  --surface:       color-mix(in oklch, var(--seed-paper) 42%, white);
  --surface-deep:  color-mix(in oklch, var(--seed-paper) 93%, black);
  --surface-raise: color-mix(in oklch, var(--seed-paper) 26%, white);
  --ctrl:          color-mix(in oklch, var(--seed-paper) 89%, black);
  --ctrl-hi:       color-mix(in oklch, var(--seed-paper) 85%, black);
  --hover:         color-mix(in oklch, var(--seed-paper) 88%, black);
  --grad-ground: linear-gradient(172deg, color-mix(in oklch, var(--seed-paper) 30%, white) 0%, var(--surface) 40%, color-mix(in oklch, var(--seed-paper) 97%, black) 100%);

  --text:    color-mix(in oklch, var(--seed-paper) 24%, black);
  --muted:   color-mix(in oklch, var(--seed-paper) 46%, black);
  --subtle:  color-mix(in oklch, var(--seed-paper) 56%, black);
  --disabled:color-mix(in oklch, var(--seed-paper) 72%, black);

  --line: rgba(25,40,48,.10);
  --line-strong: rgba(25,40,48,.18);
  --rim: rgba(255,255,255,.95);

  --accent-text: var(--seed-accent-hi);
  --accent-live: var(--seed-accent-hi);
  --accent-lit:  var(--seed-accent-hi);
  --accent-deep: color-mix(in oklab, var(--seed-accent-hi) 55%, var(--seed-accent-lo));
  --chart-1: oklch(from var(--accent-lit) clamp(.36, l, .58) max(c, .095) var(--chart-1-h, h));
  --chart-2: oklch(.46 .120 var(--chart-2-h));
  --chart-3: oklch(.43 .145 var(--chart-3-h));
  --accent-tint: color-mix(in srgb, var(--seed-accent-hi) 11%, transparent);
  --on-accent:   color-mix(in srgb, var(--seed-paper) 70%, white);

  --danger:  color-mix(in oklch, var(--seed-status) 84%, black);
  --warning: oklch(from var(--seed-status) calc(l - .16) calc(c * .92) 72);
  --success: oklch(from var(--seed-status) calc(l - .18) calc(c * .62) 150);
  --info:    oklch(from var(--seed-status) calc(l - .16) calc(c * .58) 240);
  /* Readable on paper: drive lightness DOWN hard. A gold that reads as a bar at
     l .62 is unreadable as 13px text; it has to come to roughly l .45. */
  --danger-text:  oklch(from var(--danger)  clamp(.30, calc(l - .16), .50) calc(c * .96) h);
  --warning-text: oklch(from var(--warning) clamp(.30, calc(l - .16), .50) calc(c * .96) h);
  --success-text: oklch(from var(--success) clamp(.30, calc(l - .14), .50) calc(c * .96) h);
  --info-text:    oklch(from var(--info)    clamp(.30, calc(l - .14), .50) calc(c * .96) h);
  --alert: var(--danger); --err: var(--danger);

  /* Light mode: the surface holds still, the shadow carries the whole signal. */
  --e0: none;
  --e1: 0 0 0 1px rgba(15,32,39,calc(.045 * var(--elev))),
        0 1px 1px -.5px rgba(15,32,39,calc(.06 * var(--elev))),
        0 2px 4px -1.5px rgba(15,32,39,calc(.06 * var(--elev)));
  --e2: 0 0 0 1px rgba(15,32,39,calc(.045 * var(--elev))),
        0 1px 1px -.5px rgba(15,32,39,calc(.06 * var(--elev))),
        0 3px 6px -2px rgba(15,32,39,calc(.07 * var(--elev))),
        0 8px 16px -6px rgba(15,32,39,calc(.06 * var(--elev)));
  --e3: 0 0 0 1px rgba(15,32,39,calc(.05 * var(--elev))),
        0 4px 8px -3px rgba(15,32,39,calc(.06 * var(--elev))),
        0 12px 24px -8px rgba(15,32,39,calc(.08 * var(--elev))),
        0 24px 40px -16px rgba(15,32,39,calc(.06 * var(--elev)));
  --e4: 0 0 0 1px rgba(15,32,39,calc(.05 * var(--elev))),
        0 8px 16px -6px rgba(15,32,39,calc(.08 * var(--elev))),
        0 24px 48px -16px rgba(15,32,39,calc(.12 * var(--elev))),
        0 48px 96px -32px rgba(15,32,39,calc(.10 * var(--elev)));
  --e5: var(--e4), 0 80px 160px -48px rgba(15,32,39,calc(.14 * var(--elev)));
  --e-sunk: inset 0 2px 5px rgba(15,32,39,calc(.16 * var(--elev))), inset 0 -1px 0 rgba(255,255,255,.75);
  --sh: var(--e3); --sh-sm: var(--e2); --well: var(--e-sunk);
  --extrude: 0 9px 20px -7px rgba(15,32,39,calc(.20 * var(--elev))), 0 1px 0 rgba(255,255,255,.95) inset;

  --edge-ring: linear-gradient(155deg, rgba(255,255,255,.95) 0%, rgba(255,255,255,.40) 32%, rgba(15,32,39,.05) 68%, rgba(15,32,39,.13) 100%);
  --glass-bg:     color-mix(in srgb, var(--surface) 62%, transparent);
  --glass-border: rgba(255,255,255,.70);
  --glass-hi:     rgba(255,255,255,.95);
  --scrim: color-mix(in srgb, var(--seed-ink) 44%, transparent);
}

/* ── BROWSER RESET · nothing default ships ──────────────────────────────── */
* { margin:0; padding:0; box-sizing:border-box; }
html { transition: background-color var(--d-base) var(--ease-hover); -webkit-text-size-adjust:100%; }
body { background:var(--bg); color:var(--text); font-family:var(--f); font-size:var(--fs-md);
  font-weight:450; line-height:var(--lh-body); min-height:100dvh; -webkit-font-smoothing:antialiased;
  transition: background-color var(--d-base) ease, color var(--d-base) ease; }
[data-theme="light"] body { font-weight:400; }
body::after { content:""; position:fixed; inset:0; z-index:2; pointer-events:none;
  background-image:var(--noise); background-size:140px; opacity:.03; mix-blend-mode:overlay; }
[data-theme="light"] body::after { opacity:.02; mix-blend-mode:multiply; }
img, svg, video { max-width:100%; display:block; }
button, input, select, textarea { font:inherit; color:inherit; background:none; border:none; }
button { cursor:pointer; }
a { color:var(--accent-text); }
a:hover { color:var(--accent-live); }
/* NOTHING is selectable by default. Only real text fields are, because only a
   field holds content the user typed and may legitimately want to copy. */
* { -webkit-tap-highlight-color:transparent; -webkit-user-select:none; user-select:none; }
input, textarea, [contenteditable="true"], .well-input, .t6-edit, .stepper input {
  -webkit-user-select:text; user-select:text; }
/* An outline follows the element's OWN corner, so anything that can take focus
   needs a radius even when it has no visible border. `border-radius:inherit`
   here was wrong twice over: it changes the ELEMENT, not the ring, and it
   inherits from the parent rather than matching the shape you can see. */
:focus-visible { outline:2px solid var(--accent-live); outline-offset:2px; }
/* A field inside a well does NOT draw its own ring. The well carries focus, and
   two indicators for one event is one too many. This is also why the ring looked
   square: the input has no corner, the well has all of it. */
.well :is(input, textarea, button, [contenteditable]):focus-visible,
.well:focus-within :is(input, textarea) { outline:none; }
/* The well's own focus is unmistakable without a ring: the rim takes the accent
   and the trough deepens. */
.well:focus-within { border-color:var(--accent-live);
  box-shadow:var(--e-sunk), inset 0 0 0 1px color-mix(in srgb, var(--seed-accent-hi) 45%, transparent); }
/* Anything focusable that renders square gets a corner for the ring to follow. */
.rowitem, .optrow, .frow, .check, .ckrow, .tree-row, .cmdk .crow,
.menu button, .drop-menu button, .combo-list button, .sp-menu button {
  border-radius:calc(var(--r-xs) * .8); }
.rowitem:focus-visible, .frow:focus-visible { outline-offset:-2px; }
::selection { background:color-mix(in srgb, var(--seed-accent-hi) 30%, transparent); }
* { scrollbar-width:thin; scrollbar-color:var(--line-strong) transparent; }
*::-webkit-scrollbar { width:9px; height:9px; }
*::-webkit-scrollbar-thumb { background:var(--line-strong); border-radius:99px; border:2px solid transparent; background-clip:content-box; }
*::-webkit-scrollbar-track { background:transparent; }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration:.01ms !important; animation-iteration-count:1 !important;
    transition-duration:.01ms !important; scroll-behavior:auto !important; }}
h1,h2,h3,h4 { max-width:100%; text-wrap:balance; }
h1 { font-family:var(--f-hero); font-size:var(--fs-display); font-weight:400; text-transform:uppercase;
  letter-spacing:var(--ls-display); line-height:var(--lh-display); margin-top:var(--sp-3); }
.mono { font-family:var(--fm); font-size:var(--fs-sm); color:var(--muted); }
/* Section label inside a card. Lived in the console block in the lab, so it came
   out with it; the reference sections still use it. */
.c-lab { display:block; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--subtle); margin-bottom:var(--sp-2); }
.stack { display:flex; flex-direction:column; gap:var(--sp-3); }
.row { display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; }
.grid2 { display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4); }
.grid3 { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-4); }
@media (max-width:760px){ .grid2,.grid3 { grid-template-columns:1fr; }}
/* A grid item defaults to min-width:auto, so its min-content can push the whole
   column past the viewport. This is what overflowed the page at 375px. */
.grid2 > *, .grid3 > *, .kpi > *, .kb > *, .media-grid > * { min-width:0; }
/* The genuinely wide pieces then scroll inside themselves rather than the page. */
.filing, .steps, .timep { max-width:100%; }
.steps { overflow-x:auto; }
.cal, .iconrow { max-width:100%; }
.frow { min-width:0; }
.fname, .fmeta { min-width:0; overflow-wrap:anywhere; }

/* ── SURFACES ───────────────────────────────────────────────────────────── */
.card { background:var(--grad-ground); border-radius:var(--r-lg);
  padding:var(--sp-5); box-shadow:var(--e2); }
.card, .t6-wrap, .kb-card, .modal, .wayfind, .pillar, .ramp > div { position:relative; isolation:isolate; }
.card::before, .t6-wrap::before, .kb-card::before, .modal::before, .wayfind::before,
.pillar::before, .ramp > div::before, .seedcard .scw::before {
  content:""; position:absolute; inset:0; border-radius:inherit; padding:1px; pointer-events:none; z-index:3;
  background:var(--edge-ring); -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite:exclude; }
.card::after { content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background-image:var(--noise); background-size:120px; opacity:.035; mix-blend-mode:overlay; }
[data-theme="light"] .card::after { opacity:.028; mix-blend-mode:multiply; }
.glass { position:relative; overflow:hidden; background:var(--glass-bg);
  -webkit-backdrop-filter:blur(30px) saturate(150%); backdrop-filter:blur(30px) saturate(150%);
  border:1px solid var(--glass-border); box-shadow:var(--e3), inset 0 1px 0 var(--glass-hi);
  border-radius:var(--r); }
.glass::before { content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(155deg, var(--glass-hi), transparent 38%); opacity:.45; mix-blend-mode:screen; }
.glass::after { content:""; position:absolute; inset:0; pointer-events:none;
  background-image:var(--noise); background-size:130px; opacity:.05; mix-blend-mode:overlay; }
/* .well is defined ONCE, with the controls. A second padded copy here was double-padding every input. */

/* ═══ SURFACE FINISH · v3 ═══ machined (V4 default) · liquid glass · soft ═══
   One attribute, three physics. Machined is the carved-hardware read the lab
   already has, so it adds no rules. The other two only re-light SURFACES:
   colour, type and spacing tokens stay untouched, so finishes compose with
   every palette. */
[data-finish="soft"] { --soft-hi:rgba(255,255,255,.045); --soft-lo:rgba(0,0,0,.44); }
[data-theme="light"][data-finish="soft"] { --soft-hi:rgba(255,255,255,.92); --soft-lo:rgba(140,152,160,.40); }
[data-finish="soft"] .card, [data-finish="soft"] .kb-card, [data-finish="soft"] .toast, [data-finish="soft"] .modal {
  border-color:transparent; box-shadow:-5px -5px 12px var(--soft-hi), 7px 9px 20px var(--soft-lo); }
[data-finish="soft"] .well, [data-finish="soft"] .stepper {
  border-color:transparent; box-shadow:inset 3px 3px 7px var(--soft-lo), inset -3px -3px 7px var(--soft-hi); }
[data-finish="soft"] .btn-key { border-color:transparent;
  box-shadow:-3px -3px 8px var(--soft-hi), 4px 5px 12px var(--soft-lo); }
[data-finish="soft"] .btn-key:active {
  box-shadow:inset 2px 2px 6px var(--soft-lo), inset -2px -2px 6px var(--soft-hi); }

[data-finish="glass"] .card, [data-finish="glass"] .kb-card, [data-finish="glass"] .toast,
[data-finish="glass"] .modal, [data-finish="glass"] .drop-menu, [data-finish="glass"] .combo-list {
  background:color-mix(in srgb, var(--surface-raise) 58%, transparent);
  -webkit-backdrop-filter:blur(20px) saturate(160%); backdrop-filter:blur(20px) saturate(160%);
  border-color:var(--glass-border);
  box-shadow:0 12px 30px rgba(0,0,0,.16), inset 0 1px 0 var(--glass-hi); }
[data-finish="glass"] .btn-key {
  background:color-mix(in srgb, var(--ctrl) 55%, transparent);
  -webkit-backdrop-filter:blur(12px); backdrop-filter:blur(12px);
  border-color:var(--glass-border);
  box-shadow:inset 0 1px 0 var(--glass-hi), 0 4px 12px rgba(0,0,0,.14); }

/* ═══ CONTROLS ═════════════════════════════════════════════════════════════
   Rebuilt 23-08-2026 on V4's physics after Knox called these boring. The first
   V5 pass turned every control into a soft pill, which is exactly the generic
   look V4 was built to avoid. What comes back: keys with a real seated edge,
   chunky padding, uppercase labels at the tracked micro size, and a press that
   SCALES into a carved well on a calc(70ms * var(--dur)) override rather than nudging down 1px.  */

/* Button character is a dial. Key is V4's machined rounded-rect. */
:root { --btn-r: 11px; --btn-pad: 13px 24px; --btn-fs: var(--fs-caps); --btn-ls: .07em; }

.btn-accent, .btn-key, .btn-ghost, .btn-danger {
  position:relative; display:inline-flex; align-items:center; justify-content:center; gap:9px;
  /* A label that wraps changes the height of the row it sits in. It never wraps. */
  white-space:nowrap;
  /* Every variant carries a 1px border, transparent where it is not drawn, so a
     ghost and a key measure the same. Without this the outlined one is 2px taller
     and a button row never quite lines up. */
  border:1px solid transparent;
  font-family:var(--f-ui); font-size:var(--btn-fs); font-weight:600; letter-spacing:var(--btn-ls);
  text-transform:uppercase; line-height:1; border-radius:var(--btn-r); padding:var(--btn-pad);
  transition:transform calc(90ms * var(--dur)) var(--ease-press), box-shadow calc(90ms * var(--dur)) ease,
             background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover),
             border-color var(--d-fast) var(--ease-hover); }

/* Primary. The Fill Law: oklab stops, DIAGONAL light, veil hover, seated edge. */
.btn-accent { overflow:hidden; isolation:isolate; color:var(--on-accent);
  background:linear-gradient(115deg in oklab, color-mix(in oklab, var(--seed-accent-hi) 88%, white) 0%,
    var(--seed-accent-hi) 55%, color-mix(in oklab, var(--seed-accent-hi) 82%, black) 100%);
  border:1px solid color-mix(in oklab, var(--seed-accent-lo) 70%, black);
  box-shadow:0 2px 5px rgba(0,0,0,.30), inset 0 1px 0 rgba(255,255,255,.18); }
[data-theme="light"] .btn-accent { box-shadow:0 2px 6px rgba(15,32,39,.22), inset 0 1px 0 rgba(255,255,255,.30); }
.btn-accent::before { content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:rgba(255,255,255,.10); opacity:0; transition:opacity calc(160ms * var(--dur)) ease; z-index:-1; }
.btn-accent:hover::before { opacity:1; }
.btn-accent:active { transform:scale(.98); transition-duration:calc(70ms * var(--dur)); box-shadow:var(--e-sunk); }

/* Secondary. The key: sits low, no border, presses into the well. */
.btn-key { color:var(--text); background:var(--surface);
  box-shadow:0 2px 4px rgba(0,0,0,.24); }
[data-theme="light"] .btn-key { box-shadow:0 2px 5px rgba(15,32,39,.12); }
.btn-key::before { content:""; position:absolute; inset:0; border-radius:inherit; padding:1px; pointer-events:none;
  background:var(--edge-ring); -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite:exclude; }
.btn-key:hover { background:var(--hover); }
.btn-key:active { transform:scale(.98); box-shadow:var(--e-sunk); transition-duration:calc(70ms * var(--dur)); }

/* Tertiary. A real outlined key, not a naked word. */
.btn-ghost { color:var(--muted); background:transparent; border-color:var(--line-strong); }
.btn-ghost:hover { color:var(--text); border-color:var(--ctrl-hi); background:rgba(255,255,255,.03); }
[data-theme="light"] .btn-ghost:hover { background:rgba(15,32,39,.04); }
.btn-ghost:active { transform:scale(.98); box-shadow:var(--e-sunk); transition-duration:calc(70ms * var(--dur)); }

.btn-accent[disabled], .btn-key[disabled], .btn-ghost[disabled], .btn-danger[disabled] { color:var(--disabled); pointer-events:none;
  background:var(--ctrl); box-shadow:none; border-color:var(--line); filter:saturate(.15); }
.b-sm { height:32px; padding:0 16px; border-radius:calc(var(--btn-r) * .8); }
.b-lg { height:52px; padding:0 32px; font-size:var(--fs-sm); border-radius:calc(var(--btn-r) * 1.25); }
.b-icon { width:42px; height:42px; padding:0; }
.b-icon svg { width:16px; height:16px; }

/* The X. One icon button, one size logic, everywhere a surface needs closing. */
.ib { display:grid; place-items:center; width:28px; height:28px; border-radius:calc(var(--r-xs) * .9);
  color:var(--subtle); flex-shrink:0; cursor:pointer;
  transition:color var(--d-fast) var(--ease-hover), background var(--d-fast) var(--ease-hover); }
.ib:hover { color:var(--text); background:var(--hover); }
.ib:active { box-shadow:var(--e-sunk); }
.ib svg { width:12px; height:12px; }

/* ── THE SPLIT BUTTON ──
   Two halves, two jobs. The FACE runs the currently chosen action. The CHEVRON
   opens the menu and only changes which action that is. The first build pressed
   as ONE object, which makes a split button a plain button wearing a chevron.
   Each half now presses on its own, and the shared face keeps them reading as
   a single key rather than two buttons shoved together. */
.split { position:relative; display:inline-flex; isolation:isolate; border-radius:var(--btn-r);
  border:1px solid color-mix(in oklab, var(--seed-accent-lo) 70%, black);
  box-shadow:0 2px 5px rgba(0,0,0,.30), inset 0 1px 0 rgba(255,255,255,.18);
  background:linear-gradient(115deg in oklab, color-mix(in oklab, var(--seed-accent-hi) 88%, white) 0%,
    var(--seed-accent-hi) 55%, color-mix(in oklab, var(--seed-accent-hi) 82%, black) 100%); }
[data-theme="light"] .split { box-shadow:0 2px 6px rgba(15,32,39,.22), inset 0 1px 0 rgba(255,255,255,.30); }
.split > .btn-accent, .split > .sp-more { position:relative; background:none; border:none;
  box-shadow:none; transform:none; overflow:hidden;
  transition:transform calc(90ms * var(--dur)) var(--ease-press), box-shadow calc(90ms * var(--dur)) ease; }
.split > .btn-accent { border-radius:calc(var(--btn-r) - 1px) 0 0 calc(var(--btn-r) - 1px); }
.split > .sp-more { border-radius:0 calc(var(--btn-r) - 1px) calc(var(--btn-r) - 1px) 0;
  display:grid; place-items:center; padding:0 14px; color:var(--on-accent);
  border-left:1px solid rgba(0,0,0,.30); box-shadow:inset 1px 0 0 rgba(255,255,255,.10); }
/* Each half takes its own hover veil and its own press. */
.split > .btn-accent::before, .split > .sp-more::before { content:""; position:absolute; inset:0;
  background:rgba(255,255,255,.10); opacity:0; pointer-events:none;
  transition:opacity var(--d-fast) var(--ease-hover); }
.split > .btn-accent:hover::before, .split > .sp-more:hover::before { opacity:1; }
.split > .btn-accent:active { transform:scale(.975); transition-duration:calc(70ms * var(--dur));
  box-shadow:inset 0 2px 5px rgba(0,0,0,.4); }
.split > .sp-more:active { transform:scale(.94); transition-duration:calc(70ms * var(--dur));
  box-shadow:inset 0 2px 5px rgba(0,0,0,.4), inset 1px 0 0 rgba(255,255,255,.10); }
.split > .sp-more svg { width:12px; height:12px;
  transition:transform var(--d-base) var(--ease-spring); }
.split.open > .sp-more svg { transform:rotate(180deg); }
.split .sp-menu { position:absolute; top:calc(100% + 8px); right:0; z-index:var(--z-drop);
  /* A menu is as wide as its trigger, and grows only if its OWN content needs
     more. The 224px floor here was arbitrary: it had nothing to do with the
     button it hangs off or the items inside it, so a 169px Export key opened a
     221px menu for no reason. min-width:100% is the floor that actually means
     something; max-content is the ceiling. */
  min-width:100%; width:max-content; max-width:min(340px, 90vw);
  opacity:0; transform:translateY(-6px) scale(.985);
  pointer-events:none; transform-origin:top right;
  transition:opacity var(--d-fast) var(--ease-hover), transform var(--d-base) var(--ease-spring); }
.split.open .sp-menu { opacity:1; transform:none; pointer-events:auto; }
/* THE TINT CARRIES THE STATE, THE TYPE STAYS FULL STRENGTH. Accent type on an
   accent-tinted ground is the same washed-out pairing that made the model keys
   read light red, and it also leans on colour alone to say "current". A tinted
   ground plus --text says it without either problem, which is what the tabs
   already do. */
.split .sp-menu button.on { color:var(--text); background:var(--accent-tint); }

/* ── THE WELL ──
   V4's well is a CONTAINER, not an input. Anything can sit in it, and the
   trough visibly DEEPENS on focus rather than gaining an outline ring. */
.well { position:relative; display:flex; align-items:stretch; background:var(--surface-deep); border:1px solid var(--line);
  border-radius:var(--r-xs); box-shadow:var(--e-sunk);
  transition:border-color var(--d-fast) var(--ease-hover), box-shadow var(--d-fast) var(--ease-hover); }
.well:focus-within { border-color:color-mix(in srgb, var(--seed-accent-hi) 50%, transparent);
  box-shadow:var(--e-sunk), inset 0 0 12px rgba(0,0,0,.10); }
.well-input { flex:1; min-width:0; width:100%; font-family:var(--f-body); font-size:var(--fs-md);
  line-height:1.2; color:var(--text); background:transparent; border:none; outline:none; padding:12px 15px; }
.well-input::placeholder { color:var(--subtle); }
textarea.well-input { min-height:84px; resize:none; line-height:1.5; display:block; }
.well.err { border-color:var(--danger); }
.well.err:focus-within { border-color:var(--danger); }

.field { display:flex; flex-direction:column; gap:7px; }
.f-label { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--muted); }
.f-label em { font-style:normal; color:var(--accent-text); }
.f-help { font-size:var(--fs-sm); color:var(--subtle); }
.f-err { font-family:var(--f-ui); font-size:var(--fs-sm); font-weight:600; color:var(--danger-text); }
/* The textarea's native grip is browser chrome and never ships. */
.grip { position:absolute; right:7px; bottom:7px; width:14px; height:14px; cursor:ns-resize;
  touch-action:none; color:var(--subtle); display:grid; place-items:center; border-radius:4px;
  transition:color var(--d-fast) var(--ease-hover), background var(--d-fast) var(--ease-hover); }
.grip:hover { color:var(--accent-text); background:var(--accent-tint); }

/* Number stepper, seated in a well. */
.stepper { display:inline-flex; align-items:stretch; overflow:hidden; border-radius:inherit; }
/* Sized for three digits and no more. The cap is 100, and anything above that
   is a quantity nobody types by accident, so it goes through approval rather
   than through a wider box. A field built for four digits invites a fourth. */
.stepper input { width:46px; text-align:center; font-family:var(--f-num); font-weight:var(--num-wt);
  font-size:var(--fs-md); line-height:1.2; font-variant-numeric:tabular-nums; padding:11px 0; -moz-appearance:textfield;
  background:transparent; outline:none; color:var(--text); }
.stepper input::-webkit-outer-spin-button, .stepper input::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
.stepper:has(input:focus-visible) { outline:2px solid var(--accent-live); outline-offset:2px; }
.stepper input:focus-visible { outline:none; }
/* The keys inside a stepper are glyph keys, not buttons in the row's sense, so
   they carry their own square press and never a ring on click. */
.stepper button:focus-visible { outline:none; }
.stepper button:focus-visible::after { content:""; position:absolute; inset:2px;
  border-radius:calc(var(--r-xs) * .6); outline:2px solid var(--accent-live); }
.stepper button { position:relative; }
.stepper button { width:38px; color:var(--muted); display:grid; place-items:center;
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
/* The accent means "this is live under your hand". A grey hover on a plus and
   minus reads as disabled, which is the opposite of what a hover is for. */
.stepper button:hover { background:var(--accent-tint); color:var(--accent-text); }
.stepper button:active { box-shadow:var(--e-sunk); }
.stepper button svg { width:12px; height:12px; }
.stepper button:first-of-type { border-right:1px solid var(--line); }
.stepper button:last-of-type { border-left:1px solid var(--line); }

/* ── DROPDOWN ──
   The trigger has NO chrome of its own; it sits inside a well like any other
   input. The menu is glass over the page, opening from the top on the spring. */
.drop { position:relative; width:100%; }
.drop-trig { display:flex; width:100%; align-items:center; justify-content:space-between; gap:10px;
  padding:12px 15px; font-family:var(--f-body); font-size:var(--fs-md); line-height:1.2;
  color:var(--text); background:none; }
.drop-trig svg { width:11px; height:7px; color:var(--subtle); flex-shrink:0;
  transition:transform calc(240ms * var(--dur)) var(--ease-spring); }
.drop.open .drop-trig svg { transform:rotate(180deg); }
.drop-menu { position:absolute; top:calc(100% + 8px); left:0; right:0; z-index:var(--z-drop); padding:6px;
  border-radius:var(--r-xs); background:color-mix(in srgb, var(--surface-raise) 94%, transparent);
  -webkit-backdrop-filter:blur(22px) saturate(160%); backdrop-filter:blur(22px) saturate(160%);
  border:1px solid var(--glass-border); box-shadow:var(--e3), inset 0 1px 0 var(--glass-hi);
  opacity:0; transform:translateY(-6px) scale(.985); pointer-events:none; transform-origin:top center;
  transition:opacity calc(160ms * var(--dur)) ease, transform calc(240ms * var(--dur)) var(--ease-spring); }
.drop.open .drop-menu { opacity:1; transform:none; pointer-events:auto; }
.drop-menu button { display:block; width:100%; text-align:left; font-family:var(--f-body);
  font-size:var(--fs-sm); color:var(--muted); padding:10px 12px; border-radius:calc(var(--r-xs) * .8);
  transition:background calc(130ms * var(--dur)) ease, color calc(130ms * var(--dur)) ease; }
.drop-menu button:hover { background:color-mix(in srgb, var(--seed-accent-hi) 22%, transparent); color:var(--text); }
.drop-menu button.on { color:var(--text); background:color-mix(in srgb, var(--seed-accent-hi) 16%, transparent); }

/* Combobox, same glass list. */
.combo { position:relative; width:270px; }
.combo-list { position:absolute; top:calc(100% + 8px); left:0; right:0; z-index:var(--z-drop); padding:6px;
  border-radius:var(--r-xs); background:color-mix(in srgb, var(--surface-raise) 94%, transparent);
  -webkit-backdrop-filter:blur(22px) saturate(160%); backdrop-filter:blur(22px) saturate(160%);
  border:1px solid var(--glass-border); box-shadow:var(--e3), inset 0 1px 0 var(--glass-hi);
  max-height:180px; overflow-y:auto; display:none; }
.combo.open .combo-list { display:block; }
.combo-list button { display:block; width:100%; text-align:left; font-family:var(--f-body);
  font-size:var(--fs-sm); color:var(--muted); padding:10px 12px; border-radius:calc(var(--r-xs) * .8); }
.combo-list button:hover, .combo-list button.hi { background:color-mix(in srgb, var(--seed-accent-hi) 22%, transparent); color:var(--text); }
.combo-list mark { background:none; color:var(--accent-text); font-weight:600; }
.combo-list .none { font-size:var(--fs-sm); color:var(--subtle); padding:12px; }

/* ── SEGMENTED ──
   The pill is a gradient, the slide is sprung, and the label padding is
   asymmetric on purpose: letter-spacing adds its space after the LAST letter
   too, so symmetric padding leaves the word half a space left of its own pill. */
.seg { position:relative; display:inline-flex; flex-shrink:0; padding:4px; border-radius:var(--r-pill);
  background:var(--surface-deep); border:1px solid var(--line); box-shadow:var(--e-sunk); isolation:isolate; }
.seg-b { position:relative; z-index:1; font-family:var(--f-ctrl); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.11em; text-transform:uppercase; line-height:1; color:var(--muted);
  padding:11px calc(22px - .055em) 11px calc(22px + .055em); border-radius:var(--r-pill);
  display:inline-flex; align-items:center; justify-content:center;
  transition:color calc(240ms * var(--dur)) var(--ease-hover); }
.seg-b.on { color:var(--on-accent); }
.seg-b:not(.on):hover { color:var(--text); }
.seg-pill { position:absolute; z-index:0; top:4px; bottom:4px; border-radius:var(--r-pill);
  background:linear-gradient(180deg in oklab, color-mix(in oklab, var(--seed-accent-hi) 88%, white) 0%, var(--seed-accent-hi) 100%);
  box-shadow:var(--e2);
  transition:left calc(320ms * var(--dur)) var(--ease-spring), width calc(320ms * var(--dur)) var(--ease-spring); }
[data-theme="light"] .seg-b.on { color:var(--on-accent); }

/* ── TOGGLE ── recessed track, domed thumb, groove kept when lit */
.tgl { width:46px; height:26px; border-radius:var(--r-pill); background:var(--surface-deep); position:relative;
  border:1px solid var(--line); box-shadow:var(--e-sunk); flex-shrink:0; cursor:pointer;
  transition:background var(--d-base) var(--ease-hover), border-color var(--d-base) var(--ease-hover); }
.tgl.on { background:var(--accent-lit); border-color:color-mix(in srgb, var(--seed-accent-hi) 60%, transparent);
  box-shadow:var(--e-sunk); }
.tgl::after { content:""; position:absolute; top:2px; left:2px; width:20px; height:20px; border-radius:50%;
  background:radial-gradient(120% 100% at 50% -10%, var(--ctrl-hi) 0%, var(--ctrl) 100%);
  box-shadow:0 2px 5px rgba(0,0,0,.45), 0 1px 0 rgba(255,255,255,.14) inset;
  transition:transform var(--d-base) var(--ease-spring), background var(--d-base) var(--ease-hover); }
[data-theme="light"] .tgl::after { background:radial-gradient(120% 100% at 50% -10%, #fff 0%, var(--surface-raise) 100%);
  box-shadow:0 2px 6px rgba(15,32,39,.28), 0 1px 0 rgba(255,255,255,1) inset; }
.tgl.on::after { transform:translateX(20px); }

/* ── CHECK AND RADIO ── carved when off, POPS OUT of the well when on */
.ck, .rd { width:20px; height:20px; flex-shrink:0; display:grid; place-items:center; cursor:pointer;
  background:var(--surface-deep); border:1px solid var(--line); box-shadow:var(--e-sunk); color:transparent;
  transition:background calc(200ms * var(--dur)) var(--ease-hover), border-color calc(200ms * var(--dur)) var(--ease-hover), box-shadow calc(200ms * var(--dur)) var(--ease-hover); }
.ck { border-radius:calc(var(--r-xs) * .85); } .rd { border-radius:50%; }
.ck.on { background:var(--accent-lit); border-color:rgba(0,0,0,.18); box-shadow:var(--e2); color:var(--on-accent); }
.ck svg { width:11px; height:11px; opacity:0; transform:scale(.4);
  transition:opacity calc(160ms * var(--dur)) ease, transform calc(240ms * var(--dur)) var(--ease-spring); }
.ck.on svg { opacity:1; transform:scale(1); }
.rd::after { content:""; width:9px; height:9px; border-radius:50%; background:var(--accent-lit);
  opacity:0; transform:scale(.4); transition:opacity calc(160ms * var(--dur)) ease, transform calc(240ms * var(--dur)) var(--ease-spring); }
.rd.on { border-color:color-mix(in srgb, var(--seed-accent-hi) 60%, transparent); }
.rd.on::after { opacity:1; transform:scale(1); }
.ckrow { display:flex; align-items:center; gap:11px; padding:7px 0; cursor:pointer;
  font-size:var(--fs-md); color:var(--muted); transition:color calc(130ms * var(--dur)) var(--ease-hover); }
.ckrow:hover { color:var(--text); }
.check.done .lbl { text-decoration:line-through; text-decoration-color:var(--accent-text);
  text-decoration-thickness:1.5px; color:var(--subtle); }

/* ── SLIDER ── carved track, SOLID LIT FILL, domed thumb */
.sld { position:relative; height:28px; display:flex; align-items:center; }
.sld .track { position:absolute; left:0; right:0; height:8px; border-radius:99px;
  background:var(--surface-deep); border:1px solid var(--line); box-shadow:var(--e-sunk); }
.sld .fill { position:absolute; left:1px; height:8px; border-radius:99px; background:var(--accent-lit); }
.sld input { position:relative; z-index:2; width:100%; -webkit-appearance:none; appearance:none;
  background:transparent; height:28px; cursor:pointer; }
.sld input::-webkit-slider-thumb { -webkit-appearance:none; width:20px; height:20px; border-radius:50%;
  background:radial-gradient(120% 100% at 50% -10%, var(--ctrl-hi) 0%, var(--ctrl) 100%); border:none;
  box-shadow:0 2px 5px rgba(0,0,0,.45), 0 1px 0 rgba(255,255,255,.14) inset; }
.sld input::-moz-range-thumb { width:20px; height:20px; border:none; border-radius:50%;
  background:var(--ctrl-hi); box-shadow:0 2px 5px rgba(0,0,0,.45); }
.sld input::-moz-range-track { background:transparent; }
[data-theme="light"] .sld input::-webkit-slider-thumb {
  background:radial-gradient(120% 100% at 50% -10%, #fff 0%, var(--surface-raise) 100%);
  box-shadow:0 2px 6px rgba(15,32,39,.28), 0 1px 0 rgba(255,255,255,1) inset; }

/* ── CHIP AND TAG ── */
.chip { display:inline-flex; align-items:center; gap:8px; font-family:var(--f-ctrl); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:.07em; text-transform:uppercase; line-height:1;
  color:var(--muted); background:var(--ctrl); border:1px solid var(--line-strong);
  border-radius:var(--r-pill); padding:8px 15px; cursor:pointer;
  box-shadow:0 1px 3px rgba(0,0,0,.2), inset 0 1px 0 rgba(255,255,255,.04);
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover),
             transform calc(90ms * var(--dur)) var(--ease-press), box-shadow calc(90ms * var(--dur)) ease; }
[data-theme="light"] .chip { box-shadow:0 1px 3px rgba(15,32,39,.08), inset 0 1px 0 rgba(255,255,255,.85); }
.chip:hover { background:var(--ctrl-hi); color:var(--text); }
.chip:active { transform:scale(.97); box-shadow:var(--e-sunk); transition-duration:calc(70ms * var(--dur)); }
.chip.on { background:var(--accent-lit); color:var(--on-accent); border-color:rgba(0,0,0,.2); }
[data-theme="light"] .chip.on { color:var(--on-accent); }
.taginput { display:flex; flex-wrap:wrap; align-items:center; gap:7px; padding:9px 12px; }
.taginput input { flex:1; min-width:90px; background:none; border:none; outline:none;
  font-family:var(--f-body); font-size:var(--fs-sm); color:var(--text); }
/* Built like a key rather than a tint. A dark tint carrying pale accent text is
   two weak reds arguing; the fill plus the paper label is one clear object. */
.tag { display:inline-flex; align-items:center; gap:6px; font-family:var(--f-ctrl); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:.07em; text-transform:uppercase; line-height:1;
  color:var(--on-accent);
  background:linear-gradient(115deg in oklab, color-mix(in oklab, var(--seed-accent-hi) 88%, white) 0%,
    var(--seed-accent-hi) 60%, color-mix(in oklab, var(--seed-accent-lo) 55%, var(--seed-accent-hi)) 100%);
  border:1px solid color-mix(in oklab, var(--seed-accent-lo) 70%, black);
  box-shadow:0 1px 3px rgba(0,0,0,.26), inset 0 1px 0 rgba(255,255,255,.16);
  border-radius:var(--r-pill); padding:5px 8px 5px 12px; }
.tag button { color:color-mix(in srgb, var(--on-accent) 70%, transparent); width:16px; height:16px; display:grid; place-items:center; border-radius:50%;
  transition:color calc(130ms * var(--dur)) ease, background calc(130ms * var(--dur)) ease; }
.tag button:hover { color:var(--on-accent); background:rgba(0,0,0,.24); }
.tag button svg { width:8px; height:8px; }

/* The keycap. The thicker bottom edge is what makes it read as a key. */
.kbd { display:inline-flex; align-items:center; font-family:var(--f-ctrl); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:.07em; text-transform:uppercase; color:var(--subtle); background:var(--surface-deep);
  border:1px solid var(--line-strong); border-bottom-width:2px; border-radius:6px; padding:3px 7px; line-height:1; }
/* Inside a search well it is plain text, never a boxed key. */
.tsearch .kbd { background:none; border:none; padding:0; }

/* Status. Typography only: a standalone dot and a caps label. No container. */
.status { display:inline-flex; align-items:center; gap:7px; font-family:var(--f-ui); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:var(--ls-caps); text-transform:uppercase; white-space:nowrap; }
.status i { width:6px; height:6px; border-radius:50%; background:currentColor; flex-shrink:0; }
.status.done { color:var(--subtle); }
.status.live { color:var(--text); }
.status.wait { color:var(--muted); } .status.wait i { background:none; box-shadow:inset 0 0 0 1.5px currentColor; }
.status.attn { color:var(--danger-text); }

/* Level bars */
.levels { display:inline-flex; gap:3px; align-items:flex-end; }
.levels i { width:13px; height:38px; border-radius:3px; background:var(--surface-deep);
  border:1px solid var(--line); box-shadow:var(--e-sunk); }
.levels i.lit { background:var(--accent-lit); border-color:transparent; box-shadow:none; }

/* The pillow: a big extruded squircle key for primary tile actions. */
.pillow { width:86px; height:86px; border-radius:calc(var(--r) * 1.3); display:grid; place-items:center;
  color:var(--text); cursor:pointer; position:relative; isolation:isolate;
  background:linear-gradient(150deg in oklab, var(--ctrl-hi) 0%, var(--ctrl) 55%, var(--surface) 100%);
  box-shadow:var(--extrude);
  transition:transform calc(320ms * var(--dur)) var(--ease-hover), box-shadow calc(320ms * var(--dur)) var(--ease-hover); }
.pillow::before { content:""; position:absolute; inset:0; border-radius:inherit; padding:1px; pointer-events:none;
  background:var(--edge-ring); -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor; mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite:exclude; }
.pillow:hover { transform:translateY(-2px); }
.pillow:active { transform:scale(.98); box-shadow:var(--e-sunk); transition-duration:calc(70ms * var(--dur)); }
.pillow svg { width:26px; height:26px; }

/* ═══ WAYFINDING ═══════════════════════════════════════════════════════════
   Four surfaces, each with variants driven off a data-attribute on <html>.
   Everything still rides the seeds: change the accent and all of it re-dresses. */
.wayfind { display:grid; grid-template-columns:218px 1fr;
  border-radius:var(--r-lg); overflow:hidden; background:var(--grad-ground); box-shadow:var(--e3);
  transition:grid-template-columns var(--d-slow) var(--ease-entrance); }
.wayfind .grain { position:absolute; inset:0; pointer-events:none; z-index:0;
  background-image:var(--noise); background-size:120px; opacity:.04; mix-blend-mode:overlay; }
[data-theme="light"] .wayfind .grain { opacity:.025; mix-blend-mode:multiply; }

/* ── RAIL ── */
.rail { position:relative; z-index:1; padding:var(--sp-4) var(--sp-2);
  display:flex; flex-direction:column; gap:var(--sp-1);
  background:rgba(0,0,0,.16); border-right:1px solid var(--line);
  transition:background var(--d-base) var(--ease-hover), margin var(--d-slow) var(--ease-entrance),
             border-radius var(--d-slow) var(--ease-entrance), box-shadow var(--d-slow) var(--ease-entrance); }
.rail-logo { font-family:var(--f-hero); font-size:var(--fs-sm); text-transform:uppercase;
  letter-spacing:var(--ls-head); color:var(--text); padding:6px var(--sp-3) var(--sp-4); white-space:nowrap; overflow:hidden; }
/* The lit accent, not the text tint. --accent-text is 45% white, which at
   display size is a pale pink; that is what made "Vision" look wrong. */
.rail-logo span { color:var(--accent-lit); }
.rail-group { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--subtle); padding:var(--sp-4) var(--sp-3) var(--sp-2); white-space:nowrap; }
.rail a { position:relative; z-index:1; display:flex; align-items:center; gap:var(--sp-3);
  font-family:var(--f-body); font-size:var(--fs-sm); color:var(--muted); padding:10px var(--sp-3);
  border-radius:var(--r-xs); text-decoration:none; white-space:nowrap; overflow:hidden;
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
.rail a:hover { background:var(--hover); color:var(--text); }
.rail a.on { color:var(--on-accent); }
[data-theme="light"] .rail a.on { color:var(--on-accent); }
.rail a.on:hover { background:transparent; }
.rail a svg { width:15px; height:15px; flex-shrink:0; }
.rail a .lbl { transition:opacity var(--d-base) var(--ease-hover); }
.rail a .ct { margin-left:auto; font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--subtle); font-variant-numeric:tabular-nums; }
.rail a.on .ct { color:color-mix(in srgb, var(--on-accent) 85%, transparent); }
.rail-pill { position:absolute; left:var(--sp-2); right:var(--sp-2); top:0; height:0; opacity:0; z-index:0;
  border-radius:var(--r-xs); background:var(--accent-lit); box-shadow:var(--e2);
  transition:top var(--d-slow) var(--ease-entrance), height var(--d-slow) var(--ease-entrance),
             opacity var(--d-fast) ease, left var(--d-slow) var(--ease-entrance), right var(--d-slow) var(--ease-entrance); }

/* Variant A · CARVED (V4's current) */
[data-rail="carved"] .rail { background:rgba(0,0,0,.16); border-right:1px solid var(--line); }
[data-theme="light"] [data-rail="carved"] .rail { background:rgba(15,32,39,.045); }

/* Variant B · FLOATING. The rail detaches and becomes an object on the ground. */
[data-rail="floating"] .wayfind { grid-template-columns:214px 1fr; }
[data-rail="floating"] .rail { margin:var(--sp-3) 0 var(--sp-3) var(--sp-3); border-radius:var(--r);
  background:var(--glass-bg); -webkit-backdrop-filter:blur(22px) saturate(150%);
  backdrop-filter:blur(22px) saturate(150%); border:1px solid var(--glass-border);
  box-shadow:var(--e3), inset 0 1px 0 var(--glass-hi); }

/* Variant C · ICONS. Collapses to 62px; labels fade, pill goes square. */
[data-rail="icons"] .wayfind { grid-template-columns:62px 1fr; }
[data-rail="icons"] .rail { background:rgba(0,0,0,.16); border-right:1px solid var(--line); align-items:center; }
[data-theme="light"] [data-rail="icons"] .rail { background:rgba(15,32,39,.045); }
[data-rail="icons"] .rail a { justify-content:center; padding:var(--sp-3) 0; width:42px; }
[data-rail="icons"] .rail a .lbl, [data-rail="icons"] .rail a .ct, [data-rail="icons"] .rail-group { opacity:0;
  width:0; overflow:hidden; padding:0; height:0; }
[data-rail="icons"] .rail-group { margin:var(--sp-2) 0; height:1px; width:26px; background:var(--line-strong); }
/* KNOX in an expanded face does not fit 62px. The collapsed rail shows the K. */
[data-rail="icons"] .rail-logo { padding:var(--sp-2) 0 var(--sp-4);
  text-align:center; width:100%; font-size:0; }
[data-rail="icons"] .rail-logo .full { display:none; }
[data-rail="icons"] .rail-logo::before { content:"K"; font-size:var(--fs-xl); color:var(--accent-text); }
[data-rail="icons"] .rail-pill { left:10px; right:10px; }

/* Variant D · GROUPED. Sections become carved wells with their own headers. */
[data-rail="grouped"] .rail { background:rgba(0,0,0,.16); border-right:1px solid var(--line); gap:var(--sp-1); }
[data-theme="light"] [data-rail="grouped"] .rail { background:rgba(15,32,39,.045); }
[data-rail="grouped"] .rail-group { color:var(--accent-text); border-top:1px solid var(--line);
  margin-top:var(--sp-2); padding-top:var(--sp-3); }
[data-rail="grouped"] .rail-group:first-of-type { border-top:none; margin-top:0; }
[data-rail="grouped"] .rail a { font-size:var(--fs-sm); padding:7px var(--sp-3); }

/* ── TOP BAR ── */
.topb { position:relative; z-index:var(--z-chrome); display:flex; align-items:center; gap:var(--sp-3);
  margin:var(--sp-3) var(--sp-4) 0; padding:var(--sp-2) var(--sp-4); border-radius:var(--r-pill);
  background:var(--glass-bg); -webkit-backdrop-filter:blur(22px) saturate(150%);
  backdrop-filter:blur(22px) saturate(150%); border:1px solid var(--glass-border);
  box-shadow:var(--e2), inset 0 1px 0 var(--glass-hi);
  transition:margin var(--d-slow) var(--ease-entrance), border-radius var(--d-slow) var(--ease-entrance),
             background var(--d-base) var(--ease-hover), box-shadow var(--d-base) var(--ease-hover); }
/* The breadcrumb was 11px uppercase with .1em tracking applied to a PROJECT
   NAME. Caps mangle a proper noun and tracked caps at 11px are unreadable at a
   glance, which is what Knox saw in "BEAUTYWORLD HALL FILM". The trail stays a
   caps label because it is a category. The current page is a name, so it is set
   in sentence case at the small step and carries the weight instead. */
/* One voice for the whole trail (Knox, 23-08). The crumb takes ALL the slack,
   so a project called "ABC" and one called "Beautyworld hall film" leave the
   search well and the avatar in exactly the same place. */
.crumb { display:flex; align-items:center; gap:var(--sp-2); white-space:nowrap;
  overflow:hidden; min-width:0; flex:1 1 0;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; }
.crumb .trail { color:var(--subtle); flex-shrink:0; }
.crumb b { font:inherit; letter-spacing:inherit; color:var(--text);
  overflow:hidden; text-overflow:ellipsis; min-width:0; }
.crumb i { font-style:normal; color:var(--subtle); opacity:.55; flex-shrink:0; }
.topb .sp { display:none; }
.tsearch { display:flex; align-items:center; gap:var(--sp-2); background:var(--surface-deep);
  border-radius:var(--r-pill); box-shadow:var(--e-sunk); padding:0 var(--sp-3);
  flex:0 0 210px; width:210px; min-width:0; }
.tsearch input { font-size:var(--fs-sm); font-family:var(--f-body); color:var(--text); padding:var(--sp-2) 0; min-width:0; width:100%; }
.tsearch input::placeholder { color:var(--disabled); }
.tsearch svg { width:13px; height:13px; color:var(--subtle); flex-shrink:0; }
.tsearch .kbd { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:500; letter-spacing:.04em;
  color:var(--subtle); white-space:nowrap; }
.ava { width:30px; height:30px; border-radius:50%; flex-shrink:0; display:grid; place-items:center;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.03em; text-indent:.03em; color:var(--on-accent); cursor:pointer;
  background:linear-gradient(150deg in oklab, color-mix(in oklab, var(--seed-accent-hi) 88%, white) 0%,
    var(--seed-accent-hi) 60%, color-mix(in oklab, var(--seed-accent-hi) 82%, black) 100%); box-shadow:var(--e2); }

/* Variant A · PILL (V4's current). Floating glass, detached from every edge. */
[data-topbar="pill"] .topb { margin:var(--sp-3) var(--sp-4) 0; padding:var(--sp-2) var(--sp-4);
  border-radius:var(--r-pill); background:var(--glass-bg);
  -webkit-backdrop-filter:blur(22px) saturate(150%); backdrop-filter:blur(22px) saturate(150%);
  border:1px solid var(--glass-border); box-shadow:var(--e2), inset 0 1px 0 var(--glass-hi); }

/* Variant B · FULL. Edge to edge glass, hairline under it, crumb gets room. */
[data-topbar="full"] .topb { margin:0; padding:var(--sp-3) var(--sp-5); border-radius:0;
  background:var(--glass-bg); -webkit-backdrop-filter:blur(22px) saturate(150%);
  backdrop-filter:blur(22px) saturate(150%); border-bottom:1px solid var(--line); box-shadow:none; }
[data-topbar="full"] .tsearch { flex:0 0 240px; width:240px; }

/* Variant C · SPLIT. Crumb sits on the ground, the command cluster floats alone. */
[data-topbar="split"] .topb { margin:var(--sp-4) var(--sp-4) 0; padding:0; background:none;
  border:none; box-shadow:none; backdrop-filter:none; -webkit-backdrop-filter:none; }
[data-topbar="split"] .topb .cluster { display:flex; align-items:center; gap:var(--sp-2);
  padding:var(--sp-1) var(--sp-1) var(--sp-1) var(--sp-3); border-radius:var(--r-pill);
  background:var(--glass-bg); -webkit-backdrop-filter:blur(22px) saturate(150%);
  backdrop-filter:blur(22px) saturate(150%); border:1px solid var(--glass-border);
  box-shadow:var(--e2), inset 0 1px 0 var(--glass-hi); min-width:0; }
[data-topbar="pill"] .topb .cluster, [data-topbar="full"] .topb .cluster {
  display:flex; align-items:center; gap:var(--sp-3); flex:0 0 auto; }

/* ── TABS ── */
.canvas { position:relative; z-index:1; padding:var(--sp-4) var(--sp-5) var(--sp-5); }
.vtabs { position:relative; display:flex; gap:2px;
  transition:background var(--d-base) var(--ease-hover), padding var(--d-base) var(--ease-hover),
             border-radius var(--d-base) var(--ease-hover), box-shadow var(--d-base) var(--ease-hover); }
.vtabs button { position:relative; z-index:1; font-family:var(--f-ctrl); font-size:var(--fs-sm); font-weight:500;
  color:var(--subtle); padding:var(--sp-3) var(--sp-4); line-height:1;
  transition:color var(--d-base) var(--ease-hover); }
.vtabs button:hover { color:var(--muted); }
.vtabs button.on { color:var(--text); }
.vtabs .ind { position:absolute; z-index:0; bottom:-1px; height:2px; border-radius:2px; background:var(--accent-lit);
  transition:left var(--d-base) var(--ease-spring), width var(--d-base) var(--ease-spring),
             top var(--d-base) var(--ease-spring), height var(--d-base) var(--ease-spring); }

/* Variant A · UNDERLINE (V4's current) */
[data-tabs="underline"] .vtabs { border-bottom:1px solid var(--line); }
[data-tabs="underline"] .vtabs .ind { bottom:-1px; top:auto; height:2px; border-radius:2px; background:var(--accent-lit); }

/* Variant B · PILL. Indicator becomes a filled pill behind the label. */
[data-tabs="pill"] .vtabs { gap:var(--sp-1); }
[data-tabs="pill"] .vtabs button { padding:var(--sp-2) var(--sp-4); border-radius:var(--r-pill); }
[data-tabs="pill"] .vtabs button.on { color:var(--on-accent); }
[data-theme="light"] [data-tabs="pill"] .vtabs button.on { color:var(--on-accent); }
[data-tabs="pill"] .vtabs .ind { top:0; bottom:0; height:auto; border-radius:var(--r-pill);
  background:var(--accent-lit); box-shadow:var(--e2); }

/* Variant C · CARVED. The whole strip is a well; the indicator is the raised key. */
[data-tabs="carved"] .vtabs { background:var(--surface-deep); border-radius:var(--r-pill);
  padding:3px; box-shadow:var(--e-sunk); align-self:flex-start; }
[data-tabs="carved"] .vtabs button { padding:var(--sp-2) var(--sp-4); border-radius:var(--r-pill); }
[data-tabs="carved"] .vtabs button.on { color:var(--text); }
[data-tabs="carved"] .vtabs .ind { top:3px; bottom:3px; height:auto; border-radius:var(--r-pill);
  background:var(--ctrl-hi); box-shadow:var(--extrude); }

/* minmax(0,1fr) IS LOAD-BEARING. Every pane sits in the same grid cell, so the
   WIDEST pane sizes the cell, including the ones that are not showing. A plain
   1fr track refuses to shrink below its content, so one hidden pane holding a
   horizontal scroller silently widens the whole page. Found in Darkroom on
   23-08-2026: a review strip on an inactive tab pushed the document to 1907px
   inside a 1512px window. */
.panes { display:grid; grid-template-columns:minmax(0,1fr); margin-top:var(--sp-4); }
.pane { min-width:0; }
.pane { grid-area:1/1; opacity:0; transform:translateY(6px); pointer-events:none;
  transition:opacity var(--d-base) var(--ease-hover), transform var(--d-base) var(--ease-hover); }
.pane.on { opacity:1; transform:none; pointer-events:auto; }

/* ── DOCK ── */
.dock { position:relative; display:inline-flex; align-items:center; gap:var(--sp-2); padding:var(--sp-2);
  border-radius:var(--r-pill); background:var(--glass-bg);
  -webkit-backdrop-filter:blur(24px) saturate(150%); backdrop-filter:blur(24px) saturate(150%);
  border:1px solid var(--glass-border); box-shadow:var(--e3), inset 0 1px 0 var(--glass-hi);
  transition:border-radius var(--d-slow) var(--ease-entrance), width var(--d-slow) var(--ease-entrance); }
.dock > span { position:relative; z-index:1; height:44px; border-radius:var(--r-pill);
  display:inline-flex; align-items:center; justify-content:center; gap:var(--sp-2); color:var(--muted);
  cursor:pointer; padding:0; width:44px; overflow:hidden;
  transition:background var(--d-base) var(--ease-hover), color var(--d-base) var(--ease-hover),
             width var(--d-slow) var(--ease-entrance), padding var(--d-slow) var(--ease-entrance); }
.dock > span:hover { color:var(--text); background:rgba(255,255,255,.05); }
[data-theme="light"] .dock > span:hover { background:rgba(15,32,39,.05); }
.dock > span.on { color:var(--on-accent); }
[data-theme="light"] .dock > span.on { color:var(--on-accent); }
.dock > span.on:hover { background:transparent; }
.dock > span svg { width:18px; height:18px; flex-shrink:0; }
.dock > span .dlbl { font-family:var(--f-ui); font-size:var(--fs-sm); font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; white-space:nowrap; display:none; }
.dock .dsep { width:1px; height:24px; background:var(--line-strong); cursor:default; }
.dock .dsep:hover { background:var(--line-strong); }
.dock > span.dock-pill { position:absolute; top:var(--sp-2); height:44px; width:44px; padding:0;
  border-radius:var(--r-pill); background:var(--accent-lit); box-shadow:var(--e2); opacity:0; z-index:0;
  overflow:visible; cursor:default;
  transition:left var(--d-slow) var(--ease-entrance), width var(--d-slow) var(--ease-entrance), opacity var(--d-fast) ease; }
.dock > span.dock-pill:hover { background:var(--accent-lit); }

/* Variant B · LABELS. Icons keep company; the pill grows to fit. */
[data-dock="labels"] .dock > span { width:auto; padding:0 var(--sp-4); }
[data-dock="labels"] .dock > span .dlbl { display:inline; }
/* The separator is not an item and must not take the item padding. */
[data-dock="labels"] .dock .dsep { width:1px; padding:0; }
/* A labelled dock is 496px wide. Below 560 the labels drop and it becomes the
   icon dock, which is what a real dock does rather than overflowing the page. */
@media (max-width:560px){
  [data-dock="labels"] .dock > span { width:44px; padding:0; }
  [data-dock="labels"] .dock > span .dlbl { display:none; }}
.dock { max-width:100%; }

/* Variant C · BAR. Bottom tab bar for the Crew PWA. Square corners, full width. */
[data-dock="bar"] .dock { border-radius:var(--r) var(--r) 0 0; width:100%; justify-content:space-around;
  padding:var(--sp-2) var(--sp-3) calc(var(--sp-2) + env(safe-area-inset-bottom, 0px)); }
[data-dock="bar"] .dock > span { flex-direction:column; gap:3px; height:auto; width:auto;
  padding:var(--sp-2) var(--sp-3); border-radius:var(--r-xs); }
[data-dock="bar"] .dock > span .dlbl { display:inline; font-size:var(--fs-caps); }
[data-dock="bar"] .dock .dsep { display:none; }
[data-dock="bar"] .dock > span.dock-pill { display:none; }
[data-dock="bar"] .dock > span.on { color:var(--accent-text); background:var(--accent-tint); }

@media (max-width:760px){ .wayfind { grid-template-columns:1fr !important; } .rail { display:none; }}
/* The demo canvas is a small viewport of its own, so its contents answer to its
   width, not the page's. Without this the KPI row and the top bar clip. */
@supports (container-type: inline-size) {
  .wayfind > div:last-child { container-type:inline-size; }
  @container (max-width: 560px) { .canvas .kpi { grid-template-columns:1fr 1fr; } .topb .tsearch { flex-basis:130px; }}
  @container (max-width: 400px) { .canvas .kpi { grid-template-columns:1fr; } .topb .tsearch { display:none; }}}


/* ═══ CARRIED FROM V4 ══════════════════════════════════════════════════════ */
.t6-wrap { position:relative; isolation:isolate; border-radius:var(--r); overflow:hidden;
  background:var(--grad-ground); box-shadow:var(--e2); }
.t6 { width:100%; border-collapse:collapse; font-size:var(--fs-sm); }
.t6 th { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--subtle); text-align:left; padding:var(--sp-3) var(--sp-4);
  border-bottom:1px solid var(--line-strong); position:sticky; top:0; z-index:var(--z-sticky);
  background:var(--surface); }
.t6 th.num, .t6 td.num { text-align:right; }
.t6 td { padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--line); color:var(--muted); font-family:var(--f-body); }
.t6 tbody tr:last-child td { border-bottom:none; }
.t6 tbody tr { transition:background var(--d-fast) var(--ease-hover); }
.t6 tbody tr:hover { background:var(--hover); }
.t6 td.num { font-family:var(--f-num); font-weight:var(--num-wt); color:var(--text); font-variant-numeric:tabular-nums lining-nums; }
.t6 td.t-name { color:var(--text); font-weight:500; }
.t6 td.who { font-weight:600; color:var(--accent-text); }
.t6 tr.sel { background:var(--accent-tint); }
.t6 tr.sel:hover { background:color-mix(in srgb, var(--seed-accent-hi) 18%, transparent); }
.t6-edit { width:74px; text-align:right; font-family:var(--f-num); font-weight:var(--num-wt);
  font-size:var(--fs-sm); color:var(--text); background:var(--surface-deep);
  border:1px solid color-mix(in srgb, var(--seed-accent-hi) 55%, transparent); border-radius:6px;
  padding:5px 9px; outline:none; box-shadow:var(--e-sunk); margin-right:-9px; }

.kpi { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-4); }
@media (max-width:760px){ .kpi { grid-template-columns:1fr; }}
.klabel { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--muted); }
.kval { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-2xl); color:var(--text);
  margin-top:var(--sp-2); line-height:1.05; font-variant-numeric:tabular-nums; letter-spacing:-.01em; }
.kval small { font-size:var(--fs-md); font-weight:600; color:var(--subtle); }
.kdelta { display:inline-flex; gap:5px; font-family:var(--f-num); font-weight:var(--num-wt);
  font-size:var(--fs-sm); margin-top:var(--sp-2); font-variant-numeric:tabular-nums; }
.kdelta.up { color:var(--success-text); } .kdelta.attn { color:var(--danger-text); }
.spark { display:block; margin-top:var(--sp-3); width:100%; height:34px; cursor:crosshair; }
.spark-tip { position:fixed; z-index:var(--z-overlay); pointer-events:none; white-space:nowrap;
  font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-caps); color:var(--text);
  background:var(--glass-bg); -webkit-backdrop-filter:blur(14px); backdrop-filter:blur(14px);
  border:1px solid var(--glass-border); border-radius:8px; padding:5px 10px;
  opacity:0; transition:opacity calc(130ms * var(--dur)) ease; }
.spark .sline { stroke-dasharray:200; stroke-dashoffset:200; animation:draw calc(900ms * var(--dur)) var(--ease-entrance) calc(200ms * var(--dur)) forwards; }
.spark .sfill { opacity:0; animation:fadein calc(600ms * var(--dur)) ease calc(600ms * var(--dur)) forwards; }
@keyframes draw { to { stroke-dashoffset:0; }}
@keyframes fadein { to { opacity:1; }}

.kb { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-4); }
@media (max-width:760px){ .kb { grid-template-columns:1fr; }}
.kb-col { background:var(--surface-deep); border-radius:var(--r); padding:var(--sp-2); box-shadow:var(--e-sunk); }
.kb-head { display:flex; align-items:baseline; gap:var(--sp-2); padding:6px var(--sp-2) var(--sp-2); }
.kb-head b { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--muted); }
.kb-head .ct { margin-left:auto; font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm); color:var(--subtle); }
.kb-card { position:relative; background:var(--grad-ground); border-radius:var(--r-xs); padding:var(--sp-3);
  box-shadow:var(--e1); cursor:grab; margin-bottom:var(--sp-2); transition:background var(--d-fast) var(--ease-hover), box-shadow var(--d-fast) var(--ease-hover); }
.kb-card:hover { background:var(--hover); box-shadow:var(--e2); }
.kb-card:last-child { margin-bottom:0; }
.kb-card b { font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600; color:var(--text); display:block; }
.kb-meta { display:flex; align-items:center; gap:var(--sp-2); margin-top:var(--sp-3); }
.kb-meta small { font-family:var(--f-num); font-weight:600; font-size:var(--fs-sm); color:var(--subtle); }
.ava-s { width:24px; height:24px; border-radius:calc(var(--r-xs) * .7); display:grid; place-items:center; flex-shrink:0;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; line-height:1;
  letter-spacing:.03em; text-indent:.03em; color:var(--text); background:var(--ctrl); box-shadow:var(--e1); }
.ava-stack { display:flex; }
.ava-stack .ava-s { margin-left:-8px;
  background:linear-gradient(150deg in oklab, color-mix(in oklab, var(--seed-accent-hi) 92%, white) 0%,
    var(--seed-accent-hi) 55%, color-mix(in oklab, var(--seed-accent-lo) 55%, var(--seed-accent-hi)) 100%);
  color:var(--on-accent); box-shadow:0 0 0 2px var(--surface-deep); }
[data-theme="light"] .ava-stack .ava-s { color:var(--on-accent); }
.ava-stack .ava-s:first-child { margin-left:0; }

.prog { height:7px; border-radius:99px; background:var(--surface-deep); box-shadow:var(--e-sunk); overflow:hidden; }
.prog > i { display:block; height:100%; border-radius:99px; background:var(--accent-lit);
  transition:width var(--d-slow) var(--ease-entrance); }
.skel { position:relative; overflow:hidden; background:var(--surface-deep); border-radius:var(--r-xs); box-shadow:var(--e-sunk); }
.skel::after { content:""; position:absolute; inset:0; transform:translateX(-100%);
  background:linear-gradient(90deg, transparent, rgba(255,255,255,.06), transparent);
  animation:shim 1.4s infinite; }
[data-theme="light"] .skel::after { background:linear-gradient(90deg, transparent, rgba(255,255,255,.75), transparent); }
@keyframes shim { to { transform:translateX(100%); }}

.tip { position:relative; }
/* A tooltip at opacity 0 is invisible but STILL LAID OUT. With white-space:nowrap
   a long tip becomes a 330px box that pushes the page wider than the viewport,
   which is exactly what overflowed this page at 375px. It wraps and is capped. */
.tip::after { content:attr(data-tip); position:absolute; bottom:calc(100% + 8px); left:50%;
  transform:translate(-50%,4px) scale(.97); white-space:normal; width:max-content;
  max-width:min(230px, 60vw); text-align:center; line-height:1.4;
  font-family:var(--f-ui); font-size:var(--fs-sm);
  color:var(--text); background:var(--glass-bg); -webkit-backdrop-filter:blur(16px); backdrop-filter:blur(16px);
  border:1px solid var(--glass-border); border-radius:var(--r-xs); padding:5px var(--sp-3);
  box-shadow:var(--e3); opacity:0; pointer-events:none; z-index:var(--z-overlay);
  transition:opacity var(--d-fast) ease, transform var(--d-fast) var(--ease-entrance); }
.tip:hover::after { opacity:1; transform:translate(-50%,0) scale(1); }

/* A menu sits OVER content, so it is mostly opaque. The blur stays for the edge
   and the sense of a pane, but you should never be able to read the table
   underneath it. */
.menu { border-radius:var(--r-xs); padding:6px; min-width:200px;
  background:color-mix(in srgb, var(--surface-raise) 94%, transparent);
  -webkit-backdrop-filter:blur(26px) saturate(150%); backdrop-filter:blur(26px) saturate(150%);
  border:1px solid var(--glass-border); box-shadow:var(--e3), inset 0 1px 0 var(--glass-hi); }
/* ══ THE THREE REGISTERS ══════════════════════════════════════════════════════
   Every string in the system sits in exactly one of three, and almost every
   casing fault is a string in the wrong one.

   1. THE LABEL LAYER, small tracked uppercase. Anything that names or
      classifies content instead of BEING content: table heads, eyebrows, status
      labels, accordion headers, and every command in a menu. The tell is that it
      never appears inside a sentence.
   2. THE NAME LAYER, Title Case with small words down. Discrete selectable
      things: service lines, options, chips, tags, workflow states, and buttons
      that name an object. "Hero Cut, up to 90 Seconds" keeps "up" and "to" low,
      which is what makes it title case rather than Capitalising Every Word.
   3. THE PROSE LAYER, sentence case. Body copy, help text, accordion bodies,
      errors, tooltips. Anything read as language.

   CAPS IS NOT LOUDER HERE, IT IS QUIETER, and that is the whole reason it
   works. The command register drops from 13px body to 11px tracked UI type, so
   a menu reads as a row of machined labels rather than a row of sentences. Caps
   set at body size WOULD shout, which is why a full size button naming an
   object stays in the name layer and never takes this treatment.

   The source strings are stored in correct Title Case regardless, so a surface
   that renders them without the transform is still right. */
.menu > button:not(.optrow) { font-family:var(--f-ui); font-size:var(--fs-caps);
  letter-spacing:var(--ls-caps); text-transform:uppercase; }
/* .optrow is the exception that proves it: that menu holds service NAMES with
   prices, not commands, so it stays in the name layer and at a readable size. */
.menu button { display:flex; width:100%; align-items:center; gap:var(--sp-3); font-family:var(--f-body);
  font-size:var(--fs-sm); color:var(--muted); padding:var(--sp-2) var(--sp-3); border-radius:calc(var(--r-xs)*.7);
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
.menu button:hover { background:color-mix(in srgb, var(--seed-accent-hi) 18%, transparent); color:var(--text); }
.menu button.attn:hover { background:color-mix(in srgb, var(--danger) 16%, transparent); }
.menu button.attn { color:var(--danger-text); }
.menu button .k { margin-left:auto; font-family:var(--f-ui); font-size:var(--fs-caps); color:var(--subtle); }
.menu hr { height:1px; background:var(--line); border:none; margin:var(--sp-1) var(--sp-2); }

.empty { text-align:center; padding:var(--sp-7) var(--sp-4);
  border:1px dashed var(--line-strong); border-radius:var(--r-lg); }
.empty .ic { width:46px; height:46px; margin:0 auto var(--sp-3); border-radius:var(--r-xs);
  display:grid; place-items:center; background:var(--surface-deep); box-shadow:var(--e-sunk); color:var(--subtle); }
.empty b { font-family:var(--f-head); font-size:var(--fs-sm); font-weight:400; text-transform:uppercase;
  letter-spacing:var(--ls-head); display:block; margin-bottom:6px; }
.empty p { font-size:var(--fs-sm); color:var(--subtle); max-width:38ch; margin:0 auto var(--sp-4); }

/* ═══ NEW IN V5 · the atoms V4 never had ═══════════════════════════════════ */

/* Inline alert / banner. V4 had a toast but nothing that lives IN the page. */
.banner { display:flex; gap:var(--sp-3); align-items:flex-start; padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-xs); background:var(--surface-deep); box-shadow:inset 3px 0 0 var(--tone, var(--info)); }
.banner .bi { color:var(--tone-text, var(--info-text)); flex-shrink:0; margin-top:1px; }
.banner .bi svg { width:15px; height:15px; }
.banner b { display:block; font-family:var(--f-ui); font-size:var(--fs-sm); font-weight:600;
  letter-spacing:.06em; text-transform:uppercase; color:var(--tone-text, var(--info-text)); margin-bottom:3px; }
.banner p { font-size:var(--fs-sm); color:var(--muted); }
.banner.danger  { --tone:var(--danger);  --tone-text:var(--danger-text); }
.banner.warning { --tone:var(--warning); --tone-text:var(--warning-text); }
.banner.success { --tone:var(--success); --tone-text:var(--success-text); }
.banner.info    { --tone:var(--info);    --tone-text:var(--info-text); }

/* Spinner. Only for indeterminate waits where a skeleton cannot know the shape. */
.spin { width:18px; height:18px; border-radius:50%; flex-shrink:0; padding:2.5px;
  background:conic-gradient(from 0deg, transparent 0deg, var(--accent-lit) 300deg);
  -webkit-mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); mask-composite:exclude;
  animation:spin calc(800ms * var(--dur)) linear infinite; }
@keyframes spin { to { transform:rotate(360deg); }}

/* Popover. Distinct from tooltip (hover, text) and menu (list of actions). */
.pop { position:absolute; z-index:var(--z-drop); width:260px; background:var(--surface-raise);
  border-radius:var(--r-xs); box-shadow:var(--e3); padding:var(--sp-4); }
.pop h4 { font-family:var(--f-ui); font-size:var(--fs-sm); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--muted); margin-bottom:var(--sp-2); }

/* Drawer / side sheet. */
.drawer-demo { position:relative; height:230px; border-radius:var(--r); overflow:hidden;
  background:var(--grad-ground); box-shadow:var(--e2); }
.drawer-demo .scrim { position:absolute; inset:0; background:var(--scrim); opacity:0; pointer-events:none;
  -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px); transition:opacity var(--d-base) ease; }
.drawer-demo.open .scrim { opacity:1; pointer-events:auto; }
.drawer { position:absolute; top:0; right:0; bottom:0; width:min(280px, 78%); background:var(--surface-raise);
  box-shadow:var(--e4); padding:var(--sp-4); transform:translateX(100%);
  transition:transform var(--d-slow) var(--ease-entrance); display:flex; flex-direction:column; gap:var(--sp-3); }
.drawer-demo.open .drawer { transform:none; }
.drawer .dh { display:flex; align-items:center; justify-content:space-between; }
.drawer .dh b { font-family:var(--f-head); font-size:var(--fs-sm); font-weight:400; text-transform:uppercase;
  letter-spacing:var(--ls-head); }

/* Combobox: defined once, with the pickers. Glass list, same skin as the dropdown. */

/* Dropzone. */
.dz { border-radius:var(--r-xs); background:var(--surface-deep); box-shadow:var(--e-sunk);
  padding:var(--sp-6) var(--sp-4); text-align:center; cursor:pointer;
  transition:background var(--d-fast) var(--ease-hover), box-shadow var(--d-fast) var(--ease-hover); }
.dz:hover, .dz.over { background:var(--accent-tint); box-shadow:var(--e-sunk); outline:2px dashed var(--accent-live); outline-offset:-4px; }
.dz svg { width:22px; height:22px; color:var(--subtle); margin:0 auto var(--sp-2); }
.dz b { display:block; font-family:var(--f-ui); font-size:var(--fs-sm); font-weight:600; color:var(--text); }
.dz small { font-size:var(--fs-sm); color:var(--subtle); }

/* Timeline / activity feed. */
.tl { position:relative; padding-left:var(--sp-5); }
.tl::before { content:""; position:absolute; left:5px; top:6px; bottom:6px; width:1px; background:var(--line-strong); }
.tl-item { position:relative; padding-bottom:var(--sp-4); }
.tl-item:last-child { padding-bottom:0; }
.tl-item::before { content:""; position:absolute; left:calc(var(--sp-5) * -1 + 2px); top:5px; width:7px; height:7px;
  border-radius:50%; background:var(--ctrl); box-shadow:0 0 0 3px var(--surface); }
.tl-item.on::before { background:var(--accent-lit); }
.tl-item b { font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600; color:var(--text); display:block; }
.tl-item time { font-family:var(--f-num); font-weight:600; font-size:var(--fs-sm); color:var(--subtle);
  font-variant-numeric:tabular-nums; }
.tl-item p { font-size:var(--fs-sm); color:var(--muted); margin-top:3px; }

/* Tree nav. */
.tree-row { display:flex; align-items:center; gap:var(--sp-2); font-family:var(--f-body); font-size:var(--fs-sm);
  color:var(--muted); padding:5px var(--sp-2); border-radius:calc(var(--r-xs)*.7); cursor:pointer;
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
.tree-row:hover { background:var(--hover); color:var(--text); }
.tree-row.on { color:var(--accent-text); background:var(--accent-tint); }
.tree-row .cv { width:11px; height:11px; color:var(--subtle); transition:transform var(--d-base) var(--ease-spring); }
.tree-row.open .cv { transform:rotate(90deg); }
.tree-kids { padding-left:var(--sp-4); margin-left:5px; border-left:1px solid var(--line); display:none; }
.tree-kids.open { display:block; }

/* Bulk-select toolbar. Appears when rows are checked. */
.bulk { display:flex; align-items:center; flex-wrap:wrap; min-width:0; max-width:100%;
  gap:var(--sp-2) var(--sp-4); padding:var(--sp-3) 0; border-bottom:1px solid var(--line-strong);
  background:none; border-radius:0; box-shadow:none; }
.bulk .n { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm); color:var(--text);
  font-variant-numeric:tabular-nums; }
.bulk .n small { font-family:var(--f-ui); font-weight:600; font-size:var(--fs-sm); color:var(--subtle);
  letter-spacing:.06em; text-transform:uppercase; margin-left:5px; }

/* Command palette. E5, the highest surface in the system. */
.cmdk { border-radius:var(--r); background:var(--surface-raise); box-shadow:var(--e5); overflow:hidden; }
.cmdk .cin { display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-4);
  border-bottom:1px solid var(--line); }
.cmdk .cin input { font-family:var(--f-body); font-size:var(--fs-lg); color:var(--text); width:100%; }
.cmdk .cin input::placeholder { color:var(--disabled); }
.cmdk .cin svg { width:17px; height:17px; color:var(--subtle); flex-shrink:0; }
.cmdk .cgrp { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.14em;
  text-transform:uppercase; color:var(--subtle); padding:var(--sp-3) var(--sp-4) var(--sp-1); }
.cmdk .crow { display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-2) var(--sp-4);
  font-size:var(--fs-sm); color:var(--muted); cursor:pointer; }
.cmdk .crow:hover, .cmdk .crow.hi { background:var(--hover); color:var(--text); }
.cmdk .crow svg { width:14px; height:14px; color:var(--subtle); }
.cmdk .crow .k { margin-left:auto; font-family:var(--f-ui); font-size:var(--fs-caps); color:var(--subtle); }

/* Charts. V4 shipped only a sparkline. */
.bars { display:flex; align-items:flex-end; gap:var(--sp-2); height:120px; margin-bottom:26px; }
.bars > div { flex:1; border-radius:var(--r-xs) var(--r-xs) 3px 3px; position:relative;
  background:linear-gradient(180deg in oklab, var(--accent-lit) 0%, color-mix(in oklab, var(--seed-accent-hi) 55%, transparent) 100%);
  transform-origin:bottom; animation:grow calc(700ms * var(--dur)) var(--ease-entrance) both; }
@keyframes grow { from { transform:scaleY(0); }}
.bars > div span { position:absolute; bottom:-20px; left:0; right:0; text-align:center;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); }
.donut { width:120px; height:120px; }
/* BUTT caps, not round. A round cap adds half the stroke width of arc at each
   end, which on a 13px stroke over a 97px circumference is enormous: every
   segment overran its neighbour and the ring read as two arcs crossing. Round
   is only correct on a single-value ring with nothing beside it. */
.donut circle { fill:none; stroke-width:13; stroke-linecap:butt;
  transform:rotate(-90deg); transform-origin:center; }
.donut.single circle { stroke-linecap:round; }

/* Pagination */
.pager { display:inline-flex; align-items:center; gap:var(--sp-1); }
.pager button { min-width:32px; height:32px; padding:0 var(--sp-2); border-radius:var(--r-xs);
  font-family:var(--f-num); font-weight:600; font-size:var(--fs-sm); color:var(--muted);
  font-variant-numeric:tabular-nums; transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
.pager button:hover { background:var(--hover); color:var(--text); }
.pager button svg { width:11px; height:11px; margin:0 auto; }
.pager button.on { background:var(--accent-lit); color:var(--on-accent); }
[data-theme="light"] .pager button.on { color:var(--on-accent); }

/* Toast. Severity is a status line inside, never a coloured bar. */
.toast { display:flex; gap:var(--sp-3); align-items:flex-start; padding:var(--sp-3) var(--sp-4);
  border-radius:var(--r-xs); background:var(--glass-bg); -webkit-backdrop-filter:blur(22px);
  backdrop-filter:blur(22px); border:1px solid var(--glass-border); box-shadow:var(--e4); max-width:360px; }
.toast b { font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600; color:var(--text); display:block; }
.toast p { font-size:var(--fs-sm); color:var(--muted); margin-top:2px; }

/* Modal */
.modal { border-radius:var(--r-lg); background:var(--grad-ground); box-shadow:var(--e4);
  padding:var(--sp-5); max-width:420px; }
.scrim-demo { position:relative; border-radius:var(--r-lg); overflow:hidden; min-height:250px;
  background:var(--grad-ground); box-shadow:var(--e2); display:grid; place-items:center; padding:var(--sp-4); }
.scrim-demo .scrim { position:absolute; inset:0; display:grid; place-items:center; z-index:2;
  background:var(--scrim); -webkit-backdrop-filter:blur(10px); backdrop-filter:blur(10px);
  opacity:0; pointer-events:none; transition:opacity var(--d-base) ease; }
.scrim-demo.open .scrim { opacity:1; pointer-events:auto; }
.scrim-demo .modal { transform:translateY(14px) scale(.97);
  transition:transform calc(320ms * var(--dur)) var(--ease-entrance); }
.scrim-demo.open .modal { transform:none; }
.modal h3 { font-family:var(--f-head); font-size:var(--fs-md); font-weight:400; text-transform:uppercase;
  letter-spacing:var(--ls-head); line-height:var(--lh-head); margin:0 0 var(--sp-3); color:var(--text); }
.modal p { font-size:var(--fs-sm); color:var(--muted); margin-bottom:var(--sp-5); }
/* ── THE SEED CARDS ──
   Six cards on the same rhythm and the same corner as the six ramp swatches
   below them, because they are the same kind of object: a colour with its name
   under it. The old block was three tall pillars with the palette NAME painted
   over the colour, which failed twice. It carried hardcoded V4 names and hexes,
   so the document said "Royal Onyx, #2C5364 to #0F2027" over a red gradient and
   lied about its own system. And type sitting directly on the swatch can only
   stay readable if you know the colour in advance, which stopped being true the
   moment these became pickers.

   So the label sits on a --surface bar UNDER the swatch. That is readable
   against anything anybody picks, and it is the only version of this card that
   cannot break. Each card is a label wrapping a hidden colour input, so the
   whole card is the hit target and every hex is live.

   Roles, not names: Ink, Paper, Accent. A palette gets renamed; a role does
   not, and a name painted into a document drifts the day the seed changes. */
.seeds { display:grid; grid-template-columns:repeat(6,1fr); gap:var(--sp-2); margin-top:var(--sp-3); }
@media (max-width:900px){ .seeds { grid-template-columns:repeat(3,1fr); }}
@media (max-width:520px){ .seeds { grid-template-columns:repeat(2,1fr); }}
.seedcard { position:relative; display:block; border-radius:var(--r-xs); box-shadow:var(--e1);
  overflow:hidden; cursor:pointer; }
.seedcard.fixed { cursor:default; }
.seedcard .scw { display:block; height:70px; background:var(--sc, var(--ctrl)); position:relative; }
.seedcard .scm { display:block; padding:var(--sp-2); background:var(--surface); }
.seedcard b { display:block; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--text); white-space:nowrap;
  overflow:hidden; text-overflow:ellipsis; }
.seedcard .mono { display:block; margin-top:2px; font-family:var(--fm); font-size:var(--fs-caps);
  color:var(--subtle); letter-spacing:0; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.seedcard input[type=color] { position:absolute; inset:0; width:100%; height:100%; opacity:0;
  border:none; padding:0; cursor:pointer; }
.seedcard:has(input:focus-visible) { outline:2px solid var(--accent-live); outline-offset:2px; }


/* ═══ RESTORED FROM V4 ═════════════════════════════════════════════════════
   Fifteen components the first V5 pass dropped without saying so. Rebuilt on
   V5 tokens, so they ride the seeds and the density dial like everything else. */

/* Calendar. A native date input renders OS chrome and never ships. */
.cal { max-width:290px; }
.cal-head { display:flex; justify-content:space-between; align-items:center; margin-bottom:var(--sp-3); }
.cal-head b { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--text); }
.cal-nav { display:flex; gap:6px; }
.cal-nav button { width:26px; height:26px; border-radius:calc(var(--r-xs) * .9); display:grid; place-items:center;
  background:var(--surface-deep); color:var(--subtle); box-shadow:var(--e-sunk);
  transition:color calc(130ms * var(--dur)) var(--ease-hover); }
.cal-nav button:hover { color:var(--text); }
.cal-nav svg { width:11px; height:11px; }
.cal-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:2px; text-align:center; }
.cal-grid b { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.08em;
  color:var(--subtle); padding:6px 0; }
.cal-grid span { font-family:var(--f-num); font-weight:600; font-size:var(--fs-sm); padding:8px 0;
  border-radius:calc(var(--r-xs) * .9); cursor:pointer; color:var(--muted); font-variant-numeric:tabular-nums;
  transition:background calc(130ms * var(--dur)) var(--ease-hover), color calc(130ms * var(--dur)) var(--ease-hover); }
.cal-grid span:hover { background:var(--hover); color:var(--text); }
.cal-grid span.dim { color:var(--disabled); }
/* The in-range fill is squared off so a range reads as one continuous band. */
.cal-grid span.inr { background:var(--accent-tint); border-radius:0; color:var(--text); }
.cal-grid span.on:has(+ span.inr) { border-radius:calc(var(--r-xs) * .9) 0 0 calc(var(--r-xs) * .9); }
.cal-grid span.inr + span.on { border-radius:0 calc(var(--r-xs) * .9) calc(var(--r-xs) * .9) 0; }
.cal-grid span.on { background:var(--accent-lit); color:var(--on-accent); box-shadow:var(--e2); }
[data-theme="light"] .cal-grid span.on { color:var(--on-accent); }

/* Time picker */
.timep { display:flex; gap:9px; align-items:center; }
.timep .cell { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-lg); color:var(--text);
  background:var(--surface-deep); border-radius:calc(var(--r-xs) * .9); padding:10px 15px;
  box-shadow:var(--e-sunk); font-variant-numeric:tabular-nums; }
.timep .colon { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-lg); color:var(--subtle); }
.timep .mer { display:flex; flex-direction:column; gap:4px; }
.timep .mer button { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.08em;
  padding:5px 11px; border-radius:calc(var(--r-xs) * .7); background:var(--surface-deep); color:var(--subtle);
  box-shadow:var(--e-sunk); transition:background calc(130ms * var(--dur)) var(--ease-hover), color calc(130ms * var(--dur)) var(--ease-hover); }
.timep .mer button.on { background:var(--accent-lit); color:var(--on-accent); box-shadow:var(--e2); }
[data-theme="light"] .timep .mer button.on { color:var(--on-accent); }

/* Filing index. Subgrid, so the three columns align across every row. */
.filing { display:grid; grid-template-columns:1fr max-content max-content;
  border-radius:var(--r); overflow:hidden; background:var(--grad-ground); box-shadow:var(--e1); }
.frow { grid-column:1/-1; display:grid; grid-template-columns:subgrid; gap:var(--sp-4); align-items:center;
  padding:var(--sp-3) var(--sp-4); border-bottom:1px solid var(--line); cursor:pointer;
  transition:background calc(130ms * var(--dur)) var(--ease-hover); }
.frow:last-child { border-bottom:none; }
.frow:hover { background:var(--hover); }
.fname { font-family:var(--f-body); font-size:var(--fs-md); color:var(--text); }
.fname small { display:block; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.1em; text-transform:uppercase; color:var(--subtle); margin-top:2px; }
.fmeta { font-family:var(--f-num); font-weight:600; font-size:var(--fs-sm); color:var(--subtle);
  white-space:nowrap; justify-self:end; font-variant-numeric:tabular-nums; }
/* Attention rows wear an inset bar, never a coloured background. */
.frow.attn { box-shadow:inset 3px 0 0 var(--danger); background:color-mix(in srgb, var(--danger) 6%, transparent); }
.frow.attn:hover { background:color-mix(in srgb, var(--danger) 10%, transparent); }

/* Accordion. Height animates on grid-template-rows, so no measured max-height. */
.acc { border-radius:var(--r); overflow:hidden; background:var(--grad-ground); box-shadow:var(--e1); }
.acc-item + .acc-item { border-top:1px solid var(--line); }
/* An accordion header labels the block under it, so it belongs to the label
   layer. It stays at 13px rather than dropping to 11: this one is a hit target
   as well as a label, and it has to stay easy to aim at. */
.acc-trig { width:100%; display:flex; justify-content:space-between; align-items:center; gap:var(--sp-3);
  text-align:left; padding:var(--sp-4); color:var(--text); font-family:var(--f-ui); font-size:var(--fs-sm);
  font-weight:600; letter-spacing:var(--ls-caps); text-transform:uppercase;
  transition:background calc(130ms * var(--dur)) var(--ease-hover); }
.acc-trig:hover { background:var(--hover); }
.acc-trig svg { width:12px; height:12px; flex-shrink:0; color:var(--subtle);
  transition:transform calc(320ms * var(--dur)) var(--ease-spring); }
.acc-item.open .acc-trig svg { transform:rotate(180deg); }
.acc-body { display:grid; grid-template-rows:0fr; transition:grid-template-rows calc(320ms * var(--dur)) var(--ease-entrance); }
.acc-item.open .acc-body { grid-template-rows:1fr; }
.acc-body > div { overflow:hidden; }
/* The body had ZERO top padding, so it opened flush under the trigger and the
   trigger's hover fill ran straight into the first line of prose. The gap is
   not decoration: it is what separates the label from the thing it labels. */
.acc-body p { padding:var(--sp-3) var(--sp-4) var(--sp-4); font-size:var(--fs-sm);
  color:var(--muted); line-height:1.6; }

/* Step indicator */
.steps { display:flex; align-items:center; flex-wrap:nowrap; gap:var(--sp-3);
  /* The current step's ring is drawn outside its circle. Without room it is
     clipped by the scroll box, which is what sliced the top off step three. */
  padding:5px 2px; }
.step { display:flex; align-items:center; gap:10px; }
.step .sn { width:27px; height:27px; border-radius:50%; display:grid; place-items:center; flex-shrink:0;
  font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-caps); color:var(--subtle);
  background:var(--surface-deep); box-shadow:var(--e-sunk);
  transition:background calc(200ms * var(--dur)) var(--ease-hover), color calc(200ms * var(--dur)) var(--ease-hover), box-shadow calc(200ms * var(--dur)) var(--ease-hover); }
.step b { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); }
.step.done .sn { background:var(--accent-lit); color:var(--on-accent); box-shadow:var(--e2); }
[data-theme="light"] .step.done .sn { color:var(--on-accent); }
.step.done b { color:var(--muted); }
.step.cur .sn { box-shadow:var(--e-sunk), 0 0 0 2px color-mix(in srgb, var(--seed-accent-hi) 50%, transparent);
  color:var(--accent-text); }
.step.cur b { color:var(--text); }
.step .sline { width:38px; height:1px; background:var(--line-strong); }

/* Media cards */
.media-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:var(--sp-4); }
.media-card { position:relative; isolation:isolate; border-radius:var(--r); overflow:hidden;
  background:var(--grad-ground); box-shadow:var(--e1); cursor:pointer;
  transition:background calc(130ms * var(--dur)) var(--ease-hover), box-shadow calc(130ms * var(--dur)) var(--ease-hover); }
.media-card:hover { box-shadow:var(--e2); }
.media-thumb { aspect-ratio:16/9; display:grid; place-items:center; color:rgba(255,255,255,.8); position:relative;
  background:radial-gradient(95% 115% at 72% 0%, color-mix(in srgb, var(--seed-accent-hi) 45%, transparent), transparent 65%), var(--surface-deep); }
.media-thumb svg { width:26px; height:26px; }
.media-thumb .dur { position:absolute; right:8px; bottom:8px; font-family:var(--f-num); font-weight:600;
  font-size:var(--fs-caps); background:rgba(0,0,0,.55); color:var(--on-accent); padding:2px 7px; border-radius:6px; }
.media-body { padding:var(--sp-3); }
.media-body b { font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600; color:var(--text); display:block; }
.media-body small { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.08em;
  text-transform:uppercase; color:var(--subtle); }

/* Icon grid, carved cells */
.iconrow { display:grid; grid-template-columns:repeat(8,1fr); gap:10px; }
.icell { aspect-ratio:1; display:grid; place-items:center; border-radius:calc(var(--r-xs) * 1.1); cursor:pointer;
  background:var(--surface-deep); box-shadow:var(--e-sunk); color:var(--muted);
  transition:color calc(130ms * var(--dur)) var(--ease-hover), background calc(130ms * var(--dur)) var(--ease-hover); }
.icell:hover { color:var(--accent-text); background:var(--accent-tint); }
.icell svg { width:16px; height:16px; }


/* Checklist. The whole row is the hit area, never just the box. */
.check { display:flex; align-items:center; gap:var(--sp-3); padding:11px 2px; border-bottom:1px solid var(--line);
  cursor:pointer; }
.check:last-child { border-bottom:none; }
.check .lbl { font-family:var(--f-body); font-size:var(--fs-md); color:var(--muted); flex:1;
  transition:color calc(160ms * var(--dur)) var(--ease-hover); }
.check:hover .lbl { color:var(--text); }
.check small { font-family:var(--f-num); font-weight:600; font-size:var(--fs-caps); color:var(--subtle); }

/* Toggle list row: the label answers to the switch. */
.vt-row { display:flex; align-items:center; justify-content:space-between; gap:var(--sp-4);
  padding:14px 2px; border-bottom:1px solid var(--line); }
.vt-row:last-child { border-bottom:none; }
.vt-row span { font-size:var(--fs-md); color:var(--muted); transition:color calc(200ms * var(--dur)) var(--ease-hover); }
.vt-row.on span { color:var(--accent-text); font-weight:500; }

/* The accent hero band */
.accent-band { position:relative; border-radius:var(--r-lg); overflow:hidden; padding:30px;
  background:radial-gradient(85% 100% at 78% 0%, color-mix(in srgb, var(--seed-accent-hi) 75%, transparent), transparent 62%), var(--grad-accent);
  box-shadow:var(--e3); color:var(--on-accent); }
.accent-band::after { content:""; position:absolute; inset:0; background-image:var(--noise);
  background-size:130px; opacity:.06; mix-blend-mode:overlay; pointer-events:none; }
.accent-band > * { position:relative; z-index:1; }
.accent-band h3 { font-family:var(--f-head); font-size:var(--fs-xl); font-weight:400; text-transform:uppercase;
  letter-spacing:var(--ls-head); line-height:var(--lh-head); color:var(--on-accent); margin:0 0 var(--sp-3); }
.accent-band p { color:color-mix(in srgb, var(--on-accent) 78%, transparent); max-width:52ch; }

.toast .ib { margin-left:auto; margin-top:-3px; margin-right:-6px; }
.pager .ell { color:var(--subtle); padding:0 4px; font-family:var(--f-num); }

@media (max-width:760px){ .media-grid { grid-template-columns:1fr; } .iconrow { grid-template-columns:repeat(4,1fr); }}



/* ═══ IDENTITY · THE RAIL FOOT ═════════════════════════════════════════════
   THERE IS NO USER CHIP. Knox, 23-08-2026: identity is not a tag floating in a
   layout, it is a fixed block at the bottom of the rail, and it is the same
   block in every app. This is lifted from Knox Artiste Accounts, which is where
   the pattern was settled and where Darkroom and Grid already follow it.

   Two rows, one block, pinned to the bottom of the rail above a hairline:
   the theme switch, then the account. Settings lives inside the account menu,
   never in the rail, because the rail is for the work and the account is not
   the work. The menu opens UPWARDS, since the block sits at the bottom. */
.rail-foot { margin-top:auto; padding-top:var(--sp-3); display:flex; flex-direction:column;
  gap:var(--sp-1); border-top:1px solid var(--line); }

/* THE LABEL NAMES THE MODE YOU ARE IN, not the one you might switch to.
   Knox, 19-08-2026, on the KAA build: "the toggle is always showing light mode,
   even if I'm going into dark mode." The old row was a switch called "Light
   mode" and the toggle said whether light was on, which is correct in principle
   and ambiguous by construction: half the people read it as a label about now
   and half as an offer. So the words and the glyph both state what is true
   right now. A moon and "Dark mode" in the dark, a sun and "Light mode" in the
   light. Do not re-litigate this. */
.rail-theme { display:flex; align-items:center; justify-content:space-between; gap:10px;
  padding:7px 12px; border-radius:var(--r-xs); cursor:pointer;
  transition:background var(--d-fast) var(--ease-hover); }
.rail-theme:hover { background:var(--hover); }
.rail-theme .lbl { display:flex; align-items:center; gap:10px; min-width:0;
  font-family:var(--f-body); font-size:var(--fs-sm); color:var(--muted);
  transition:color var(--d-fast) var(--ease-hover); }
.rail-theme:hover .lbl { color:var(--text); }
.rail-theme .lbl svg { width:15px; height:15px; flex-shrink:0; }
.rail-theme .tgl { transform:scale(.82); transform-origin:right center; }

.who { position:relative; display:flex; align-items:center; gap:9px; width:100%;
  box-sizing:border-box; padding:7px 12px; border-radius:var(--r-xs); cursor:pointer;
  transition:background var(--d-fast) var(--ease-hover); }
.who:hover { background:var(--hover); }
.who .whoname { display:block; min-width:0; font-family:var(--f-body); font-size:var(--fs-sm);
  color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
  transition:color var(--d-fast) var(--ease-hover); }
.who:hover .whoname { color:var(--text); }
.who .ava { width:26px; height:26px; font-size:var(--fs-caps); }
.who .chev { margin-left:auto; width:10px; height:6px; flex-shrink:0; color:var(--subtle);
  transition:transform 240ms var(--ease-spring); }
.who.open .chev { transform:rotate(180deg); }

/* This menu overlaps its OWN container, so it cannot be glass. A 42% translucent
   panel over the canvas reads as depth; the same panel over the rail lets the
   nav links and their counts bleed straight through the account name. Opaque,
   with the blur kept only for the edge treatment. */
.who-menu { position:absolute; z-index:var(--z-drop); top:auto; bottom:calc(100% + 10px);
  left:0; right:auto; min-width:100%; width:max-content; max-width:min(320px, 88vw);
  transform-origin:bottom left;
  background:var(--surface-raise); -webkit-backdrop-filter:none; backdrop-filter:none;
  box-shadow:var(--e4);
  opacity:0; transform:translateY(6px) scale(.985); pointer-events:none;
  transition:opacity 180ms var(--ease-hover), transform 240ms var(--ease-spring); }
.who.open .who-menu { opacity:1; transform:none; pointer-events:auto; }
/* The identity line is a caption, not a menu item, so it does not take the hover. */
.who-menu .who-head { display:block; padding:10px 12px 11px; border-radius:0; cursor:default; }
.who-menu .who-head:hover { background:transparent; }
.who-menu .who-head b { display:block; font-family:var(--f-body); font-size:var(--fs-sm);
  font-weight:600; color:var(--text); }
.who-menu .who-head span { display:block; font-family:var(--f-ui); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:.08em; text-transform:uppercase; color:var(--muted);
  margin-top:4px; overflow-wrap:anywhere; }

/* Collapsed rail: the block stays, the words go. */
[data-rail="icons"] .rail-foot { align-items:center; }
[data-rail="icons"] .rail-theme { justify-content:center; padding:7px 0; width:42px; }
[data-rail="icons"] .rail-theme .lbl span:not(#themeIcon) { display:none; }
[data-rail="icons"] .rail-theme .tgl { display:none; }
[data-rail="icons"] .who { justify-content:center; padding:7px 0; width:42px; }
[data-rail="icons"] .who .whoname, [data-rail="icons"] .who .chev { display:none; }
[data-rail="icons"] .who-menu { min-width:210px; }


/* ═══ THE FRONT DOOR · AUTH SURFACES ═══════════════════════════════════════
   Sign in, sign up, password reset. Structure taken from Knox Artiste Accounts'
   own signin.html, which is where this was settled.

   A finance or production tool's front door is not a marketing page: no hero,
   no gradient wash, no illustration, no social buttons. One card on the ground
   gradient, the wordmark above it, the fields, one key. The page should look
   like the app it opens, because it is the first promise the app makes. */
.ground { position:relative; isolation:isolate; min-height:100dvh; display:grid;
  place-items:center; padding:var(--sp-6) var(--sp-5); background:var(--grad-ground); }
.ground > .grain { position:absolute; inset:0; pointer-events:none; z-index:0;
  background-image:var(--noise); background-size:120px; opacity:.04; mix-blend-mode:overlay; }
[data-theme="light"] .ground > .grain { opacity:.025; mix-blend-mode:multiply; }

.gate { position:relative; z-index:1; width:min(400px, 100%); }
/* Sign-up carries eight fields. At 400px that is a scroll; at 560 it is a form. */
.gate.wide { width:min(568px, 100%); }

.mark { text-align:center; margin-bottom:var(--sp-5); }
.mark .wordmark { font-family:var(--f-hero); font-size:var(--fs-lg); text-transform:uppercase;
  letter-spacing:var(--ls-head); color:var(--text); }
.mark .wordmark span { color:var(--accent-lit); }
/* --subtle sits near the AA floor, and these captions have to be read, so they
   take --muted. Same finding as the app and as KAA's own front door. */
.mark .sub { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.14em; text-transform:uppercase; color:var(--muted); margin-top:9px; }

.gate .card { padding:var(--sp-5); }
.gate h1 { font-family:var(--f-head); font-size:var(--fs-lg); font-weight:400;
  text-transform:uppercase; letter-spacing:var(--ls-head); line-height:var(--lh-head);
  color:var(--text); margin:0 0 6px; }
.gate-lede { font-family:var(--f-body); font-size:var(--fs-sm); color:var(--muted);
  margin:0 0 var(--sp-5); line-height:1.55; max-width:38ch; }

.gate.wide .gate-lede { max-width:56ch; }
.rows { display:flex; flex-direction:column; gap:var(--sp-4); }
.rows.pair { display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4); }
@media (max-width:520px){ .rows.pair { grid-template-columns:1fr; }}
/* One action on the page is the definition of a primary, so the front door
   takes the accent key at full width. */
.gate .btn-accent, .gate .btn-key { width:100%; justify-content:center; margin-top:var(--sp-5); }
.gate .btn-accent[disabled] { pointer-events:none; }

/* The reveal lives INSIDE the well, the same way the shortcut hint lives inside
   the search well. It is never a checkbox below the field. */
.pw { display:flex; align-items:center; padding-right:6px; }
.pw .well-input { flex:1; }
.peek { display:grid; place-items:center; width:30px; height:30px; border-radius:calc(var(--r-xs)*.8);
  color:var(--subtle); cursor:pointer; flex-shrink:0; background:none; border:none;
  transition:color var(--d-fast) var(--ease-hover), background var(--d-fast) var(--ease-hover); }
.peek:hover { color:var(--text); background:var(--accent-tint); }
.peek:active { transform:scale(.94); transition-duration:70ms; }
.peek svg { width:15px; height:15px; }

/* Requirements are a CHECKLIST, not a coloured strength bar. A bar says "weak"
   without saying what to change, and it signals with colour alone. */
.reqs { display:flex; flex-direction:column; gap:5px; margin-top:10px; }
/* The UNMET line is the one somebody needs to read, so it takes the readable
   tier and the met one recedes. The first pass had this the wrong way round,
   which put the only text that matters at 2.95:1. */
.req { display:flex; align-items:center; gap:8px; font-family:var(--f-body);
  font-size:var(--fs-caps); color:var(--muted); transition:color var(--d-fast) var(--ease-hover); }
.req .box { width:13px; height:13px; border-radius:4px; flex-shrink:0; display:grid;
  place-items:center; background:var(--surface-deep); box-shadow:var(--e-sunk); color:transparent;
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
.req .box svg { width:8px; height:8px; }
.req.met { color:var(--subtle); }
.req.met .box { background:var(--accent-lit); color:var(--on-accent); box-shadow:none; }
[data-theme="light"] .req.met .box { color:var(--on-accent); }

.gate .f-err { display:none; margin-top:var(--sp-4); line-height:1.5; }
.gate .f-err.on { display:block; }
/* The done state borrows the accent, not the status colour: finishing a reset is
   not an alert. */
.gate .f-done { display:none; margin-top:var(--sp-4); font-family:var(--f-ui);
  font-size:var(--fs-sm); font-weight:600; color:var(--accent-text); line-height:1.5; }
.gate .f-done.on { display:block; }

.authrow { display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
  margin-top:var(--sp-4); }
.authlink { font-family:var(--f-body); font-size:var(--fs-sm); color:var(--muted);
  text-decoration:none; border-bottom:1px solid var(--line-strong); cursor:pointer;
  transition:color var(--d-fast) var(--ease-hover), border-color var(--d-fast) var(--ease-hover); }
.authlink:hover { color:var(--text); border-color:var(--accent-live); }

.foot { margin-top:var(--sp-5); text-align:center; font-family:var(--f-body);
  font-size:var(--fs-caps); color:var(--muted); line-height:1.7; }
.foot a { color:var(--muted); text-decoration:none; border-bottom:1px solid var(--line-strong); }
.foot a:hover { color:var(--text); }


/* ═══ FROM THE APPS ════════════════════════════════════════════════════════
   Built 23-08-2026 from an inventory of Pulse, Knox Artiste Accounts and
   Darkroom. Everything here already existed in at least one app, and several
   existed in two or three under different names, which is the whole argument
   for having it in one place. Nothing below is speculative. */

/* ── SEARCH · four shapes, because they are four different jobs ─────────────
   The apps had all four and the system had one, so each app kept re-inventing
   the other three. */
/* 1 · Inline filter. Lives above a table and filters it live. */
.srch { display:flex; align-items:center; gap:var(--sp-2); padding:0 var(--sp-3); }
.srch svg { width:14px; height:14px; color:var(--subtle); flex-shrink:0;
  transition:color var(--d-fast) var(--ease-hover); }
.srch:focus-within svg { color:var(--accent-text); }
.srch input { flex:1; min-width:0; background:none; border:none; outline:none; line-height:1.2;
  font-family:var(--f-body); font-size:var(--fs-md); color:var(--text); padding:12px 0; }
.srch input::placeholder { color:var(--disabled); }
.srch .clr { width:20px; height:20px; border-radius:50%; display:none; place-items:center;
  color:var(--subtle); flex-shrink:0; }
.srch.has .clr { display:grid; }
.srch .clr:hover { color:var(--text); background:var(--hover); }
/* 2 · The result line under a live filter. Says what the search did. */
.srch-found { display:flex; align-items:center; gap:var(--sp-3); margin-top:var(--sp-2);
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--muted); }
.srch-found b { font-family:var(--f-num); font-weight:var(--num-wt); color:var(--text);
  font-variant-numeric:tabular-nums; }
/* 3 · Grouped picker: sticky category heads, a value on the right, and a way
       to add what is not there yet. A flat list cannot do a price column. */
.optgrp { position:sticky; top:0; z-index:1; background:var(--surface-raise);
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--subtle); padding:var(--sp-2) var(--sp-3) 5px; }
.optrow { display:flex; align-items:baseline; gap:var(--sp-3); width:100%; text-align:left;
  padding:9px var(--sp-3); border-radius:calc(var(--r-xs) * .8); font-family:var(--f-body);
  font-size:var(--fs-sm); color:var(--muted); }
.optrow:hover, .optrow.hi { background:var(--accent-tint); color:var(--text); }
.optrow .nm { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; }
.optrow .px { font-family:var(--f-num); font-weight:var(--num-wt); color:var(--text);
  font-variant-numeric:tabular-nums; flex-shrink:0; }
.optrow.none { color:var(--subtle); font-style:italic; pointer-events:none; }
.optrow.addnew { color:var(--accent-text); font-weight:600;
  border-top:1px solid var(--line); border-radius:0; margin-top:var(--sp-1); padding-top:var(--sp-3); }

/* ── FILTER BAR ── KAA and Darkroom each built this separately. */
.filters { display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap; }
.filters .well { flex:1 1 240px; min-width:0; }
.fchip { font-family:var(--f-ctrl); font-size:var(--fs-caps); font-weight:600; letter-spacing:.07em;
  text-transform:uppercase; line-height:1; color:var(--muted); background:none;
  border:1px solid var(--line-strong); border-radius:var(--r-pill); padding:8px 14px; cursor:pointer;
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover),
             border-color var(--d-fast) var(--ease-hover); }
.fchip:hover { color:var(--text); border-color:var(--ctrl-hi); }
.fchip.on { background:var(--accent-lit); color:var(--on-accent); border-color:transparent; }
[data-theme="light"] .fchip.on { color:var(--on-accent); }
.fchip .n { font-family:var(--f-num); font-weight:var(--num-wt); margin-left:6px; opacity:.75;
  font-variant-numeric:tabular-nums; }

/* ── DATE RANGE ── a start and an end, two months side by side, presets above.
   The single-month single-date .cal cannot express a range at all. */
.rangep { display:inline-block; }
.range-panel { background:var(--surface-raise); border-radius:var(--r); box-shadow:var(--e3);
  padding:var(--sp-4); width:max-content; max-width:100%; }
.range-presets { display:flex; flex-wrap:wrap; gap:var(--sp-1); padding-bottom:var(--sp-3);
  margin-bottom:var(--sp-3); border-bottom:1px solid var(--line); }
.range-months { display:flex; gap:var(--sp-5); }
@media (max-width:640px){ .range-months { flex-direction:column; gap:var(--sp-4); }}
.range-foot { display:flex; align-items:center; justify-content:space-between; gap:var(--sp-3);
  margin-top:var(--sp-4); padding-top:var(--sp-3); border-top:1px solid var(--line); }
.range-read { font-family:var(--f-num); font-weight:600; font-size:var(--fs-sm); color:var(--text);
  font-variant-numeric:tabular-nums; }
.range-read .sep { color:var(--subtle); margin:0 6px; }
.range-read .un { color:var(--subtle); font-family:var(--f-body); font-weight:400; }
/* The two ends are solid; everything between them is a continuous tinted band,
   so a range reads as one thing rather than a row of separate marks. */
.cal-grid span.r-in { background:var(--accent-tint); border-radius:0; color:var(--text); }
.cal-grid span.r-a { border-radius:var(--r-xs) 0 0 var(--r-xs); }
.cal-grid span.r-b { border-radius:0 var(--r-xs) var(--r-xs) 0; }
.cal-grid span.r-a, .cal-grid span.r-b { background:var(--accent-lit); color:var(--on-accent); box-shadow:var(--e2); }
[data-theme="light"] .cal-grid span.r-a, [data-theme="light"] .cal-grid span.r-b { color:var(--on-accent); }
.month-nav { display:inline-flex; align-items:center; gap:var(--sp-1); }
.month-nav b { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--text);
  min-width:112px; text-align:center; }

/* ── EXPORT ── five surfaces across two apps fired exports from bare buttons. */
.export .sp-menu button svg { width:13px; height:13px; flex-shrink:0; color:var(--subtle); }
.exform { display:grid; grid-template-columns:1fr 1fr; gap:var(--sp-4) var(--sp-5); }
@media (max-width:560px){ .exform { grid-template-columns:1fr; }}
.exform .ex-head { grid-column:1/-1; font-family:var(--f-ui); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--subtle); }
.exacts { grid-column:1/-1; display:flex; align-items:center; justify-content:space-between;
  gap:var(--sp-3); padding-top:var(--sp-4); border-top:1px solid var(--line); }

/* ── LINE ITEMS AND TOTALS ──
   KAA has the editor, Pulse has the cart. Same component at opposite ends of
   the estate, in neither case in the system. */
.lines { display:flex; flex-direction:column; gap:var(--sp-2); }
.line { display:grid; grid-template-columns:minmax(0,1fr) 92px 116px 132px auto; gap:var(--sp-3);
  align-items:center; padding:var(--sp-3); border-radius:var(--r-xs);
  background:var(--surface-deep); box-shadow:var(--e-sunk); }
@media (max-width:820px){ .line { grid-template-columns:1fr 1fr; } .line .l-desc { grid-column:1/-1; }}
.line .l-desc b { display:block; font-family:var(--f-body); font-size:var(--fs-md); font-weight:600; color:var(--text); }
.line .l-desc small { display:block; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); margin-top:3px; }
.line .num { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--text); text-align:right; font-variant-numeric:tabular-nums; }
.line .l-rm { width:28px; height:28px; border-radius:calc(var(--r-xs)*.8); display:grid;
  place-items:center; color:var(--subtle); }
.line .l-rm:hover { color:var(--danger-text); background:color-mix(in srgb, var(--danger) 12%, transparent); }
.line .l-rm svg { width:13px; height:13px; }
.totals { margin-left:auto; width:min(320px,100%); background:var(--surface-deep);
  border-radius:var(--r-xs); box-shadow:var(--e-sunk); padding:var(--sp-4); }
.totals .row { display:flex; align-items:baseline; justify-content:space-between; gap:var(--sp-4);
  padding:6px 0; font-family:var(--f-body); font-size:var(--fs-sm); color:var(--muted); }
.totals .row .v { font-family:var(--f-num); font-weight:var(--num-wt); color:var(--text);
  font-variant-numeric:tabular-nums; }
.totals .row.grand { margin-top:var(--sp-2); padding-top:var(--sp-3);
  border-top:1px solid var(--line-strong); color:var(--text); }
.totals .row.grand .v { font-size:var(--fs-lg); }
/* Reserved height, so a live figure appearing under a field cannot shift the
   layout under somebody's cursor. */
.f-calc { min-height:1.4em; font-family:var(--f-num); font-weight:600; font-size:var(--fs-caps);
  color:var(--muted); font-variant-numeric:tabular-nums; }
.f-calc.bad { color:var(--danger-text); }

/* ── FIGURE STRIP ── subgrid, so labels, values and notes align across every
   card no matter how long the content is. */
.figs { display:grid; grid-auto-flow:column; grid-auto-columns:minmax(150px,1fr);
  grid-template-rows:auto auto auto; gap:4px var(--sp-4); overflow-x:auto; }
.fig { display:grid; grid-row:1/4; grid-template-rows:subgrid; gap:4px; }
.fig .l { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--muted); }
.fig .v { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-2xl);
  color:var(--text); line-height:1.05; font-variant-numeric:tabular-nums; letter-spacing:-.01em; }
.fig .n { font-family:var(--f-body); font-size:var(--fs-caps); color:var(--subtle); }

/* ── ROW LIST ── all three apps built this. Name, sub, value, selectable. */
.rows-list { display:flex; flex-direction:column; }
/* NO HORIZONTAL PADDING. A row's padding is its hit height, not an indent, and
   15px of it on each side pushed the row's text 15px inboard of everything else
   in the same container while the divider underneath still spanned the full
   width. That is the whole fault: a rule wider than the text it sits under, and
   an invoice number that did not line up with the figure above it. Vertical
   padding stays, because that IS the target height. */
.rowitem { display:flex; align-items:center; gap:var(--sp-3); padding:var(--sp-3) 0;
  border-bottom:1px solid var(--line); cursor:pointer; text-align:left; width:100%;
  transition:background var(--d-fast) var(--ease-hover); }
.rowitem:last-child { border-bottom:none; }
/* The fill bleeds back out to where the padding used to be, so a row still
   reads as a band rather than a tight box around the words. */
.rowitem:hover { background:var(--hover); box-shadow:calc(var(--sp-3) * -1) 0 0 var(--hover),
  var(--sp-3) 0 0 var(--hover); }
.rowitem .rl { flex:1; min-width:0; }
.rowitem .rl b { display:block; font-family:var(--f-body); font-size:var(--fs-md);
  font-weight:600; color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.rowitem .rl small { display:block; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); margin-top:3px; }
.rowitem .rv { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--text); font-variant-numeric:tabular-nums; flex-shrink:0; }

/* ── TABLE ROW ACTIONS ── a fixed grid so the icons line up down the column
   whatever each row can actually do. A flex row wanders and looks broken. */
.acts { display:grid; grid-auto-flow:column; gap:2px; justify-content:end; }
.act { width:28px; height:28px; border-radius:calc(var(--r-xs)*.8); display:grid; place-items:center;
  color:var(--subtle); transition:color var(--d-fast) var(--ease-hover), background var(--d-fast) var(--ease-hover); }
.act:hover { color:var(--text); background:var(--hover); }
.act svg { width:14px; height:14px; }
.act.attn:hover { color:var(--danger-text); background:color-mix(in srgb, var(--danger) 12%, transparent); }
.act-gap { width:28px; }

/* ── NOTIFICATIONS ── KAA has one, Pulse builds it five times. */
.bell { position:relative; width:36px; height:36px; border-radius:var(--r-pill); display:grid;
  place-items:center; color:var(--muted); cursor:pointer;
  transition:color var(--d-fast) var(--ease-hover), background var(--d-fast) var(--ease-hover); }
.bell:hover { color:var(--text); background:var(--hover); }
.bell svg { width:17px; height:17px; }
.bell .n { position:absolute; top:3px; right:3px; min-width:15px; height:15px; padding:0 4px;
  border-radius:var(--r-pill); background:var(--danger); color:var(--on-accent); font-family:var(--f-num);
  font-weight:var(--num-wt); font-size:calc(var(--fs-caps) * .82); line-height:15px; text-align:center;
  font-variant-numeric:tabular-nums; box-shadow:0 0 0 2px var(--surface); }
.bell.quiet .n { display:none; }
.nlist { display:flex; flex-direction:column; gap:var(--sp-3); }
.nitem { padding:var(--sp-4); border-radius:var(--r-xs); background:var(--surface-deep);
  box-shadow:inset 3px 0 0 var(--nsev, var(--line-strong)); }
.nitem.urgent { --nsev:var(--danger); } .nitem.attention { --nsev:var(--warning); }
.nitem.info { --nsev:var(--info); }
.nitem .nsev { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--nsev, var(--muted)); }
.nitem b { display:block; font-family:var(--f-body); font-size:var(--fs-md); font-weight:600;
  color:var(--text); margin-top:5px; }
.nitem p { font-size:var(--fs-sm); color:var(--muted); margin-top:4px; }
.nitem .nact { display:flex; gap:var(--sp-2); justify-content:flex-end; margin-top:var(--sp-3); }
/* Handled items stay visible and recede. Removing them destroys the audit trail. */
.nitem.was { opacity:.55; box-shadow:inset 3px 0 0 var(--line-strong); }
.nitem.was .nact { display:none; }

/* ── COLLAPSIBLE TABLE GROUP ── a tbody that opens and closes. */
.t6 tbody.mgroup > tr.mhead td { background:var(--surface-deep); cursor:pointer;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:var(--ls-caps);
  text-transform:uppercase; color:var(--muted); }
.t6 tbody.mgroup > tr.mhead td .mchev { display:inline-block; width:9px; height:9px; margin-right:8px;
  transition:transform var(--d-base) var(--ease-spring); }
.t6 tbody.mgroup.open > tr.mhead td .mchev { transform:rotate(90deg); }
.t6 tbody.mgroup:not(.open) > tr:not(.mhead) { display:none; }

/* ── PROMPT COMPOSER ──
   Rebuilt again 23-08-2026. Two forms, because it lives in two places: FLOATING
   over a canvas, and DOCKED into a panel down the side of an app. Same component,
   one attribute apart.

   THE BAR LAW APPLIES HERE FIRST. Every control on the row is --ctrl-h tall and
   takes --bar-r. The previous cut had pill chips beside a rounded-rect key, which
   reads as two systems sharing a line. */
.composer { position:relative; border-radius:var(--r); padding:var(--sp-4);
  background:color-mix(in srgb, var(--surface-raise) 82%, transparent);
  -webkit-backdrop-filter:blur(28px) saturate(160%); backdrop-filter:blur(28px) saturate(160%);
  border:1px solid var(--glass-border);
  box-shadow:var(--e4), inset 0 1px 0 var(--glass-hi); }
.composer .ctop { display:flex; align-items:flex-start; gap:var(--sp-3); }
/* A square key at the row height, aligned to the first line of the prompt.
   Stretching it to the full height of a growing textarea turned it into a tall
   slab down the side, which is not what an "add a reference" affordance is. */
.composer .addref { width:var(--ctrl-h); height:var(--ctrl-h); flex-shrink:0;
  border-radius:var(--bar-r); display:grid; place-items:center; color:var(--subtle);
  background:var(--surface-deep); box-shadow:var(--e-sunk); cursor:pointer; align-self:flex-start;
  transition:color var(--d-fast) var(--ease-hover), background var(--d-fast) var(--ease-hover); }
.composer .addref:hover { color:var(--accent-text); background:var(--accent-tint); }
.composer .addref svg { width:15px; height:15px; }
.composer textarea { flex:1; min-width:0; min-height:calc(var(--ctrl-h) * 1.6); max-height:200px;
  resize:none; background:none; border:none; outline:none; padding:9px 2px; line-height:1.5;
  font-family:var(--f-body); font-size:var(--fs-md); color:var(--text); }
.composer textarea::placeholder { color:var(--disabled); }
.composer .cbot { display:flex; align-items:center; gap:var(--sp-2); flex-wrap:wrap;
  margin-top:var(--sp-4); padding-top:var(--sp-4); border-top:1px solid var(--line); }
.composer .cbot .sp { flex:1; min-width:0; }
.composer .cbot > * { height:var(--ctrl-h); }
.composer .cbot .btn-accent { padding:0 var(--sp-5); font-size:var(--fs-caps);
  border-radius:var(--bar-r); }

/* A chip that opens a value list. It shows what is SET; the setting's name is on
   hover. The value takes the accent, because it is the thing that changed. The
   chevron sits in a fixed slot so the label is not shunted off centre by it. */
.pchip { display:inline-flex; align-items:center; gap:9px; font-family:var(--f-ctrl);
  font-size:var(--fs-caps); font-weight:600; letter-spacing:.06em; text-transform:uppercase;
  /* --accent-text is accent TYPE on a surface, for a link or an accent figure.
     It was never a control label colour, and using it here is why the model and
     ratio keys read light red on this system and light blue on Royal Onyx. A key
     label is --text, the same as every other key on the bar. */
  line-height:1; color:var(--text); background:var(--ctrl); border-radius:var(--bar-r);
  padding:0 12px 0 14px; cursor:pointer; box-shadow:var(--e1); white-space:nowrap;
  transition:background var(--d-fast) var(--ease-hover), color var(--d-fast) var(--ease-hover); }
.pchip:hover { background:var(--ctrl-hi); color:var(--text); }
.pchip svg { width:9px; height:9px; color:var(--subtle); flex-shrink:0;
  transition:transform var(--d-base) var(--ease-spring); }
.pchip.open svg { transform:rotate(180deg); }

/* The stepper is the ONE exception to the bar law: a plus and a minus are glyph
   keys and are meant to read as small square controls. */
.composer .stepper { border-radius:var(--bar-r); background:var(--surface-deep);
  box-shadow:var(--e-sunk); overflow:hidden; }
.composer .stepper input { width:34px; font-size:var(--fs-sm); padding:0; }
.composer .stepper button { width:30px; font-size:var(--fs-md); }

/* ── DOCKED ──
   The same composer built into a panel rather than floating over a canvas. It
   loses the float, the blur and the corner on the side it is joined to, because
   it is now part of the wall rather than an object on top of it. */
[data-composer="docked"] .composer,
.composer.docked { border-radius:0; border:none; border-top:1px solid var(--line);
  background:var(--surface); -webkit-backdrop-filter:none; backdrop-filter:none;
  box-shadow:none; padding:var(--sp-4); }
.composer.docked .cbot { flex-wrap:wrap; }

/* A panel the composer docks into, for the demo and for any app that wants one. */
.sidepanel { display:flex; flex-direction:column; height:100%; min-height:0;
  background:var(--grad-ground); border-left:1px solid var(--line); }
.sidepanel .sp-head { flex-shrink:0; padding:var(--sp-4);
  border-bottom:1px solid var(--line); display:flex; align-items:center; gap:var(--sp-3); }
.sidepanel .sp-head b { flex:1; font-family:var(--f-head); font-size:var(--fs-md); font-weight:400;
  text-transform:uppercase; letter-spacing:var(--ls-head); }
.sidepanel .sp-scroll { flex:1; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-gutter:stable; padding:var(--sp-4); }

/* ── SPEND METER ── any metered app needs the balance where it can be seen. */
.spend { display:flex; align-items:center; gap:var(--sp-3); white-space:nowrap; }
.spend .m { width:96px; height:6px; border-radius:99px; background:var(--surface-deep);
  box-shadow:var(--e-sunk); overflow:hidden; }
.spend .m > i { display:block; height:100%; border-radius:99px; background:var(--accent-lit); }
.spend .v { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--text); font-variant-numeric:tabular-nums; }
.spend .v small { font-family:var(--f-ui); font-weight:600; font-size:var(--fs-caps);
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); margin-left:5px; }

/* ── MODAL VARIANTS ──
   A modal that scrolls needs its own architecture: a head that stays, a body
   that scrolls and does not chain to the page, and a gutter so the rounded
   corner does not clip the scrollbar. */
.modal.tall { display:flex; flex-direction:column; max-height:min(78vh, 720px); padding:0; }
/* THE HEAD AND FOOT RESERVE THE SAME SCROLLBAR GUTTER AS THE BODY.
   The body is the only part that scrolls, so it was the only part reserving a
   gutter, and every right edge inside it therefore sat 10px inboard of the head
   and the foot. That is why the status read further right than the figures and
   the amounts below it: not a padding mistake, a gutter only one of the three
   was paying for.

   scrollbar-gutter applies to any scroll container, and overflow:hidden makes
   one, so hidden plus stable reserves EXACTLY what the body reserves, in
   whatever browser, with no hardcoded scrollbar width to drift. Measured: a
   200px box gives 190px of content under both overflow:hidden and overflow:auto
   with stable, and 200px without. */
.modal.tall .m-head { flex-shrink:0; padding:var(--sp-5) var(--sp-5) var(--sp-4);
  border-bottom:1px solid var(--line); display:flex; align-items:center; gap:var(--sp-3);
  overflow:hidden; scrollbar-gutter:stable; }
.modal.tall .m-head h3 { margin:0; flex:1; min-width:0; font-size:var(--fs-md);
  text-wrap:balance; overflow-wrap:anywhere; }
.modal.tall .m-head .status { flex-shrink:0; }
.modal.tall .m-body { flex:1; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-gutter:stable; padding:var(--sp-5); }
.modal.tall .m-foot { flex-shrink:0; padding:var(--sp-4) var(--sp-5);
  border-top:1px solid var(--line); display:flex; align-items:center;
  overflow:hidden; scrollbar-gutter:stable;
  justify-content:flex-end; gap:var(--sp-2); }
.m-back { width:28px; height:28px; border-radius:calc(var(--r-xs)*.8); display:grid;
  place-items:center; color:var(--subtle); flex-shrink:0; }
.m-back:hover { color:var(--text); background:var(--hover); }
.m-back svg { width:14px; height:14px; }
/* A destructive confirm names the thing and asks twice. Colour is never the
   only signal here, because the status hue sits close to the brand accent. */
.modal.destructive .m-head { box-shadow:inset 0 3px 0 var(--danger); }
.confirm-name { font-family:var(--f-num); font-weight:var(--num-wt); color:var(--text);
  background:var(--surface-deep); border-radius:calc(var(--r-xs)*.7); padding:2px 7px; }
.btn-danger { background:none; border-color:var(--danger); color:var(--danger-text); }
.btn-danger:hover { background:color-mix(in srgb, var(--danger) 14%, transparent); }
.btn-danger.armed { background:var(--danger); color:var(--on-accent); border-color:transparent; }
.btn-danger:active { transform:scale(.98); transition-duration:calc(70ms * var(--dur)); box-shadow:var(--e-sunk); }

/* ═══ PRINT ═══════════════════════════════════════════════════════════════
   Chrome does not print background graphics by default, so a dark surface
   prints as nothing at all. Every export, statement and call sheet that leaves
   this system on paper goes to ink on white regardless of the theme on screen,
   and the chrome that only exists to navigate is dropped. V5 shipped without a
   print layer, which is a real bug the moment anyone hits Export PDF. */
@media print {
  :root, [data-theme="dark"], [data-theme="light"] {
    --bg:#FFFFFF; --surface:#FFFFFF; --surface-raise:#FFFFFF; --surface-deep:#F4F4F4; /* knox-allow: pure-white, this is the print sheet */
    --grad-ground:none; --text:#111111; --muted:#3A3A3A; --subtle:#5A5A5A; /* knox-allow: pure-white, paper is white */
    --line:#D8D8D8; --line-strong:#B4B4B4;
    --e0:none; --e1:none; --e2:none; --e3:none; --e4:none; --e5:none; --e-sunk:none;
    --extrude:none; --edge-ring:none; }
  body { background:#FFFFFF !important; color:#111111 !important; } /* knox-allow: pure-white, print */
  body::after, .grain { display:none !important; }
  .rail, .topb, .dock, .console, .c-open, .vtabs, .bulk, .pager, .filters, .composer,
  .drawer-demo, .scrim-demo, .cmdk, .range-presets, .range-foot { display:none !important; }
  .card, .t6-wrap, .kb-card, .modal, .filing, .acc { box-shadow:none !important;
    border:1px solid #D8D8D8 !important; background:#FFFFFF !important; break-inside:avoid; } /* knox-allow: pure-white, print */
  .card::before, .card::after, .t6-wrap::before, .kb-card::before, .modal::before { display:none !important; }
  .t6 th { background:#F4F4F4 !important; }
  .t6 tr, .frow, .tl-item, .nitem, .line { break-inside:avoid; }
  .tip::after { display:none !important; }}

/* ═══ THE REST OF THE INVENTORY ═══════════════════════════════════════════
   The ten components named in "Still to build". Built 23-08-2026 so the system
   is complete rather than complete-ish. */

/* ── DONUT, PROPERLY ──
   The ONE chart allowed more than one colour, because it splits a whole into
   named parts and a legend has to be matchable to a wedge. Every other chart
   stays one accent. Segments are conic, not stroked arcs: a stroked arc has to
   be told its own circumference, and a cap or a rounding error makes wedges
   overlap. A conic gradient cannot overlap by construction. */
/* Conic segments cannot overlap, which is why the ring is built this way. The
   soft ends come from a mask that rounds the whole band rather than from caps on
   individual arcs, which is what made the stroked version overrun itself. */
/* ── THE DONUT ──
   Drawn as SVG arcs rather than a conic gradient, because a gradient can only
   ever butt one colour against the next: hard radial seams, and the whole thing
   reads as a slice of pie that has been cut. Arcs can have round ends and sit
   apart from each other, which is what makes it read as a set of measurements
   rather than a wedge diagram.

   THE GAP MATHS IS THE WHOLE TRICK. A round cap adds HALF a stroke width beyond
   each end of the dash, so an arc of the naive length overruns its share by a
   full stroke width and overlaps its neighbour. That is exactly what broke the
   first attempt. Every arc therefore gives back one stroke width plus the gap,
   and starts half of each back from its true position, so the PAINTED arc
   occupies precisely its own percentage and nothing else. */
.ring { --ring-d:184px; --ring-band:23px; --ring-sw:12.5; --ring-c:251.327; --ring-gap:5;
  display:block; width:var(--ring-d); height:var(--ring-d); flex-shrink:0; overflow:visible; }
.ring circle { fill:none; transform:rotate(-90deg); transform-origin:50% 50%; }
.ring .trk { stroke:var(--ctrl-hi); stroke-width:calc(var(--ring-sw) * 1px); opacity:.34; }
.ring .seg { stroke:var(--sc, var(--chart-1)); stroke-width:calc(var(--ring-sw) * 1px);
  stroke-linecap:round;
  stroke-dasharray:max(.01px, calc((var(--to) - var(--from)) * var(--ring-c) / 100 * 1px
                       - var(--ring-gap) * 1px - var(--ring-sw) * 1px)) 999px;
  stroke-dashoffset:calc(-1px * (var(--from) * var(--ring-c) / 100
                       + var(--ring-gap) / 2 + var(--ring-sw) / 2)); }
.ring .seg.a { --sc:var(--chart-1); }
.ring .seg.b { --sc:var(--chart-2); }
.ring .seg.c { --sc:var(--chart-3); }
.ring::after { content:none; }
/* The figure sits in the hole, on its own surface, so it is read as a total and
   not as part of the band. */
.ringwrap { --ring-band:23px; position:relative; display:inline-grid; place-items:center; }
/* Capped to the hole, less a real margin, so the figure physically cannot touch
   the band however long the number gets. */
.ringwrap .mid { position:absolute; inset:0; display:grid; place-items:center; text-align:center;
  line-height:1.15; pointer-events:none; padding:calc(var(--ring-band, 23px) + 16px); }
.ringwrap .mid b { display:block; font-family:var(--f-num); font-weight:var(--num-wt);
  font-size:var(--fs-lg); color:var(--text); font-variant-numeric:tabular-nums;
  letter-spacing:-.015em; white-space:nowrap; }
.ringwrap .mid small { display:block; font-family:var(--f-ui); font-size:var(--fs-caps);
  font-weight:600; letter-spacing:var(--ls-caps); text-transform:uppercase;
  color:var(--muted); margin-top:3px; }
.ring-key { display:flex; flex-direction:column; gap:var(--sp-2); }
.ring-key .k { display:flex; align-items:baseline; gap:var(--sp-3); font-family:var(--f-body);
  font-size:var(--fs-sm); color:var(--muted); }
.ring-key .k i { width:9px; height:9px; border-radius:3px; flex-shrink:0; align-self:center;
  background:var(--kc, var(--accent-lit)); box-shadow:inset 0 0 0 1px rgba(0,0,0,.25); }
.ring-key .k .nm { flex:1; min-width:0; }
.ring-key .k .pc { font-family:var(--f-num); font-weight:var(--num-wt); color:var(--text);
  font-variant-numeric:tabular-nums; }
/* Three DISTINCT hues, matched to the legend. See --chart-1..3 above. */
.ring-key .k.a { --kc:var(--chart-1); }
.ring-key .k.b { --kc:var(--chart-2); }
.ring-key .k.c { --kc:var(--chart-3); }

/* ── IMAGE GRID WITH A DENSITY DIAL ── Darkroom ── */
.tiles { display:grid; grid-template-columns:repeat(var(--cols, 4), 1fr); gap:2px; }
.tile { display:block; position:relative; aspect-ratio:var(--tile-ar, 3/2); overflow:hidden; cursor:pointer;
  background:var(--surface-deep); }
.tile img, .tile .art { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; }
.tile .art { background:linear-gradient(150deg in oklab, var(--seed-accent-hi) 0%, var(--seed-accent-lo) 100%); }
.tile .vig { position:absolute; inset:0; background:linear-gradient(180deg, transparent 55%, rgba(0,0,0,.55) 100%);
  opacity:0; transition:opacity var(--d-fast) var(--ease-hover); }
.tile:hover .vig { opacity:1; }
.tile .stamp { position:absolute; left:7px; bottom:7px; z-index:1; opacity:0;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; letter-spacing:.06em;
  text-transform:uppercase; color:var(--on-accent); transition:opacity var(--d-fast) var(--ease-hover); }
.tile:hover .stamp { opacity:1; }
/* In flight: the art is there but dimmed, with the wait stated rather than implied. */
.tile.proc .art { opacity:.28; filter:saturate(.4); }
.tile.proc::after { content:""; position:absolute; inset:0;
  background:linear-gradient(100deg, transparent 20%, rgba(255,255,255,.07) 50%, transparent 80%);
  background-size:220% 100%; animation:shim calc(1400ms * var(--dur)) infinite; }
.tile .proch { position:absolute; left:7px; top:7px; z-index:2; display:flex; align-items:center;
  gap:7px; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.06em; text-transform:uppercase; color:var(--on-accent); }
.zoom { display:flex; align-items:center; gap:var(--sp-3); }
.zoom .sld { width:140px; }

/* ── MODEL SHEET ── Darkroom ── */
.sheet { border-radius:var(--r); padding:var(--sp-4); background:var(--surface-raise);
  box-shadow:var(--e4); }
.modgrid { display:grid; grid-template-columns:repeat(auto-fill, minmax(128px,1fr)); gap:var(--sp-3); }
/* display:block matters. These are spans, and aspect-ratio does nothing on an
   inline box, so the thumbnail collapsed to zero height and the card rendered as
   text only. */
.mod { display:block; border-radius:var(--r-xs); overflow:hidden; cursor:pointer;
  background:var(--surface-deep); box-shadow:var(--e-sunk);
  transition:box-shadow var(--d-fast) var(--ease-hover); }
.mod:hover { box-shadow:var(--e2); }
.mod.on { box-shadow:var(--e2); outline:2px solid var(--accent-lit); outline-offset:-2px; }
.mod .mthumb { display:block; aspect-ratio:4/3;
  background:linear-gradient(150deg in oklab, var(--seed-accent-hi) 0%, var(--seed-accent-lo) 100%); }
.mod .mbody { display:block; padding:9px 11px; }
.mod b { display:block; font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600; color:var(--text); }
.mod small { display:block; font-family:var(--f-num); font-weight:600; font-size:var(--fs-caps);
  color:var(--subtle); margin-top:2px; font-variant-numeric:tabular-nums; }

/* ── INGEST ARRIVAL ROW ── Darkroom ── */
.arr { display:grid; grid-template-columns:74px minmax(0,1fr) auto; gap:var(--sp-3); align-items:center;
  padding:var(--sp-3); border-radius:var(--r-xs); background:var(--surface-deep); box-shadow:var(--e-sunk); }
/* A 1fr column still refuses to shrink past its content, and a camera filename
   is long and unbreakable. minmax(0,1fr) lets it, and the name truncates rather
   than pushing the row wider than the page. */
.arr > * { min-width:0; }
.arr .th { display:block; aspect-ratio:3/2; border-radius:calc(var(--r-xs) * .7); background:var(--ctrl); }
.arr b { display:block; font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600;
  color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.arr small { display:block; font-family:var(--f-num); font-weight:600; font-size:var(--fs-caps);
  color:var(--subtle); margin-top:3px; font-variant-numeric:tabular-nums; }

/* ── SIDE DRAWER ── Pulse. 400px from the right, distinct from the bottom sheet. */
.sdrawer-demo { position:relative; height:260px; border-radius:var(--r); overflow:hidden;
  background:var(--grad-ground); box-shadow:var(--e2); }
.sdrawer-demo .scrim { position:absolute; inset:0; background:var(--scrim); opacity:0;
  pointer-events:none; -webkit-backdrop-filter:blur(2px); backdrop-filter:blur(2px);
  transition:opacity var(--d-base) ease; }
.sdrawer-demo.open .scrim { opacity:1; pointer-events:auto; }
.sdrawer { position:absolute; top:0; right:0; bottom:0; width:min(400px, 94%);
  background:var(--surface-raise); box-shadow:var(--e4); transform:translateX(100%);
  transition:transform var(--d-slow) var(--ease-entrance);
  display:flex; flex-direction:column; }
.sdrawer-demo.open .sdrawer { transform:none; }
.sdrawer .sd-head { flex-shrink:0; padding:var(--sp-4); border-bottom:1px solid var(--line);
  display:flex; align-items:center; gap:var(--sp-3); }
.sdrawer .sd-head b { flex:1; font-family:var(--f-head); font-size:var(--fs-md); font-weight:400;
  text-transform:uppercase; letter-spacing:var(--ls-head); }
.sdrawer .sd-body { flex:1; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-gutter:stable; padding:var(--sp-4); }

/* ── DETERMINATE IMPORT PROGRESS ── Pulse builds this six times ── */
.imp { display:flex; align-items:center; gap:var(--sp-3); }
.imp .bar { flex:1; min-width:0; }
.imp .n { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--text); font-variant-numeric:tabular-nums; white-space:nowrap; }
.imp .n small { font-family:var(--f-ui); font-weight:600; font-size:var(--fs-caps);
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); margin-left:5px; }

/* ── FACETED FILTER ── Darkroom ── */
.facets { display:grid; grid-template-columns:repeat(auto-fit, minmax(150px,1fr)); gap:var(--sp-4); }
.facet .fh { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:var(--ls-caps); text-transform:uppercase; color:var(--subtle);
  padding-bottom:var(--sp-2); margin-bottom:var(--sp-2); border-bottom:1px solid var(--line); }
.facet .fl { display:flex; flex-direction:column; gap:2px; }

/* ── MONTHLY BAR ROW ── Knox Artiste Accounts ── */
.barrow { display:grid; grid-template-columns:64px minmax(0,1fr) 110px; gap:var(--sp-3); align-items:center;
  padding:7px 0; }
.barrow .l { font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--muted); }
.barrow .v { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--text); text-align:right; font-variant-numeric:tabular-nums; }

/* ── FAILURE WITH A WAY OUT ──
   A banner states a problem. This one states it and offers the fix, which is
   what an error that a person can actually resolve should always do. */
.failure { display:flex; align-items:center; gap:var(--sp-4); padding:var(--sp-5);
  border-radius:var(--r-xs); background:var(--surface-deep);
  box-shadow:inset 3px 0 0 var(--danger); }
.failure .fx { flex:1; min-width:0; }
.failure b { display:block; font-family:var(--f-body); font-size:var(--fs-md); font-weight:600;
  color:var(--text); }
.failure p { font-size:var(--fs-sm); color:var(--muted); margin-top:4px; }

/* ── CURSOR-FOLLOWING TOOLTIP ──
   The CSS tip is anchored to its element, which is right almost everywhere and
   wrong on a dense row of icons, where the tip for the fourth icon lands under
   the third. This one follows the pointer. */
.ktip { position:fixed; z-index:var(--z-toast); pointer-events:none; white-space:nowrap;
  font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600; color:var(--text);
  background:var(--surface-raise); border-radius:calc(var(--r-xs) * .7); padding:5px 9px;
  box-shadow:var(--e3); opacity:0; transform:translate(-50%, -140%);
  transition:opacity calc(120ms * var(--dur)) ease; }
.ktip.on { opacity:1; }


/* ── MASONRY IMAGE GRID ──
   A generation grid holds 9:16, 3:4, 4:3 and 16:9 side by side. A fixed-ratio
   tile grid crops or letterboxes all of them to one shape, which is exactly what
   you do not want when the shape IS the output. Rows lock to a height and tiles
   pack along it, each keeping its own ratio, so nothing is cropped and no gaps
   are left. Justified rows, the way a contact sheet works. */
.mgrid { display:flex; flex-wrap:wrap; gap:3px; }
.mgrid .tile { display:block; position:relative; overflow:hidden; cursor:pointer;
  background:var(--surface-deep);
  /* flex-grow by aspect keeps every row full-width with no ragged right edge. */
  height:var(--row-h, 150px); flex-grow:var(--ar, 1.5); flex-basis:0;
  min-width:calc(var(--row-h, 150px) * var(--ar, 1.5) * .55); aspect-ratio:auto; }
.mgrid .tile > .art, .mgrid .tile > img { position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; }
.mgrid .tile .ratio { position:absolute; right:7px; top:7px; z-index:2; opacity:0;
  font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-caps);
  color:var(--on-accent); background:rgba(0,0,0,.5); border-radius:4px; padding:2px 6px;
  transition:opacity var(--d-fast) var(--ease-hover); }
.mgrid .tile:hover .ratio { opacity:1; }
/* Named ratios, so markup says 9:16 rather than 0.5625. */
.ar-9x16 { --ar:.5625; } .ar-3x4 { --ar:.75; } .ar-1x1 { --ar:1; }
.ar-4x3 { --ar:1.3333; } .ar-3x2 { --ar:1.5; } .ar-16x9 { --ar:1.7778; }
@media (max-width:600px){ .mgrid .tile { --row-h:104px; }}

/* ── CHECKOUT ──
   Three placements, one component. The cart is the same rows and the same totals
   whether it is a panel, a modal or inline: only where it sits changes. */
.cart { display:flex; flex-direction:column; min-height:0; }
.cart .cart-head { flex-shrink:0; display:flex; align-items:center; gap:var(--sp-3);
  padding-bottom:var(--sp-3); border-bottom:1px solid var(--line); }
.cart .cart-head b { flex:1; font-family:var(--f-head); font-size:var(--fs-md); font-weight:400;
  text-transform:uppercase; letter-spacing:var(--ls-head); }
.cart .cart-n { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-caps);
  min-width:20px; height:20px; padding:0 6px; border-radius:var(--r-pill);
  background:var(--accent-lit); color:var(--on-accent); display:grid; place-items:center;
  font-variant-numeric:tabular-nums; }
[data-theme="light"] .cart .cart-n { color:var(--on-accent); }
.cart .cart-body { flex:1; min-height:0; overflow-y:auto; overscroll-behavior:contain;
  scrollbar-gutter:stable; padding:var(--sp-3) 0; }
.cart .cart-foot { flex-shrink:0; padding-top:var(--sp-3); border-top:1px solid var(--line); }
.cartline { display:grid; grid-template-columns:52px minmax(0,1fr) auto; gap:var(--sp-3);
  align-items:center; padding:var(--sp-3) 0; border-bottom:1px solid var(--line); }
.cartline:last-child { border-bottom:none; }
.cartline .th { display:block; aspect-ratio:1; border-radius:calc(var(--r-xs) * .8);
  background:var(--ctrl); }
.cartline b { display:block; font-family:var(--f-body); font-size:var(--fs-sm); font-weight:600;
  color:var(--text); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.cartline small { display:block; font-family:var(--f-ui); font-size:var(--fs-caps); font-weight:600;
  letter-spacing:.08em; text-transform:uppercase; color:var(--subtle); margin-top:3px; }
.cartline .qty { display:flex; align-items:center; gap:var(--sp-2); }
.cartline .qty .stepper { height:30px; border-radius:var(--r-pill); background:var(--surface-deep);
  box-shadow:var(--e-sunk); overflow:hidden; }
.cartline .qty .stepper input { width:28px; font-size:var(--fs-caps); padding:0; }
.cartline .qty .stepper button { width:26px; font-size:var(--fs-sm); }
.cartline .amt { font-family:var(--f-num); font-weight:var(--num-wt); font-size:var(--fs-sm);
  color:var(--text); font-variant-numeric:tabular-nums; min-width:72px; text-align:right; }

/* ── THE ACCENT BAND, USED ──
   Knox called this the best read of the accent in the system, so it earns more
   than one appearance: a hero strip, and a surface for anything that should feel
   like a statement rather than a row. */
.accent-band.slim { padding:var(--sp-5); }
.accent-band .bandrow { display:flex; align-items:center; gap:var(--sp-5); flex-wrap:wrap; }
.accent-band .glass { flex:1; min-width:200px; }
.accent-band .fig .l { color:color-mix(in srgb, var(--on-accent) 72%, transparent); }
.accent-band .fig .v, .accent-band .fig .n { color:var(--on-accent); }
.accent-band .fig .n { opacity:.78; }
