/* design-system: page-library v1 | tokens.css */
/* Token sheet for the page library: three tiers, and nothing else.
 *
 * This banner deliberately does not name the file. The fleet linter treats a first line that
 * names tokens.css/base.css as a COPY of the canonical fleet system, which this is not: it is a
 * second vocabulary with its own names. Claim "copy" and the linter correctly demands it match
 * the fleet, and 212 findings follow. See NOTES.md, "A fork, not a copy".

WHY THREE TIERS
Tier 1 is raw value. Tier 2 is a role a product may use. Tier 3 is a component's own
decision. A component may read tier 2 or 3 and never tier 1, which is what stops a page
from picking a specific grey. The build fails on a tier-1 read outside this file.

WHY THE NAMES ARE ROLES, NOT HUES
`--ok` survives a rebrand and `--green-600` does not. The only hue-named tokens here are
tier 1, which nothing outside this file may use.

WHY HEX AND NOT OKLCH
oklch() is the better authoring space and it is Baseline. It is not here yet because the
contrast gate parses hex, and a token layer the gate cannot measure is a token layer the
gate cannot protect. That is a stated trade, not an oversight: see NOTES.md, D3.
*/

:root {
  /* ── tier 1: primitive ─────────────────────────────────────────────────── */
  --gray-0:#ffffff;   --gray-25:#f7f7f8;  --gray-50:#f2f2f4;  --gray-100:#e9e9ed;
  --gray-200:#e6e6e9; --gray-300:#d4d4d8; --gray-400:#a1a1aa; --gray-500:#71717a;
  --gray-600:#52525b; --gray-700:#3f3f46; --gray-900:#18181b; --gray-950:#111114;
  --indigo-50:#eef0ff; --indigo-500:#4f46e5; --indigo-600:#4338ca;
  --green-50:#eaf6ee;  --green-600:#15803d;  --green-200:#cfe7d8;
  --amber-50:#fdf4e7;  --amber-700:#b45309;  --amber-200:#f0dcc0;
  --red-50:#fdecec;    --red-700:#b91c1c;    --red-200:#f2cccc;

  /* The terminal chrome, deliberately outside the theme: a console is dark on a light page and on a
     dark one, so these are primitives that no theme re-points. They live here because a literal in
     components.css is a literal, even when it is the right one. */
  --term-bg:#0c0c0f;   --term-bar:#14141a;   --term-line:#1f1f25;  --term-line2:#23232b;
  --term-lamp:#2e2e38; --term-ink:#d6d6db;   --term-dim:#8b8b96;
  --term-ok:#7ee2a8;   --term-warn:#e2c07e;  --term-alert:#f08c8c; --term-info:#8ab4f8;

  /* type: one ladder, ten steps, roles not ratios */
  /* his verdict 2026-10-22: the 11px mono micro was hard to read, so micro is 12px sans.
     Mono is for values (code, kbd, addresses), never a whole step below --fs-sm. */
  --fs-micro:12px;  --fs-label:12px; --fs-sm:13px;  --fs-base:14px; --fs-prose:16px;
  --fs-h3:18px;     --fs-h2:22px;    --fs-h1:28px;  --fs-display:40px; --fs-hero:52px;
  --lh-tight:1.12;  --lh-body:1.45;  --lh-prose:1.6;
  --tracking-tight:-.02em; --tracking-hero:-.03em;

  /* space: a ladder of eight, 4px base, nothing between */
  --space-1:4px;  --space-2:8px;  --space-3:12px; --space-4:16px;
  --space-5:24px; --space-6:32px; --space-7:48px; --space-8:64px;

  --radius-sm:6px; --radius:10px; --radius-lg:14px; --radius-box:16px; --radius-pill:999px;
  --border:1px;

  /* motion: four durations, three curves */
  --dur-0:.01ms; --dur-1:90ms; --dur-2:150ms; --dur-3:220ms; --dur-4:320ms;
  /* the ladder, not arbitrary z: a raw z-index is how a dropdown ends up under a modal */
  --z-1:1; --z-2:10; --z-3:50; --z-4:100; --z-5:1000;
  --ease-out:cubic-bezier(.2,0,.1,1);
  --ease-in-out:cubic-bezier(.4,0,.2,1);
  --ease-accelerate:cubic-bezier(.4,0,1,1);   /* exits only, never an entrance */

  /* ── tier 2: semantic, light ───────────────────────────────────────────── */
  --bg:var(--gray-0);
  --sunken:#f4f4f5; /* daisy base-200, his light pick 2026-10-22 */
  --raised:var(--gray-0);
  --ink:var(--gray-900);

  --ink2:var(--gray-700);
  --muted:#6b7280; /* daisy's blue-grey, his light pick */
  --line:#e5e5e5; /* daisy base-200 border */
  --line2:var(--gray-300);

  --accent:var(--indigo-500);
  --accent-hover:var(--indigo-600);
  --accent-ink:var(--gray-0);
  --accent-soft:var(--indigo-50);

  --ok:var(--green-600);     --ok-soft:var(--green-50);     --ok-line:var(--green-200);
  --warn:var(--amber-700);   --warn-soft:var(--amber-50);   --warn-line:var(--amber-200);
  --alert:var(--red-700);    --alert-soft:var(--red-50);    --alert-line:var(--red-200);

  --font-sans:-apple-system,BlinkMacSystemFont,"Segoe UI",Inter,sans-serif;
  --font-mono:ui-monospace,SFMono-Regular,Menlo,monospace;

  --scrim:rgba(16,16,20,.5);
  --shadow:0 1px 2px rgba(16,16,20,.05),0 1px 3px rgba(16,16,20,.04);
  --shadow-lg:0 12px 32px rgba(16,16,20,.10),0 2px 8px rgba(16,16,20,.06);
  --shadow-modal:0 24px 64px rgba(0,0,0,.28);
  --ring:0 0 0 2px var(--bg),0 0 0 4px var(--accent);

  --measure:34em;
  --control-h:36px;
  --control-h-sm:28px;
  --row-h:44px;

  /* ── tier 3: component ─────────────────────────────────────────────────── */
  --btn-bg:var(--raised);
  --btn-bg-hover:var(--sunken);
  --btn-line:var(--line2);
  --btn-radius:var(--radius-sm);
  --btn-fs:13.5px;
  --btn-weight:600;   /* 500 was reading as a label, not an action. The original's CTA was 600. */
  --btn-pad:14px;

  --field-line:var(--line2);
  --field-bg:var(--raised);
  --field-radius:var(--radius-sm);

  --card-bg:var(--raised);
  --card-line:var(--line);
  --card-radius:var(--radius-box); /* 16px, his pick 2026-10-22: daisy's card cornering */
  --card-pad:18px;
  --gap-card:var(--space-4);   /* the floor between two cards. 12px reads as one object split in two. */

  --badge-h:22px;
  --badge-radius:var(--radius-pill);
  --badge-fs:11.5px;

  --table-fs:13.5px;
  --table-head-fs:12px;
  --table-cell-pad:13px 12px;

  /* THE ESTATE BAR, adopted from zpe-nav's own sheet on 2026-10-28. Its measures were literals in that file and
     they are the bar's contract: 52px tall, 11px uppercase items at 0.2em, a 68rem line that falls back to the
     gutter, and a reserve for the empty host before the script lands. A component reads tokens, so they live here. */
  --bar-h:52px;
  --bar-reserve:3.4rem;
  --bar-fs:11px;
  --bar-track:0.2em;
  --bar-track-acct:0.16em;
  --bar-gutter:clamp(1.25rem, 4vw, 2.5rem);
  --bar-line-h:68rem; /* the 48px rung, his default 2026-10-22; compact 32px lives in the data-density block */
  /* The contrast contract, per scope. Body text is what these floors protect: 7 for the primary
     ink on its own surface, 4.5 for secondary and muted text, 4.5 for a status hue as text. The
     accent is declared at 3 on purpose: it carries fills, rules and emphasis, and its own label
     pair (accent-ink on accent) is the one that has to clear 4.5 as text. */
  /* @contrast --ink on --bg 7
     @contrast --ink on --raised 7
     @contrast --ink2 on --bg 4.5
     @contrast --muted on --bg 4.5
     @contrast --muted on --raised 4.5
     @contrast --accent on --bg 3
     @contrast --accent-ink on --accent 4.5
     @contrast --ok on --bg 4.5
     @contrast --warn on --bg 4.5
     @contrast --alert on --bg 4.5 
     @contrast --ink on --accent-soft 4.5
    */
}

/* A theme re-points tier 2 and declares nothing new. A theme that ADDS a token widens the
   vocabulary for every surface and is how a second palette gets in. */
:root[data-theme="dark"] {
  /* THE UA MUST BE TOLD, or every native part the sheet does not paint stays light. Measured 2026-10-28 on a
     consumer's form in this theme: `input[type="date"]` rendered `background rgb(255,255,255)` and
     `color rgb(0,0,0)`, a white box in a near-black page, because the sheet styles the text-ish inputs and not
     this one, and the theme never declared its scheme. `tape` and `amp` already carried this line; this theme did
     not, which is how a whole palette came to keep light scrollbars, a light calendar popup and light form
     controls. Declaring it here fixes the popup and the scrollbars as well, and costs no token. */
  color-scheme: dark;
  --bg:var(--gray-950);
  --sunken:#17171c;
  --raised:#1d1d23;
  --ink:#f2f2f5;
  --ink2:#d4d4d8;
  --muted:#9b9ba4;
  --line:#30303a;
  --line2:#4d4d5a;

  --accent:#8b8bf5;
  --accent-hover:#a3a3ff;
  --accent-ink:var(--gray-950);
  --accent-soft:#23233a;
  /* The three role tokens a dark page needs to stop reading as a void, taken from THIS theme's own
     accent rather than copied from another theme's violet. Measured on the launcher (2026-10-27): the
     same page with a colour-only background read as flat, and the same page with this wash read as a
     room. `--page-glow` is consumed by base.css's body rule, so a page that sets it needs no CSS. */
  --accent-grad:linear-gradient(96deg,color-mix(in srgb,var(--accent) 78%,#3b5bdb),var(--accent));
  --accent-grad-text:linear-gradient(96deg,color-mix(in srgb,var(--accent) 72%,#b3b3ff) 0%,
                                          var(--accent) 52%,
                                          color-mix(in srgb,var(--accent) 30%,var(--ink)) 100%);
  --page-glow:radial-gradient(920px 500px at 12% -8%,color-mix(in srgb,var(--accent) 17%,transparent),transparent 62%),
              radial-gradient(780px 420px at 94% 2%,color-mix(in srgb,var(--accent) 9%,transparent),transparent 58%);

  --ok:#6ee7a0;      --ok-soft:#12251a;      --ok-line:#1f4430;
  --warn:#f0b46b;    --warn-soft:#2a1f10;    --warn-line:#4a3618;
  --alert:#f08c8c;   --alert-soft:#2a1416;   --alert-line:#4a2222;

  --scrim:rgba(0,0,0,.62);
  --shadow:0 1px 2px rgba(0,0,0,.4),0 1px 3px rgba(0,0,0,.3);
  --shadow-lg:0 12px 32px rgba(0,0,0,.5),0 2px 8px rgba(0,0,0,.35);
  --ring:0 0 0 2px var(--bg),0 0 0 4px var(--accent);
  /* The contrast contract, per scope. Body text is what these floors protect: 7 for the primary
     ink on its own surface, 4.5 for secondary and muted text, 4.5 for a status hue as text. The
     accent is declared at 3 on purpose: it carries fills, rules and emphasis, and its own label
     pair (accent-ink on accent) is the one that has to clear 4.5 as text. */
  /* @contrast --ink on --bg 7
     @contrast --ink on --raised 7
     @contrast --ink2 on --bg 4.5
     @contrast --muted on --bg 4.5
     @contrast --muted on --raised 4.5
     @contrast --accent on --bg 3
     @contrast --accent-ink on --accent 4.5
     @contrast --ok on --bg 4.5
     @contrast --warn on --bg 4.5
     @contrast --alert on --bg 4.5 
     @contrast --ink on --accent-soft 4.5
    */
}

/* A third theme, carried over from the fleet's warm-paper palette because a consumer already ships
   it and dropping it would be a visible change to a live page rather than a port. It re-points the
   semantic tier only, and it re-points the SANS slot to a serif on purpose: every page using it is
   prose first, so the reading face is the page face. */
:root[data-theme="bakery"] {
  --bg:#f5f4ed; --sunken:#edeae0; --raised:#faf9f5;
  --ink:#141413; --ink2:#3a3936; --muted:#5e5d59;
  --line:#e8e6dc; --line2:#d9d5c7;
  --accent:#a8412a; --accent-hover:#8f351f; --accent-ink:#ffffff; --accent-soft:#f2e6e1;
  --ok:#2f6f33;    --ok-soft:#e2efe4;    --ok-line:#c6ddc9;
  --warn:#8a5300;  --warn-soft:#f6ecd9;  --warn-line:#e0cba8;
  --alert:#b53333; --alert-soft:#f7e9e6; --alert-line:#d9aa9f;
  --font-sans:Georgia,"Iowan Old Style","Times New Roman",serif;
  --shadow:0 1px 2px rgba(60,50,35,.06),0 1px 3px rgba(60,50,35,.05);
  /* The contrast contract, per scope. Body text is what these floors protect: 7 for the primary
     ink on its own surface, 4.5 for secondary and muted text, 4.5 for a status hue as text. The
     accent is declared at 3 on purpose: it carries fills, rules and emphasis, and its own label
     pair (accent-ink on accent) is the one that has to clear 4.5 as text. */
  /* @contrast --ink on --bg 7
     @contrast --ink on --raised 7
     @contrast --ink2 on --bg 4.5
     @contrast --muted on --bg 4.5
     @contrast --muted on --raised 4.5
     @contrast --accent on --bg 3
     @contrast --accent-ink on --accent 4.5
     @contrast --ok on --bg 4.5
     @contrast --warn on --bg 4.5
     @contrast --alert on --bg 4.5 
     @contrast --ink on --accent-soft 4.5
    */
}

/* A third dark reading, and the first THEME that exists to make a product distinct rather than to
   suit a surface class. Near-black canvas, white headline, one violet accent. The point of a theme
   is that the components do not change: a page that picks `tape` gets the same button, table and
   badge, in this product's colour. Sameness across a portfolio is a theme problem, not a component
   problem, which is why this is 20 lines and not a second stylesheet. */
:root[data-theme="tape"] {
  color-scheme: dark;
  /* R4 verdict 2026-10-22, final: his picks are shadcn dark and daisy light, so the reference
     palettes are ADOPTED (the falsifier in DECISIONS.md fired). Dark = shadcn neutrals: cards
     separate by border, not fill; the tape identity layer (gradient, glow, accent) is untouched. */
  --bg:#09090b; --sunken:#18181b; --raised:#09090b;
  --ink:#fafafa; --ink2:#f4f4f5; --muted:#a1a1aa;
  /* R6 follow-up: #333333 measured 1.57:1 on the page, so a control border was invisible. A border
     is a UI boundary, not decoration, and the floor for it is 3:1; #606060 measures 3.16. */
  --line:#27272a; --line2:#606060;
  --accent:#7c3aed; --accent-hover:#8b5cf6; --accent-ink:#ffffff; --accent-soft:#1e1b2e;
  /* a gradient is a product's face, so it is a token: a theme may set one, components may not
     invent one. Both stops are dark enough for white text at AA, which is checked by hand below. */
  --accent-grad:linear-gradient(96deg,#7c3aed,#3b82f6);
  /* the same ramp as a TEXT fill. Three stops, because two read as a flat wash at 56px: the eye
     needs the violet to arrive, the blue to carry, and the cyan to land. */
  --accent-grad-text:linear-gradient(96deg,#8b5cf6 0%,#3b82f6 52%,#22d3ee 100%);
  /* the page wash. Not decoration: it is what stops a near-black page reading as a void, and it is
     the difference between this design and a flat #0a0a0a. */
  --page-glow:radial-gradient(1000px 520px at 12% -8%,rgba(124,92,255,.20),transparent 60%),
              radial-gradient(900px 430px at 94% 2%,rgba(34,211,238,.12),transparent 55%);
  --ok:#4ade80;    --ok-soft:#0f2318;    --ok-line:#1f4430;
  --warn:#fbbf24;  --warn-soft:#261d09;  --warn-line:#4a3618;
  --alert:#f87171; --alert-soft:#260f0f; --alert-line:#4a2222;
  --scrim:rgba(0,0,0,.68);
  /* The contrast contract, per scope. Body text is what these floors protect: 7 for the primary
     ink on its own surface, 4.5 for secondary and muted text, 4.5 for a status hue as text. The
     accent is declared at 3 on purpose: it carries fills, rules and emphasis, and its own label
     pair (accent-ink on accent) is the one that has to clear 4.5 as text. */
  /* @contrast --ink on --bg 7
     @contrast --ink on --raised 7
     @contrast --ink2 on --bg 4.5
     @contrast --muted on --bg 4.5
     @contrast --muted on --raised 4.5
     @contrast --accent on --bg 3
     @contrast --accent-ink on --accent 4.5
     @contrast --ok on --bg 4.5
     @contrast --warn on --bg 4.5
     @contrast --alert on --bg 4.5 
     @contrast --ink on --accent-soft 4.5
    */
}

/* A fourth theme, and the second that exists for a PRODUCT rather than a surface class: ampWe's
   own palette, ported 2026-10-25. Near-black canvas, one violet accent, mint for ok. It lives here
   rather than in the app because a theme is data and its values are tokens: the app then carries no
   colour literal at all, and this palette stays readable by whatever gate reads this file. The app
   reads these roles through its tailwind config, so its markup names a role and never a hue. */
:root[data-theme="amp"] {
  color-scheme: dark;
  --bg:#0a0a0f; --sunken:#12121a; --raised:#1a1a26;
  /* --muted moved from #6b7280 (4.09:1 on this page, under the body floor) to the nearest step
     above 4.5, which is the value the declared contract requires. */
  --ink:#f0f0ff; --ink2:#9999bb; --muted:#7d8695;
  --line:#2a2a3e; --line2:#3a3a5e;
  --accent:#6c63ff;   /* white on this violet measured 4.32:1, so the CTA label failed AA. A light accent takes the
     page's dark ink, which is the library's own convention in the dark theme. */
  --accent-hover:#5a52ef; --accent-ink:var(--bg); --accent-soft:#1e1c3f;
  /* the waveform and the glow are this product's face, so they are tokens, not page styles */
  --accent-grad:linear-gradient(to top,#6c63ff,#8b84ff);
  --ok:#00e5a0;    --ok-soft:#091919;    --ok-line:#134a39;
  --warn:#ffb347;  --warn-soft:#191412;  --warn-line:#4a3618;
  --alert:#ff4d6a; --alert-soft:#1b0f15; --alert-line:#4a2222;
  --scrim:rgba(0,0,0,.7);
  /* The contrast contract, per scope. Body text is what these floors protect: 7 for the primary
     ink on its own surface, 4.5 for secondary and muted text, 4.5 for a status hue as text. The
     accent is declared at 3 on purpose: it carries fills, rules and emphasis, and its own label
     pair (accent-ink on accent) is the one that has to clear 4.5 as text. */
  /* @contrast --ink on --bg 7
     @contrast --ink on --raised 7
     @contrast --ink2 on --bg 4.5
     @contrast --muted on --bg 4.5
     @contrast --muted on --raised 4.5
     @contrast --accent on --bg 3
     @contrast --accent-ink on --accent 4.5
     @contrast --ok on --bg 4.5
     @contrast --warn on --bg 4.5
     @contrast --alert on --bg 4.5 
     @contrast --ink on --accent-soft 4.5
    */
}

/* Density is a data attribute on a container, not a second stylesheet: it moves the row
   height and the control height and nothing else. Text size never changes with density. */
[data-density="compact"] {
  --control-h:28px;
  --control-h-sm:24px;
  --row-h:32px;
  --table-cell-pad:6px 10px;
  --card-pad:14px;
  --space-5:18px;
  --space-6:24px;
}
