/* ==========================================================================
   tokens.css — Brainweber AGP design tokens (CONTRACT §8)

   The only file in the system that names a colour, a size or a duration.
   Everything else composes these. Load order: this file first.

   Palette: graphite neutrals + a deep teal-green accent (#0F7A63 / #2FA98D).
   ONE accent family: the teal is the brand and it is also what a link is
   drawn in — a link is an affordance, not a meaning, so it takes the
   product's hue rather than a second one. Blue is reserved for `info`, which
   IS a meaning and must stay unlike the accent. Amber is reserved for
   `warning` — and therefore for the one non-deterministic step in the
   pipeline, the planner model call. No purple, violet, indigo or magenta
   exists here, in the semantic ramps or in the chart series. No glow, no
   neon, no gradient fill, no blurred chrome.

   Theme resolution, in cascade order:
     1. :root                                  light, always complete
     2. @media (prefers-color-scheme: dark)    dark, only when no explicit
        :root:not([data-theme="light"])        light override is set
     3. :root[data-theme="dark"]               explicit dark, wins by order
   ========================================================================== */

@layer reset, tokens, base, layout, components, chat, surfaces, utilities;

/* Type is the system stack, deliberately.
   There is no webfont here and no @font-face block: a self-hosted face is a
   real asset with a real cost, and the native UI face on each platform is a
   better default than downloading 100 KB of something that looks similar.

   To add Inter later: put InterVariable[-Italic].woff2 in static/fonts/, add
   the two @font-face blocks, and prepend Inter to --font-sans. Do all three or
   none of them. A @font-face pointing at a file that is not on disk makes
   collectstatic FAIL under CompressedManifestStaticFilesStorage -- it hashes
   and rewrites every url() it finds, and a dangling one is a hard error -- so
   the web container never becomes healthy. That is how this comment came to
   be written.
*/

@layer tokens {
  :root {
    color-scheme: light;

    /* ---- Type ------------------------------------------------------- */
    --font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
      "Helvetica Neue", Arial, sans-serif;
    --font-mono: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
      "Liberation Mono", monospace;
    --font-numeric: var(--font-mono);

    /* ---- Glassmorphic & Modern Surface Tokens ----------------------- */
    --glass-bg: rgba(255, 255, 255, 0.76);
    --glass-bg-hover: rgba(255, 255, 255, 0.9);
    --glass-border: rgba(226, 232, 240, 0.85);
    --glass-blur: 16px;
    --glass-shadow: 0 8px 32px rgba(15, 23, 42, 0.06);

    /* ---- Domain Accents (Automotive Copilot) ------------------------- */
    --domain-price: #10b981;
    --domain-price-subtle: #ecfdf5;
    --domain-price-border: #a7f3d0;
    --domain-price-text: #047857;

    --domain-supply: #6366f1;
    --domain-supply-subtle: #eef2ff;
    --domain-supply-border: #c7d2fe;
    --domain-supply-text: #4338ca;

    --domain-mileage: #06b6d4;
    --domain-mileage-subtle: #ecfeff;
    --domain-mileage-border: #a5f3fc;
    --domain-mileage-text: #0e7490;

    --domain-aging: #f59e0b;
    --domain-aging-subtle: #fffbeb;
    --domain-aging-border: #fde68a;
    --domain-aging-text: #b45309;

    --domain-sold: #14b8a6;
    --domain-sold-subtle: #f0fdfa;
    --domain-sold-border: #99f6e4;
    --domain-sold-text: #0f766e;

    --domain-market: #8b5cf6;
    --domain-market-subtle: #f5f3ff;
    --domain-market-border: #ddd6fe;
    --domain-market-text: #6d28d9;

    --text-xs: 0.75rem;      /* 12 */
    --text-sm: 0.8125rem;    /* 13 */
    --text-base: 0.875rem;   /* 14 — the app's body size */
    --text-md: 0.9375rem;    /* 15 */
    --text-lg: 1.0625rem;    /* 17 */
    --text-xl: 1.25rem;      /* 20 */
    --text-2xl: 1.5625rem;   /* 25 */
    --text-3xl: 1.9375rem;   /* 31 */

    --lh-xs: 1rem;           /* 16 */
    --lh-sm: 1.25rem;        /* 20 */
    --lh-base: 1.375rem;     /* 22 */
    --lh-md: 1.5rem;         /* 24 */
    --lh-lg: 1.625rem;       /* 26 */
    --lh-xl: 1.75rem;        /* 28 */
    --lh-2xl: 2rem;          /* 32 */
    --lh-3xl: 2.375rem;      /* 38 */

    --weight-regular: 400;
    --weight-medium: 500;
    --weight-semibold: 600;
    --weight-bold: 700;
    --tracking-caps: 0.04em;  /* the only letter-spacing in the system */

    /* ---- Space ------------------------------------------------------ */
    --space-05: 2px;   /* hairline inset: segmented track, switch padding */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 20px;
    --space-6: 24px;
    --space-8: 32px;
    --space-10: 40px;
    --space-12: 48px;
    --space-16: 64px;

    /* ---- Radius -----------------------------------------------------
       Three roles, and the name says which is which. A screen used to carry
       six corner scales at once (4, 6, 8, 12, 20 and 999 in one view), which
       is six answers to a question that has three:

         --radius-control  something you click, type into, or that wraps a
                           label: buttons, inputs, chips, badges, menu items,
                           swatches, the small inline marks in prose.
         --radius-card     something that FRAMES content: answer cards, the
                           question bubble, dialogs, popovers, the composer.
         --radius-pill     fully round: pills, dots, switches, avatars.

       --radius-shell is not a fourth scale. It is the one concentric outer
       corner in the product — the composer's dashed frame — and it is
       DERIVED, so the two curves stay parallel whatever the card radius
       becomes: an outer corner is the inner one plus the gap between them. */
    --radius-control: 6px;
    --radius-card: 8px;
    --radius-pill: 999px;
    --radius-shell: calc(var(--radius-card) + var(--space-2));

    /* ---- Fixed sizes ------------------------------------------------
       A control height is not chrome: it is the box its own text sits in, so
       it cannot be a bare pixel while `data-scale` re-roots the em. Measured
       at XL (20px root) a `.input--sm` was 28px tall around a 29.5px line box
       — `--lh-base` (1.375rem = 27.5px) plus its two 1px borders — and at
       compact XL it was 24px around the same 29.5px. The text was taller than
       the control.

       `max(px, rem)` is the whole fix and it moves nothing at the default
       root: 1.75rem IS 28px at 16px. The pixel is a FLOOR, so choosing small
       text never shrinks a fingertip target below what it is today; the rem is
       what grows, so at L and XL the box grows with the type it holds.
       `--icon-*` stay pixels on purpose — a glyph is a mark, not a text box. */
    --control-h-sm: max(28px, 1.75rem);
    --control-h-md: max(34px, 2.125rem);
    --control-h-lg: max(40px, 2.5rem);
    --icon-xs: 12px;
    --icon-sm: 14px;
    --icon-md: 16px;
    --icon-lg: 20px;
    --size-dot: 6px;
    --size-node: 10px;        /* trace / timeline rail node */
    --focus-ring-width: 2px;
    --focus-ring-offset: 2px;

    /* ---- Shell metrics ---------------------------------------------- */
    --sidebar-w: 280px;
    --topbar-h: 52px;
    --content-max: 1240px;
    /* Long-form prose outside the transcript — page subtitles, the chat's
       opening lede. Counted in the same `ch` as `--prose-measure` below, and
       read the note there before retuning either: 60ch is ≈72 RENDERED
       characters, not 60. At 68ch this lede was running 89–98 characters. */
    --measure: 60ch;
    --chat-measure: 58rem;    /* transcript column */
    --composer-max-h: 40vh;

    /* ---- Reading (the transcript) -----------------------------------
       The answer is the page's content, not its chrome, so it is set one step
       above the UI ladder with a looser leading than any label wears. Every
       value here is rem, ch or a token — never px — because `data-scale`
       re-roots the em and a reading rhythm pinned in pixels stops being a
       rhythm at S and XL.

       Why the measure is in `ch`: it is proportional to the answer's OWN
       face, so it survives all four text scales and the serif that
       `data-face` can swap in, neither of which a rem ceiling would follow.

       Why it is NOT a character count, which this comment used to claim:
       `1ch` is the advance of the glyph "0", and in -apple-system a zero is
       far wider than the average letter of running English. Measured against
       the rendered transcript, `1ch` buys **1.20 rendered characters**, so
       the old `74ch` was setting lines of 82–95 characters (mean 89) — half
       again the band it was supposed to hold. The value below is therefore
       the target character count DIVIDED by that conversion:

           60ch × 1.20 ≈ 72 rendered characters, inside the 68–75 band.

       Re-derive the 1.20 if the face ever changes; it is a property of the
       face, not of this token. It is still a ceiling, not a width: a column
       narrower than this is left alone.

       Density is absent on purpose for `--prose-*`: compact tightens space,
       never type, and the rhythm of a paragraph is type. It does tighten the
       gaps BETWEEN turns and blocks, which are chrome — see the compact block
       further down. */
    --text-answer: 1rem;      /* 16 at scale M — one step above the UI */
    --lh-answer: 1.625rem;    /* 26 — a 1.625 ratio, what a long answer wants */
    --prose-measure: 60ch;    /* ≈72 rendered characters — see the note above */
    --prose-flow: 1rem;       /* between blocks inside one answer */
    --quote-measure: 54ch;    /* how wide a quoted question may run */
    --turn-gap: 2.5rem;       /* between one exchange and the next */
    --answer-gap: 1.25rem;    /* prose → cards → sources, one gap for all three */

    /* One border language for everything an answer carries: the trace above
       it, every answer card inside it, the code it quotes. Two names so a
       retune is one edit rather than thirty. */
    --artifact-line: var(--border-subtle);
    --artifact-radius: var(--radius-card);

    /* ---- Motion ----------------------------------------------------- */
    --dur-fast: 120ms;
    --dur-base: 180ms;
    --dur-slow: 260ms;
    /* Decelerating, never overshooting. Nothing in this product bounces. */
    --ease-out: cubic-bezier(0.2, 0, 0, 1);
    --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);

    /* ---- Elevation -------------------------------------------------- */
    --z-base: 0;
    --z-sticky: 10;
    --z-dropdown: 100;
    --z-drawer: 300;
    --z-modal: 400;
    --z-popover: 500;
    --z-toast: 600;
    --z-tooltip: 700;

    /* ---- Surfaces --------------------------------------------------- */
    --bg-canvas: #f6f7f8;
    --bg-surface: #ffffff;
    --bg-raised: #ffffff;
    --bg-sunken: #eef0f2;
    --bg-inset: #e3e7e9;
    --bg-hover: #f1f3f4;
    --bg-active: #e6e9eb;
    /* The selected row/nav ground IS the accent's subtle wash — it was a
       second hand-picked value 1–2 units away from `--accent-subtle`, which
       is two answers to one question and, worse, a value `data-accent` never
       overrode: on the ocean or ember accent the whole product turned blue or
       rust and every selected row stayed teal. Declared as the alias so it
       follows whichever accent is set. */
    --bg-selected: var(--accent-subtle);
    --bg-quote: #e9ecee;      /* the ground under a quoted question */
    --bg-overlay: rgba(16, 22, 26, 0.44);

    /* ---- Ink -------------------------------------------------------- */
    --fg-primary: #1b2125;
    --fg-secondary: #475158;
    /* Quiet, not weak. #6b747b measured 4.44:1 on the canvas and 4.17:1 on
       the sunken ground — under AA for the body copy and the table headers
       that spend it — while the DARK theme's muted ink sat at 5.96:1. Light
       was the weaker theme by a whole step, which is backwards: dark is the
       forgiving one. #5c656c is the same graphite two steps down and passes
       everywhere it lands (canvas 5.54, surface 5.94, sunken 5.20, inset
       4.78), which puts the two themes within half a step of each other. */
    --fg-muted: #5c656c;
    /* The decorative step, and it was decorative to the point of illegible:
       #949ca3 measured 2.24–2.78:1 across the nine grounds in light and
       2.77–4.02:1 in dark, so it was under the 3:1 floor WCAG sets for large
       text and for non-text marks on EVERY light ground — while carrying 64
       real uses, among them a person's role beside their name, a fact's `dt`,
       a citation's kind and the correlation id on an error page.

       It cannot reach 4.5:1 without becoming `--fg-muted`: the darkest value
       that clears AA on the canvas is ~6.4 ΔE from muted, less than the 8.4
       between muted and secondary, so the ramp would fold. #767f87 is the
       value that clears 3:1 on all nine grounds of BOTH themes (worst 3.27
       light on `--bg-inset`, 3.29 dark on `--bg-active`) and still stands
       10.5 ΔE off muted in light — wider than the 8.4 between muted and
       secondary — and 8.4 in dark, which is the bar the avatar set is held to.

       One value for both themes, which is unusual here and deliberate: a
       mid-graphite is the only ink that can be 3:1 DOWN onto a near-white and
       3:1 UP off a near-black, and the requirement has exactly one answer. It
       is still restated in both dark blocks so every token in this file
       resolves the same way on all three theme paths. */
    --fg-faint: #767f87;
    --fg-inverted: #ffffff;

    /* ---- Lines ------------------------------------------------------
       One hairline, and a second only where a stronger rule is MEANT. Three
       neutral lines used interchangeably is what put 40 hairlines of one
       grey, 12 of a second and 7 of a third on a single screen — cards drawn
       in one, the composer in another, twelve pixels apart.

         --border-subtle   THE hairline. Every resting 1px line on a reading
                           surface: card frames, dividers, rails, the
                           composer, the transcript's rules.
         --border-default  The deliberate second. Only where a line must be
                           seen against a ground the element does not share:
                           the floating panel recipe (modal / dialog /
                           popover / account menu), the edge of a bare form
                           control, whose box IS its affordance, and the one
                           rule that has to be heavier than the hairlines
                           around it — a table's head-to-body seam and its
                           daybreak rows.
         --border-strong   Not a hairline at all — the edge of a MARK (the
                           trace node), the state a hover or an incognito
                           switch moves a line TO, and the dashed "this is
                           not being kept" chrome. */
    --border-subtle: #eceef0;
    --border-default: #dce0e3;
    --border-strong: #c1c8cd;
    --border-focus: #0f7a63;

    /* ---- Accent ----------------------------------------------------- */
    --accent: #0f7a63;
    --accent-hover: #0c6a56;
    --accent-pressed: #095949;
    --accent-subtle: #e6f2ef;
    --accent-border: #b9dbd2;
    --accent-text: #0d6a56;
    --accent-fg: #ffffff;

    /* ---- Semantics --------------------------------------------------
       `-fg` is the ink drawn ON the solid fill. `-text` is the ink for that
       meaning drawn on `-subtle` or on the canvas. Both are needed: a solid
       danger button and a danger badge do not share a foreground. */
    --success: #1b7f3b;
    --success-subtle: #e6f4ea;
    --success-border: #b5ddc2;
    --success-text: #146030;
    --success-fg: #ffffff;

    --warning: #b5760a;
    --warning-subtle: #fbf1de;
    --warning-border: #e8d09a;
    --warning-text: #8a5804;
    /* Amber is the one fill in the ramp white cannot sit on: #ffffff on
       #b5760a measures 3.78:1, under AA, which is what `.badge--solid-warning`
       would have shipped. The dark theme already answers this correctly with
       a near-black ink on its brighter amber; light takes the same answer,
       darker to suit the darker fill — 4.75:1. Success, danger, info and the
       accent keep white, which passes on all four. */
    --warning-fg: #201502;

    --danger: #b42318;
    --danger-subtle: #fdecea;
    --danger-border: #f0bfb9;
    --danger-text: #97200f;
    --danger-fg: #ffffff;

    --info: #14639e;
    --info-subtle: #e7f0f8;
    --info-border: #b6d2e8;
    --info-text: #10527f;
    --info-fg: #ffffff;

    /* Links are the brand hue, not a second one. A teal product whose links
       render blue is two accent families on one screen: the reader learns
       "teal means this product" from the chrome and then "blue means you can
       click it" from the prose, and neither generalises. `--info` keeps the
       blue — an informational badge is a MEANING and has to stay unlike the
       accent — and a link is not a meaning, it is an affordance.

       Declared once, as the accent's own text weight: `--accent-text` is
       already the per-theme value tuned to read on a light or dark ground
       (6.5:1 light, 7.5:1 dark), so both themes follow from this one line. */
    --link: var(--accent-text);
    --link-hover: var(--accent-hover);

    /* ---- Chart series ------------------------------------------------
       Eight categorical colours, ordered so that adjacent indices differ in
       BOTH hue family (cool / warm / neutral) and relative luminance. That
       ordering is what keeps 1–4 legible under deuteranopia, where the
       red–green axis collapses and only the blue–yellow axis and lightness
       survive. Weakest pair is 2 vs 6 (amber / green) — beyond four series,
       charts.js must also vary dash or marker shape, not colour alone.

       A series is data, not a state, so none of the eight may BE a semantic
       colour. Light keeps its amber 3.4 ΔE clear of `--warning`; dark's was
       the identical hex — a chart's second line drawn in the one colour the
       system reserves for "this step is not deterministic" — so the dark
       `--series-2` now carries light's relationship instead: the lighter,
       more chromatic amber, 6.0 ΔE clear, and further from `--series-1` and
       `--series-3` in lightness than it was. */
    --series-1: #0f7a63;  /* teal — brand */
    --series-2: #c07c16;  /* amber */
    --series-3: #1f5fa0;  /* blue */
    --series-4: #a94427;  /* rust */
    --series-5: #3e93a6;  /* cyan */
    --series-6: #4c8a3c;  /* green */
    --series-7: #5a666e;  /* graphite */
    --series-8: #2e363b;  /* ink */

    /* ---- Avatar identity ---------------------------------------------
       The eight names in `identity.models.AVATAR_COLORS`, one per person and
       kept forever (`pick_avatar_color` hashes the email). Identity is not
       status and not data, so this family is deliberately its own: every
       value sits at LOWER chroma and DEEPER value than both the semantic
       ramps and the chart series, and the nearest state token is ≥8 ΔE away
       in both themes — a monogram cannot be misread as a success, a warning
       or a refusal. `--avatar-slate` and `--series-7` are both "the
       graphite" and land near each other on purpose; a chart line and a
       person's initials never share a context.

       Nothing paints these raw. `.avatar` in components.css mixes each into
       a 16% wash for the ground and a 72% blend for the ink, which is what
       makes one token serve both themes; the pairs that come out measure
       5.30–7.09:1, so the initials clear AA on every one. No purple, violet,
       indigo or magenta: the eight hues run 9°–211°. */
    --avatar-teal:  #0b6b70;
    --avatar-slate: #5d666c;
    --avatar-moss:  #5a7c33;
    --avatar-amber: #916511;
    --avatar-blue:  #365f8d;
    --avatar-clay:  #a25a4c;
    --avatar-sand:  #877552;
    --avatar-rust:  #8f4520;

    /* ---- Shadows (graphite-tinted, never coloured) -------------------- */
    --shadow-xs: 0 1px 2px rgba(16, 22, 26, 0.06);
    --shadow-sm: 0 1px 2px rgba(16, 22, 26, 0.06), 0 2px 5px rgba(16, 22, 26, 0.05);
    --shadow-md: 0 2px 4px rgba(16, 22, 26, 0.05), 0 8px 18px rgba(16, 22, 26, 0.09);
    --shadow-lg: 0 4px 10px rgba(16, 22, 26, 0.07), 0 22px 44px rgba(16, 22, 26, 0.14);

    /* ---- Scrollbars -------------------------------------------------- */
    --scrollbar-thumb: #c8ced2;
    --scrollbar-thumb-hover: #aab2b8;
    --scrollbar-track: transparent;
    --scrollbar-size: 10px;

    /* ---- Control glyphs ----------------------------------------------
       Two mechanisms, deliberately:
       · `--icon-*` are COLOURED data URIs used as `background-image` on form
         controls, which cannot carry a pseudo-element reliably. One is left:
         the select caret. (`--icon-check` and `--icon-dash` went with the
         `.checkbox` recipe they were drawn for — the product's checkable
         controls are `.toggle` and the native inputs in the composer's
         settings popover, and neither paints a background glyph.)
       · `--mask-*` are UNCOLOURED data URIs used as `mask-image` on pseudo-
         elements with `background-color: currentColor`, so they theme for
         free. Prefer these everywhere a pseudo-element is available.
       Real product iconography is inline <svg> in the template — never a
       font, never an emoji. */
    /* One value for both themes was the claim; measured, it was not true.
       #8b9399 is 3.12:1 on the select's own white ground in light and 2.73
       on the sunken ground a disabled one takes, against 5.39 in dark — a
       UI-component indicator (WCAG 1.4.11 wants 3:1) sitting on the floor
       in one theme and comfortable in the other, which is the same "light
       is the weaker theme" defect the ink ramp had. The caret is a muted-ink
       mark, so it is now each theme's own `--fg-muted` graphite, inlined:
       5.94:1 light, 5.51:1 dark. A data URI cannot spend a var(), so the
       hex is written twice and the dark blocks restate it. */
    --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%235c656c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");

    --mask-chevron-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");
    --mask-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5 6.5 11.5 12.5 4.5'/%3E%3C/svg%3E");
    --mask-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round'%3E%3Cpath d='m4 4 8 8M12 4l-8 8'/%3E%3C/svg%3E");
    --mask-sort: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 6.5 3-3 3 3M5 9.5l3 3 3-3'/%3E%3C/svg%3E");
    --mask-sort-asc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 9.5 3.5-3.5 3.5 3.5'/%3E%3C/svg%3E");
    --mask-sort-desc: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4.5 6.5 3.5 3.5 3.5-3.5'/%3E%3C/svg%3E");
    --mask-arrow-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8h9M9 4.5 12.5 8 9 11.5'/%3E%3C/svg%3E");
    --mask-search: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.6' stroke-linecap='round'%3E%3Ccircle cx='7' cy='7' r='4'/%3E%3Cpath d='m10 10 3 3'/%3E%3C/svg%3E");
  }

  /* System-preference dark. Guarded so an explicit light choice always wins. */
  @media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
      color-scheme: dark;

      --glass-bg: rgba(22, 28, 36, 0.8);
      --glass-bg-hover: rgba(30, 38, 48, 0.9);
      --glass-border: rgba(55, 65, 81, 0.7);
      --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);

      --domain-price: #34d399;
      --domain-price-subtle: rgba(16, 185, 129, 0.16);
      --domain-price-border: rgba(16, 185, 129, 0.35);
      --domain-price-text: #6ee7b7;

      --domain-supply: #818cf8;
      --domain-supply-subtle: rgba(99, 102, 241, 0.16);
      --domain-supply-border: rgba(99, 102, 241, 0.35);
      --domain-supply-text: #a5b4fc;

      --domain-mileage: #22d3ee;
      --domain-mileage-subtle: rgba(6, 182, 212, 0.16);
      --domain-mileage-border: rgba(6, 182, 212, 0.35);
      --domain-mileage-text: #67e8f9;

      --domain-aging: #fbbf24;
      --domain-aging-subtle: rgba(245, 158, 11, 0.16);
      --domain-aging-border: rgba(245, 158, 11, 0.35);
      --domain-aging-text: #fcd34d;

      --domain-sold: #2dd4bf;
      --domain-sold-subtle: rgba(20, 184, 166, 0.16);
      --domain-sold-border: rgba(20, 184, 166, 0.35);
      --domain-sold-text: #5eead4;

      --domain-market: #a78bfa;
      --domain-market-subtle: rgba(139, 92, 246, 0.16);
      --domain-market-border: rgba(139, 92, 246, 0.35);
      --domain-market-text: #c4b5fd;

      --bg-canvas: #121618;
      --bg-surface: #191e21;
      --bg-raised: #212729;
      --bg-sunken: #0e1214;
      --bg-inset: #0a0d0f;
      --bg-hover: #212729;
      --bg-active: #293034;
      --bg-selected: var(--accent-subtle);
      /* A quoted question lifts off the dark canvas rather than sinking into
         it — the same graphite step `--border-subtle` names here. */
      --bg-quote: #232a2e;
      --bg-overlay: rgba(0, 0, 0, 0.62);

      --fg-primary: #e9edef;
      --fg-secondary: #b4bdc3;
      --fg-muted: #8b959c;
      --fg-faint: #767f87;
      --fg-inverted: #0e1214;

      --border-subtle: #232a2e;
      --border-default: #2e363b;
      --border-strong: #414a50;
      --border-focus: #3fbfa0;

      --accent: #2fa98d;
      --accent-hover: #3dbb9d;
      --accent-pressed: #249480;
      --accent-subtle: #10312a;
      --accent-border: #1e5449;
      --accent-text: #4cc0a3;
      --accent-fg: #04211b;   /* bright teal needs dark ink, not white */

      --success: #4fbe77;
      --success-subtle: #0f2a18;
      --success-border: #1f4d30;
      --success-text: #7bd39a;
      --success-fg: #052b12;

      --warning: #e0a64b;
      --warning-subtle: #33260d;
      --warning-border: #5c451a;
      --warning-text: #f0c37c;
      --warning-fg: #2b1d03;

      --danger: #e5726a;
      --danger-subtle: #3a1412;
      --danger-border: #5f2723;
      --danger-text: #f2a49c;
      --danger-fg: #2a0a06;

      --info: #5aaee8;
      --info-subtle: #0d2437;
      --info-border: #1c4363;
      --info-text: #8ecaf2;
      --info-fg: #041b2b;


      --series-1: #35b394;
      --series-2: #e8b45c;
      --series-3: #6ba5e5;
      --series-4: #e58163;
      --series-5: #63c6dc;
      --series-6: #7fc468;
      --series-7: #9eabb3;
      --series-8: #dce3e8;

      /* Identity, lifted for the dark ground — same eight hues, raised in
         value so the 72% ink blend stays 5.68:1 or better. */
      --avatar-teal:  #3fb5b0;
      --avatar-slate: #9aa3a9;
      --avatar-moss:  #8fbc5f;
      --avatar-amber: #d3a256;
      --avatar-blue:  #7fa8d6;
      --avatar-clay:  #dd9284;
      --avatar-sand:  #c6b083;
      --avatar-rust:  #cf7c4e;

      --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
      --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 5px rgba(0, 0, 0, 0.32);
      --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.38), 0 8px 18px rgba(0, 0, 0, 0.46);
      --shadow-lg: 0 4px 10px rgba(0, 0, 0, 0.42), 0 22px 44px rgba(0, 0, 0, 0.56);

      --scrollbar-thumb: #333c41;
      --scrollbar-thumb-hover: #465158;

      /* The caret is the dark theme's own muted graphite — see the light
         block's note; a data URI cannot spend a var(). */
      --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238b959c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");

    }
  }

  /* Explicit dark. Same specificity as the guarded media block above, so it
     must stay after it — source order is what decides. */
  :root[data-theme="dark"] {
    color-scheme: dark;

    --glass-bg: rgba(22, 28, 36, 0.8);
    --glass-bg-hover: rgba(30, 38, 48, 0.9);
    --glass-border: rgba(55, 65, 81, 0.7);
    --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);

    --domain-price: #34d399;
    --domain-price-subtle: rgba(16, 185, 129, 0.16);
    --domain-price-border: rgba(16, 185, 129, 0.35);
    --domain-price-text: #6ee7b7;

    --domain-supply: #818cf8;
    --domain-supply-subtle: rgba(99, 102, 241, 0.16);
    --domain-supply-border: rgba(99, 102, 241, 0.35);
    --domain-supply-text: #a5b4fc;

    --domain-mileage: #22d3ee;
    --domain-mileage-subtle: rgba(6, 182, 212, 0.16);
    --domain-mileage-border: rgba(6, 182, 212, 0.35);
    --domain-mileage-text: #67e8f9;

    --domain-aging: #fbbf24;
    --domain-aging-subtle: rgba(245, 158, 11, 0.16);
    --domain-aging-border: rgba(245, 158, 11, 0.35);
    --domain-aging-text: #fcd34d;

    --domain-sold: #2dd4bf;
    --domain-sold-subtle: rgba(20, 184, 166, 0.16);
    --domain-sold-border: rgba(20, 184, 166, 0.35);
    --domain-sold-text: #5eead4;

    --domain-market: #a78bfa;
    --domain-market-subtle: rgba(139, 92, 246, 0.16);
    --domain-market-border: rgba(139, 92, 246, 0.35);
    --domain-market-text: #c4b5fd;

    --bg-canvas: #121618;
    --bg-surface: #191e21;
    --bg-raised: #212729;
    --bg-sunken: #0e1214;
    --bg-inset: #0a0d0f;
    --bg-hover: #212729;
    --bg-active: #293034;
    --bg-selected: var(--accent-subtle);
    --bg-quote: #232a2e;
    --bg-overlay: rgba(0, 0, 0, 0.62);

    --fg-primary: #e9edef;
    --fg-secondary: #b4bdc3;
    --fg-muted: #8b959c;
    --fg-faint: #767f87;
    --fg-inverted: #0e1214;

    --border-subtle: #232a2e;
    --border-default: #2e363b;
    --border-strong: #414a50;
    --border-focus: #3fbfa0;

    --accent: #2fa98d;
    --accent-hover: #3dbb9d;
    --accent-pressed: #249480;
    --accent-subtle: #10312a;
    --accent-border: #1e5449;
    --accent-text: #4cc0a3;
    --accent-fg: #04211b;

    --success: #4fbe77;
    --success-subtle: #0f2a18;
    --success-border: #1f4d30;
    --success-text: #7bd39a;
    --success-fg: #052b12;

    --warning: #e0a64b;
    --warning-subtle: #33260d;
    --warning-border: #5c451a;
    --warning-text: #f0c37c;
    --warning-fg: #2b1d03;

    --danger: #e5726a;
    --danger-subtle: #3a1412;
    --danger-border: #5f2723;
    --danger-text: #f2a49c;
    --danger-fg: #2a0a06;

    --info: #5aaee8;
    --info-subtle: #0d2437;
    --info-border: #1c4363;
    --info-text: #8ecaf2;
    --info-fg: #041b2b;


    --series-1: #35b394;
    --series-2: #e8b45c;
    --series-3: #6ba5e5;
    --series-4: #e58163;
    --series-5: #63c6dc;
    --series-6: #7fc468;
    --series-7: #9eabb3;
    --series-8: #dce3e8;

    --avatar-teal:  #3fb5b0;
    --avatar-slate: #9aa3a9;
    --avatar-moss:  #8fbc5f;
    --avatar-amber: #d3a256;
    --avatar-blue:  #7fa8d6;
    --avatar-clay:  #dd9284;
    --avatar-sand:  #c6b083;
    --avatar-rust:  #cf7c4e;

    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 5px rgba(0, 0, 0, 0.32);
    --shadow-md: 0 2px 4px rgba(0, 0, 0, 0.38), 0 8px 18px rgba(0, 0, 0, 0.46);
    --shadow-lg: 0 4px 10px rgba(0, 0, 0, 0.42), 0 22px 44px rgba(0, 0, 0, 0.56);

    --scrollbar-thumb: #333c41;
    --scrollbar-thumb-hover: #465158;

    --icon-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238b959c' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6.5 4 4 4-4'/%3E%3C/svg%3E");

  }

  /* Explicit light needs no palette — bare :root already is light. It only
     has to restore the UA colour scheme the media query would have flipped. */
  :root[data-theme="light"] {
    color-scheme: light;
  }

  /* Compact density tightens space, never type. Set on <html> from the
     account menu; persisted server-side on User preferences. */
  :root[data-density="compact"] {
    --space-1: 4px;
    --space-2: 6px;
    --space-3: 10px;
    --space-4: 12px;
    --space-5: 16px;
    --space-6: 18px;
    --space-8: 24px;
    --space-10: 30px;
    --space-12: 36px;
    --space-16: 48px;

    /* The compact ramp is a floor too, and a floor is what `max()` makes it —
       see the note on the stock heights. Left as bare pixels, `--control-h-sm`
       held a 24px box around a 26.8px line box at L and a 29.5px one at XL. */
    --control-h-sm: max(24px, 1.5rem);
    --control-h-md: max(30px, 1.875rem);
    --control-h-lg: max(36px, 2.25rem);

    --topbar-h: 46px;
    --sidebar-w: 250px;

    /* The two transcript gaps that are chrome rather than type: the space
       between turns and the space between an answer's stacked regions. The
       reading tokens (`--prose-*`, `--lh-answer`) are deliberately absent —
       compact tightens space, never type. */
    --turn-gap: 2rem;
    --answer-gap: 1rem;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   Preference attributes — the settings dialog's half of the token contract.

   Every control in Settings and in Developer options writes exactly one
   attribute on <html>; every visual consequence lives HERE as a token
   override. Nothing in the component stylesheets knows these attributes
   exist, which is what makes each control safe: it can restyle the product
   but it cannot restructure it. All of them are per-browser preferences
   (localStorage, applied by base.html's no-FOUC bootstrap), same as the
   theme has always been.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer tokens {
  /* ── Text size (data-scale) ── the rem root; type scales, chrome holds.
     Steps are two pixels of root, ±12.5% and +25% — the first version used
     one-pixel steps, which "worked" in the computed styles and did nothing a
     person could actually see. A setting nobody can perceive is a setting
     that does not work. */
  :root[data-scale="s"]  { font-size: 14px; }
  :root[data-scale="l"]  { font-size: 18px; }
  :root[data-scale="xl"] { font-size: 20px; }

  /* ── Answer font (data-face) ── the prose of answers only, never the UI. */
  :root { --font-prose: var(--font-sans); }
  :root[data-face="serif"] {
    --font-prose: Charter, Georgia, Cambria, "Times New Roman", serif;
  }

  /* ── Motion (data-motion="off") ── the person's explicit no, honoured the
     same way prefers-reduced-motion is. */
  :root[data-motion="off"] *,
  :root[data-motion="off"] *::before,
  :root[data-motion="off"] *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }

  /* ── Colour scheme (data-scheme) ── the greys, never the brand colour.
     Cool leans blue-grey, warm leans stone; both defined for light and dark. */
  :root[data-scheme="cool"] {
    --bg-canvas: #f7f9fb; --bg-surface: #fbfdfe; --bg-sunken: #eef2f6;
    --border-subtle: #e6ebf1; --border-default: #d5dde5; --border-strong: #b9c5d1;
  }
  :root[data-scheme="warm"] {
    --bg-canvas: #faf9f7; --bg-surface: #fdfcfb; --bg-sunken: #f3f1ed;
    --border-subtle: #edeae4; --border-default: #dfdad2; --border-strong: #c8c1b5;
  }
  @media (prefers-color-scheme: dark) {
    :root[data-scheme="cool"]:not([data-theme="light"]) {
      --bg-canvas: #10161c; --bg-surface: #151c23; --bg-sunken: #0b1116;
      --border-subtle: #1f2934; --border-default: #2a3641; --border-strong: #3c4b58;
    }
    :root[data-scheme="warm"]:not([data-theme="light"]) {
      --bg-canvas: #17140f; --bg-surface: #1d1a14; --bg-sunken: #110f0b;
      --border-subtle: #2a251d; --border-default: #363026; --border-strong: #4c4433;
    }
  }
  :root[data-theme="dark"][data-scheme="cool"] {
    --bg-canvas: #10161c; --bg-surface: #151c23; --bg-sunken: #0b1116;
    --border-subtle: #1f2934; --border-default: #2a3641; --border-strong: #3c4b58;
  }
  :root[data-theme="dark"][data-scheme="warm"] {
    --bg-canvas: #17140f; --bg-surface: #1d1a14; --bg-sunken: #110f0b;
    --border-subtle: #2a251d; --border-default: #363026; --border-strong: #4c4433;
  }

  /* ── Developer options: design morphism ─────────────────────────────────
     Not in production surfaces — reachable only through the admin's
     Developer options dialog, but harmless anywhere: tokens only. */

  /* Brand accent (data-accent). Teal is the brand and the default.
     `--border-focus` is part of this family and was the one member the two
     variants forgot: with ocean or ember set, every ring the keyboard drew
     stayed teal while the rest of the product was blue or rust — 1944 of the
     2916 attribute combinations. It is declared as the accent itself in
     light and as `--accent-hover` in dark, which is the same one-step lift
     the stock dark ring already has over the stock dark accent. */
  :root[data-accent="ocean"] {
    --accent: #1d63d8; --accent-hover: #1957bf; --accent-pressed: #154aa3;
    --accent-subtle: #e8effb; --accent-border: #bcd2f2; --accent-text: #1a56b8;
    --accent-fg: #ffffff;
    --border-focus: var(--accent);
  }
  :root[data-accent="ember"] {
    --accent: #b34e0f; --accent-hover: #9d440d; --accent-pressed: #86390b;
    --accent-subtle: #f9ece2; --accent-border: #ecc9ab; --accent-text: #a04a10;
    --accent-fg: #ffffff;
    --border-focus: var(--accent);
  }
  @media (prefers-color-scheme: dark) {
    :root[data-accent="ocean"]:not([data-theme="light"]) {
      --accent: #5b93ec; --accent-hover: #6ea1ef; --accent-pressed: #4a83e2;
      --accent-subtle: #12233d; --accent-border: #24406b; --accent-text: #7cacf1;
      --accent-fg: #061224;
      --border-focus: var(--accent-hover);
    }
    :root[data-accent="ember"]:not([data-theme="light"]) {
      --accent: #e08948; --accent-hover: #e69a61; --accent-pressed: #d67a35;
      --accent-subtle: #33200f; --accent-border: #5c3a1d; --accent-text: #eda76f;
      --accent-fg: #241203;
      --border-focus: var(--accent-hover);
    }
  }
  :root[data-theme="dark"][data-accent="ocean"] {
    --accent: #5b93ec; --accent-hover: #6ea1ef; --accent-pressed: #4a83e2;
    --accent-subtle: #12233d; --accent-border: #24406b; --accent-text: #7cacf1;
    --accent-fg: #061224;
    --border-focus: var(--accent-hover);
  }
  :root[data-theme="dark"][data-accent="ember"] {
    --accent: #e08948; --accent-hover: #e69a61; --accent-pressed: #d67a35;
    --accent-subtle: #33200f; --accent-border: #5c3a1d; --accent-text: #eda76f;
    --accent-fg: #241203;
    --border-focus: var(--accent-hover);
  }

  /* Corners (data-corners). Soft is the default ramp. Two roles to move, not
     four scales: `--radius-pill` stays round at every setting and
     `--radius-shell` re-derives itself from the card. */
  :root[data-corners="sharp"] { --radius-control: 3px; --radius-card: 4px; }
  :root[data-corners="round"] { --radius-control: 10px; --radius-card: 14px; }

  /* Rule weight (data-rules). Hairline fades the lines, strong asserts them. */
  :root[data-rules="hairline"] { --border-subtle: #f3f4f6; --border-default: #e9ecee; --border-strong: #d5dade; }
  :root[data-rules="strong"]   { --border-subtle: #dce0e3; --border-default: #c1c8cd; --border-strong: #9aa4ab; }
  @media (prefers-color-scheme: dark) {
    :root[data-rules="hairline"]:not([data-theme="light"]) { --border-subtle: #1b2125; --border-default: #232a2e; --border-strong: #323a40; }
    :root[data-rules="strong"]:not([data-theme="light"])   { --border-subtle: #2e363b; --border-default: #414a50; --border-strong: #5a656d; }
  }
  :root[data-theme="dark"][data-rules="hairline"] { --border-subtle: #1b2125; --border-default: #232a2e; --border-strong: #323a40; }
  :root[data-theme="dark"][data-rules="strong"]   { --border-subtle: #2e363b; --border-default: #414a50; --border-strong: #5a656d; }

  /* Elevation (data-elev). Flat removes the shadows; lifted doubles down —
     across the whole ramp, and per theme: a theme-blind override would beat
     the dark palette by source order and render lifted FAINTER than dark
     stock, so lifted carries the same dark-guarded pair scheme, accent and
     rule weight do. */
  :root[data-elev="flat"] { --shadow-xs: none; --shadow-sm: none; --shadow-md: none; --shadow-lg: none; }

  /* All four steps, in every one of the three blocks. `--shadow-sm` was the
     one lifted forgot — `flat` zeroed four and `lifted` raised three — so the
     two surfaces that spend it, the composer at `:focus-within` and an
     interactive card on hover, were the only things in the product that
     answered a person's elevation preference in one direction and ignored it
     in the other. It also folded the ramp: lifted-xs and stock-sm are within
     a fifth of a step of each other, which is two names for one shadow. */
  :root[data-elev="lifted"] {
    --shadow-xs: 0 1px 3px rgba(16, 22, 26, 0.10);
    --shadow-sm: 0 1px 3px rgba(16, 22, 26, 0.10), 0 4px 9px rgba(16, 22, 26, 0.09);
    --shadow-md: 0 4px 8px rgba(16, 22, 26, 0.08), 0 14px 28px rgba(16, 22, 26, 0.14);
    --shadow-lg: 0 8px 18px rgba(16, 22, 26, 0.12), 0 32px 64px rgba(16, 22, 26, 0.22);
  }
  @media (prefers-color-scheme: dark) {
    :root[data-elev="lifted"]:not([data-theme="light"]) {
      --shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.5);
      --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5), 0 4px 9px rgba(0, 0, 0, 0.42);
      --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.46), 0 14px 28px rgba(0, 0, 0, 0.55);
      --shadow-lg: 0 8px 18px rgba(0, 0, 0, 0.5), 0 32px 64px rgba(0, 0, 0, 0.68);
    }
  }
  :root[data-theme="dark"][data-elev="lifted"] {
    --shadow-xs: 0 1px 3px rgba(0, 0, 0, 0.5);
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5), 0 4px 9px rgba(0, 0, 0, 0.42);
    --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.46), 0 14px 28px rgba(0, 0, 0, 0.55);
    --shadow-lg: 0 8px 18px rgba(0, 0, 0, 0.5), 0 32px 64px rgba(0, 0, 0, 0.68);
  }
}
