/* ============================================================
   Subconscious Design System — derived canon.

   The extended token layer that builds on the `contract.css` pair:
   theme-aware grey ramp, motion tokens, signal red, radii, rule
   weights, focus rings, blur, element-level typography defaults,
   utility classes, and the two-register product-scale overrides.

   This file used to be `tokens.css`. Splitting the two responsibilities
   makes contract.css copy-pasteable (no imports, no element styling)
   while keeping the full preview canon a one-line import.

   Import order in a consuming surface:
     @import "./fonts.css";       // public Google Fonts (SIL-OFL)
     @import "./contract.css";    // the four-variable contract + chart palette + type scale
     @import "./derived.css";     // everything below

   Apps that copy only `contract.css` get the lean contract. Apps that
   want the editorial canon (every preview page, every slide, the kit's
   index.css) import this file *after* `contract.css`.
   ============================================================ */

/* Fonts — public stack (resolves #4). Three SIL-OFL families load from
   Google Fonts: Inter Tight (display + body), IBM Plex Mono (meta/code),
   Redaction (editorial italic). Exposure / Söhne Mono remain first in
   the font-family stacks below as licensed upgrades for consumers that
   load them privately. font-display=optional matches `fonts.css`. */
@import url("./fonts.css");

/* ---------- DERIVED COLOR TOKENS ----------
   The contract pair (--darkColor / --lightColor / --color /
   --backgroundColor) lives in contract.css. Everything below derives
   from var(--color) and var(--backgroundColor) via color-mix(), so
   each derived token retunes itself when data-theme or data-mode
   changes. No per-mode overrides needed.

   The legacy --n-0..--n-1000 neutral scale was deleted on 2026-05-04
   because it was static hex (didn't theme-track). Use the contract pair
   and color-mix() blends instead:
     --backgroundColor   instead of --n-0
     --hair              instead of --n-100  (color-mix var(--color) 12% transparent)
     --grey-300          instead of --n-200, --n-300
     --grey-500          instead of --n-400, --n-500
     --grey-700          instead of --n-600, --n-700, --n-800
     --color             instead of --n-900, --n-1000
   A CI test (tests/no-static-greys.test.mjs) now fails on any --n-X reference. */

:root {
  /* Foreground / background aliases ----------------------------- */
  --fg-1: var(--color);                                                /* primary ink          */
  --fg-2: color-mix(in srgb, var(--color) 70%, var(--backgroundColor));/* secondary ink       */
  --fg-3: color-mix(in srgb, var(--color) 45%, var(--backgroundColor));/* tertiary / captions */
  --fg-4: color-mix(in srgb, var(--color) 25%, var(--backgroundColor));/* disabled / hairline */

  --bg-1: var(--backgroundColor);
  --bg-2: color-mix(in srgb, var(--color) 3%, var(--backgroundColor));  /* section tint  */
  --bg-3: color-mix(in srgb, var(--color) 6%, var(--backgroundColor));  /* card on tint  */
  --hairline: color-mix(in srgb, var(--color) 15%, transparent);
  --border:   color-mix(in srgb, var(--color) 40%, transparent);

  /* Greys derived from the contract pair (theme-aware) ---------- */
  --grey-300: color-mix(in srgb, var(--color) 30%, var(--backgroundColor));
  --grey-500: color-mix(in srgb, var(--color) 50%, var(--backgroundColor));
  --grey-700: color-mix(in srgb, var(--color) 70%, var(--backgroundColor));
  --hair:     color-mix(in srgb, var(--color) 12%, transparent);
  --red:      #c8102e;                  /* signal red — gap only, never themed */

  /* Canonical primitive furniture ------------------------------- */
  --rule-strong: 3px solid var(--color);
  --rule: 1px solid var(--color);
  --rule-muted: 1px solid var(--hair);
  --radius-sharp: 0;
  --radius-control: 2px;
  --radius-dot: 999px;
  --focus-ring: 2px solid var(--color);
  --focus-ring-destructive: 2px solid var(--red);
}

/* ---------- TYPE-RAMP NOTES ----------
   The contract pins five sizes in px (--fs-display 64 / -h1 40 / -h2 28 /
   -body 16 / -meta 12). The preview garden cards also use a fixed-px
   working ramp for their specimen layouts; #313 collapsed ~35 ad-hoc
   sizes onto 12 steps:

     9 · 10 · 12 · 15 · 18 · 22 · 28 · 36 · 44 · 56 · 72 · 96

   New preview CSS should land on a step of this ramp (or a --fs-*
   token). EXCEPTION: deliberate one-off display specimens — the single
   "violently large" headline a page is built around — may exceed the
   ramp (108-300px). These are page-local, never reused, and intentional
   per doctrine rule 1. Current specimens: type-system 280/300, doctrine
   240/260, brand-hero 168, and the 108/110/120/144 page heroes.

   Letter-spacing — Bringhurst's counterpoint principle (Elements of
   Typographic Style §3.2) — lives in `contract.css` alongside the type
   scale (--tracking-display / -h1 / -h2 / -body / -meta / -caps /
   -caps-l). One legacy alias the contract did not pick up:
   --tracking-wordmark. Kept here for the lowercase wordmark. */

:root {
  /* The wordmark is a one-off; the contract carries the rest of the
     tracking scale (per-size + legacy aliases). */
  --tracking-wordmark: -0.06em;   /* lowercase wordmark — special case        */

  /* Weight scale carries one extra over the contract: --fw-heading.
     Only hits when using the Inter fallback (Exposure has no bolds);
     preserved here because preview specimens still reach for it. */
  --fw-heading:   600;

  /* Fluid type scale — overrides the contract's pinned px sizes.
     The contract is pinned px (for app build determinism); the
     editorial preview garden and slides want fluid clamp() so
     hero type scales with the viewport. Five canonical sizes,
     same names as contract.css, fluid bounds:

       clamp(min_px, preferred_vw, max_vw)

     Importing `derived.css` AFTER `contract.css` swaps the type
     scale to fluid for the preview surfaces. Apps that copy only
     `contract.css` keep the pinned px scale unchanged. */
  --fs-display: clamp(44px, 5.4vw, 5.4vw);   /* hero / cover           */
  --fs-h1:      clamp(36px, 3.5vw, 3.5vw);   /* primary heading        */
  --fs-h2:      clamp(28px, 2.4vw, 2.4vw);   /* section heading        */
  --fs-body:    clamp(15px, 1.1vw, 1.1vw);   /* body default           */
  --fs-meta:    clamp(12px, 0.78vw, 0.78vw); /* labels, mono captions  */
}

/* ---------- SEMANTIC ELEMENT MAPPINGS ----------
   These style raw tags so prototypes inherit the system automatically. */

html { -webkit-text-size-adjust: 100%; }

body {
  background-color: var(--backgroundColor);
  color: var(--color);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--tracking-body);
  font-weight: var(--fw-light);
  -webkit-font-smoothing: antialiased;
}

/* Raw h1-h6 selectors are wrapped in :where() so their specificity
   drops to 0,0,0 — any consumer Tailwind utility class (specificity
   0,1,0) beats them cleanly. Picked up from #407 when derived.css
   absorbed tokens.css's element-level styling. The preview garden's
   bare <h1>-<h6> still inherit DS sizing because no consumer rules
   compete in that surface. Refs #405. */
:where(h1, h2, h3, h4, h5, h6) {
  font-family: var(--font-display);
  font-weight: var(--fw-light);
  letter-spacing: var(--tracking-h1);
  line-height: var(--lh-display);
}

/* Element mappings collapse to the 5-size canonical scale and pull
   the per-size optical tracking. h1 is display-tight; h2 sits at
   h1-tight; h3/h4 sit at h2-tight (mid-scale collapsed by design);
   h5/h6 are uppercase mono and use --tracking-caps for breathing room. */
:where(h1) { font-size: var(--fs-display); font-weight: var(--fw-light); letter-spacing: var(--tracking-display); }
:where(h2) { font-size: var(--fs-h1);      font-weight: var(--fw-light); letter-spacing: var(--tracking-h1); }
:where(h3) { font-size: var(--fs-h2);      font-weight: var(--fw-light); letter-spacing: var(--tracking-h2); }
:where(h4) { font-size: var(--fs-h2);      font-weight: var(--fw-regular); letter-spacing: var(--tracking-h2); }
:where(h5) { font-size: var(--fs-body);    font-weight: var(--fw-regular); text-transform: uppercase; letter-spacing: var(--tracking-caps); }
:where(h6) { font-size: var(--fs-body);    font-weight: var(--fw-regular); text-transform: uppercase; letter-spacing: var(--tracking-caps); }

p      { font-size: var(--fs-body); line-height: var(--lh-body); max-width: 58ch; }
small  { font-size: var(--fs-body); line-height: var(--lh-loose); color: var(--fg-3); }
code, pre, kbd, samp { font-family: var(--font-mono); font-size: 0.92em; letter-spacing: -0.01em; }

/* Utility classes ------------------------------------------ */
.kicker      { font-family: var(--font-mono); font-size: var(--fs-meta); text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--fg-3); }
.meta        { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--fg-2); }
.comment     { font-family: var(--font-mono); font-size: var(--fs-meta); color: var(--fg-3); opacity: 0.7; }
.comment::before { content: "// "; }
.tabular     { font-variant-numeric: tabular-nums; }
.lowercase-wordmark { font-family: var(--font-display); font-weight: var(--fw-display); letter-spacing: var(--tracking-wordmark); text-transform: lowercase; }

/* Link treatment lives in preview/_base.css (fuzzy-at-rest, sharp-on-hover). */

/* Selection */
::selection { background: var(--color); color: var(--backgroundColor); }

/* ---------- EDITORIAL POLISH (raw-element overrides) ----------
   Absorbed from preview/_base-editorial.css (#407) when derived.css
   took over the element-level styling responsibility from tokens.css.
   These rules touch unscoped browser elements (h1/h2/h3, p, body),
   so they ride alongside the other raw-element rules above.

   Wrapped in :where() where applicable so consumer classes win on
   specificity — preview garden's bare tags still inherit. */

/* Headings get balance — each line ends up roughly equal length.
   Avoids the 'orphan last word' awkwardness in display type. */
:where(h1, h2, h3, .head, .hero h1) {
  text-wrap: balance;
}

/* Body paragraphs get pretty — optimized for prose, cheaper than balance. */
:where(p, .lede, .cap, .body) {
  text-wrap: pretty;
}

/* Long-form prose: hang opening quotes and dashes outside the column.
   Classic editorial typesetting; subtle but unmistakable. */
:where(.lede, .cap) {
  hanging-punctuation: first last;
}

/* Tabular numerals everywhere a number renders — consistent column alignment
   for stats, prices, dates, source citations. */
:where(.num, .v, .n, [data-counter],
.proof-row .n, .src, .chart-src) {
  font-variant-numeric: tabular-nums;
}

/* optimizeLegibility activates kerning + ligatures consistently. */
body {
  text-rendering: optimizeLegibility;
  -moz-osx-font-smoothing: grayscale;
}

/* ---------- RADII, BLUR, MOTION ----------
   Spacing scale lives in contract.css as --sp-1..--sp-8 (4 / 8 / 12 /
   16 / 24 / 32 / 48 / 64 px, pinned). The motion vocabulary (ease + dur)
   is the system-wide knob set — preview/_base.css redeclares richer
   motion engine tokens (--ease-causal, --dur-base) on top of these. */
:root {
  /* Radii — editorial paper, no soft corners. --radius-pill kept
     for the single red dot on the .label kicker.                        */
  --radius-pill: 999px;

  /* Motion — mostly eases, rarely springy. */
  --ease-out:     cubic-bezier(0.25, 0.8, 0.25, 1);
  --ease-in-out:  cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast:     200ms;
  --dur-med:      300ms;
  --dur-slow:     1100ms;  /* entrances, draw-ins, strikes — one knob */

  /* Blur — used in protection gradients, chrome menus, and link hover */
  --blur-sm: 0.8px;
  --blur-md: 6px;
  --blur-lg: 12px;
}

/* ============================================================
   Two-register scale system

   The editorial doctrine ("one thing violently large, one thing
   uncomfortably small") is correct for marketing surfaces, decks,
   case studies, and the preview garden — the type runs hot, the
   motion runs slow, the rules read as authority. It's wrong for
   product UI, where the user reads-then-acts dozens of times in a
   session and wants visual consistency, not contrast.

   Two registers:

     editorial (default)  hot type, slow motion, dramatic register
                          --fs-display: 64px, --dur-slow: 1100ms
                          surfaces: landing, doctrine, color-system,
                          type-system, brand-hero, decks, case studies

     product              calmer type, faster motion, dense register
                          --fs-display: 28–32px, --dur-slow: 400ms
                          surfaces: chat, settings, dashboards, tables,
                          forms — anywhere the user repeats actions

   Apply via `<html data-scale="product">` or `<div data-scale="product">`.
   No attribute = editorial. Same colors. Same fonts. Same primitives.
   The vocabulary is the same; the intensity drops one tier.

   Why no `--accent` token in product mode (per design decision):
   adding a non-red accent is what makes systems look like every other
   AI/SaaS design system. The two-color contract is a doctrinal
   commitment — black-to-white as cause-to-effect. Product surfaces
   that need a "primary action" indicator use full-ink fills
   (var(--color) on the contract bg) and rely on placement + size
   for hierarchy, not color. Hold the line. */
[data-scale="product"] {
  /* Type — pinned upper bounds, no fluid scaling above the threshold.
     Heading hierarchy still has a 1.25–1.5x ratio between steps so
     "kill the middle weight" still applies — just at a calmer volume. */
  --fs-display: clamp(26px, 3vw, 32px);   /* product hero / page title */
  --fs-h1:      clamp(20px, 2.2vw, 24px); /* primary heading           */
  --fs-h2:      clamp(17px, 1.7vw, 19px); /* section heading           */
  --fs-body:    clamp(14px, 1vw, 15px);   /* body default              */
  --fs-meta:    clamp(11px, 0.75vw, 12px);/* labels, mono captions     */

  /* Motion — discrete state changes, not editorial entrances. */
  --dur-fast:   150ms;  /* hover / focus feedback */
  --dur-med:    220ms;  /* state changes, sweeps  */
  --dur-slow:   400ms;  /* entrances, settles     */
}
