/* ============================================================================
   House of Value - design tokens · F0 · v1.0.0
   Frozen 2026-08-13 from the accepted design spec:
   the frozen shell spec, kept outside this repo (variant D · white · deep-indigo · serif
   display), chosen by Natan after 5 rejected rounds - the full trail is in
   the header of this file. DO NOT re-warm the palette: mocha/brown was
   explicitly rejected ("I like more white").
   Modes: [data-mode="light"] (default) · [data-mode="dark"].
   ============================================================================ */

[data-mode="light"]{
  /* surfaces - cool near-whites, content on pure white */
  --paper:#fafafa;          /* app ground: header, rails */
  --page:#ffffff;           /* reading surface */
  --card:#fff;              /* bordered cards */
  --wash-card:#f4f5fd;      /* accent-tinted callout fill */
  /* lines */
  --rule:rgba(15,18,25,.08);
  --rule2:rgba(15,18,25,.15);
  /* ink - neutral greys, never warm */
  --text:#17181c; --body:#3d4149; --soft:#656972; --muted:#8d919b;
  /* the one accent - deep royal indigo, used sparingly */
  --accent:#4338ca; --accent-ink:#3730a3;
  --wash:rgba(67,56,202,.075); --wash2:rgba(67,56,202,.14);
  /* kind colors (one dot per folder, from house-of-value) */
  --g-guide:#16a34a; --g-ref:#2563eb; --g-std:#9333ea; --g-rec:#ea580c; --g-tmp:#ca8a04;
  /* status */
  --warn:#b45309; --warn-wash:rgba(180,83,9,.07); --ok:#15803d;
  /* code */
  --code-bg:rgba(15,18,25,.05); --code-fg:#3730a3;
  --block:#14161c; --block-fg:#e3e6ec;
  /* elevation - three levels: rest, raised (hover), floating (popover) */
  --sh:0 1px 2px rgba(15,18,25,.04);
  --sh-raise:0 2px 6px rgba(15,18,25,.07),0 1px 2px rgba(15,18,25,.05);
  --sh-pop:0 18px 50px rgba(15,18,25,.15),0 2px 8px rgba(15,18,25,.06);
  --veil:rgba(15,18,28,.35);
  /* skeleton - measured, not chosen. The usual #e0e0e0 default is 1.32:1 against
     this surface, less than half the 3:1 a non-text graphic owes a low-vision
     reader, and #d4d4d4 (the usual "accessible" swap) is 1.48:1. #909090 is
     3.1925:1 against --page #ffffff and 3.0587:1 against --paper #fafafa, so it
     clears on both surfaces a skeleton can land on. It is heavier than a designer
     would pick by eye, which is the point: the eye was the thing that was wrong.
     Ratios are computed to four places by verify-loading.mjs, because a value that
     ROUNDS to 3.00 is not at 3.00 - #959595 is 2.9953:1 and fails. */
  --sk:#909090;
  --sk-strong:#767676;      /* 4.5422:1 - only under prefers-contrast: more */
}
[data-mode="dark"]{
  --paper:#101114; --page:#16171b; --card:#1c1d22; --wash-card:#1f2030;
  --rule:rgba(255,255,255,.09); --rule2:rgba(255,255,255,.17);
  --text:#eef0f4; --body:#c3c6cd; --soft:#8f939c; --muted:#686c75;
  --accent:#a5b4fc; --accent-ink:#c7d2fe;
  --wash:rgba(165,180,252,.1); --wash2:rgba(165,180,252,.19);
  --g-guide:#4ade80; --g-ref:#60a5fa; --g-std:#c084fc; --g-rec:#fb923c; --g-tmp:#facc15;
  --warn:#d9a441; --warn-wash:rgba(217,164,65,.09); --ok:#4ade80;
  --code-bg:rgba(255,255,255,.07); --code-fg:#c7d2fe;
  --block:#0e0f13; --block-fg:#d6d9e0;
  --sh:0 1px 2px rgba(0,0,0,.4);
  --sh-raise:0 2px 8px rgba(0,0,0,.5);
  --sh-pop:0 18px 50px rgba(0,0,0,.6);
  --veil:rgba(0,0,0,.5);
  --sk:#6a6a6a;             /* 3.3114:1 against --page #16171b */
  --sk-strong:#808080;      /* 4.5349:1 */
}

/* type - three voices, no more:
   serif display (doc titles, h2)   = Newsreader, opsz high
   UI                               = SF / system stack
   data, labels, badges             = JetBrains Mono */
:root{
  --font-ui:-apple-system,BlinkMacSystemFont,"SF Pro Text","Segoe UI",system-ui,sans-serif;
  --font-display:'Newsreader',Georgia,serif;
  --font-mono:'JetBrains Mono',ui-monospace,monospace;
  --fs-ui:14.5px; --lh:1.65;
  --r-xs:4px; --r-sm:5px; --r-md:8px; --r-lg:9px; --r-xl:12px; --r-pill:99px; --r-circle:50%;
  /* motion - two easings, three speeds. Respect prefers-reduced-motion.

     TWO CURVES, NOT ONE, AND WHICH GOES WHERE. An entrance decelerates: it puts
     the destination on screen fast and settles into it, so the reader sees where
     the thing landed rather than watching it travel. An exit accelerates: the
     reader has already decided, and the fastest honest way to say "gone" is to
     leave quickly. One curve used both ways makes every dismissal linger, which
     is the single most common motion defect in a shipped interface.

     The pairing rule in this file: the CLOSED state carries --ease-exit (it is
     the rule that plays while the thing leaves) and the .open state re-declares
     the same transition with --ease (it plays while the thing arrives). Both
     directions stay interruptible, because both are transitions - reverse a
     half-open drawer and it runs back from where it is, not from the start. */
  --ease:cubic-bezier(.25,.7,.3,1);        /* decelerate - entrances, arrivals */
  --ease-exit:cubic-bezier(.4,0,1,1);      /* accelerate - exits, dismissals */
  --dur-micro:120ms;   /* hover, color, border */
  --dur-small:180ms;   /* lift, reveal */
  --dur-med:240ms;     /* drawer, popover, palette */
  /* The fourth step, added 2026-08-25 for the brand reveal and nothing else. A logo
     arriving is not a micro-interaction: the mark's block travels 7.5px at header
     size, and at 240ms that reads as a flicker rather than a movement. 400ms is
     Carbon's slow-01 and sits exactly on this scale's own ceiling, so it is the
     slowest thing that can be expressed here rather than a new licence. Do not reach
     for it for feedback - the reference band for that is 70-150ms. */
  --dur-slow:400ms;    /* a brand reveal, and only that */
}
@media (prefers-reduced-motion:reduce){
  /* .01ms, not 0. This block is load-bearing beyond the durations it names: the
     closed state of every overlay defers `visibility` by `var(--dur-med)`, and this
     is the only thing that collapses that delay - the blanket rule in shell.css
     forces transition-duration and animation-duration, and never touches a delay.
     It says .01ms for the same reason the blanket rule does, and a stranger test
     caught it saying 0ms while both the check and docs/motion.md insisted on .01ms.
     Nothing read this file then. tests/verify-motion.mjs reads it now. */
  :root{--dur-micro:.01ms;--dur-small:.01ms;--dur-med:.01ms;--dur-slow:.01ms}
}

/* ============================================================================
   LAYOUT - breakpoints and widths, and where the numbers come from
   ============================================================================

   This block was added 2026-08-23 after the question "is there a worldwide
   standard we are following, or is this our own custom?" was asked and the
   honest answer was neither: every number was chosen here, and three of the five
   breakpoints sat 8-20px away from a published one. A near-miss is the worst
   case - arbitrary numbers to memorise, with none of a convention's benefit.

   THE SOURCES, read rather than remembered:
     MUI 9.3.1     xs 0 · sm 600 · md 900 · lg 1200 · xl 1536
                   read out of the package itself, not from memory. This repo
                   has no node_modules and no package.json, so re-verify with:
                     npm i @mui/material && grep -oE '(xs|sm|md|lg|xl): [0-9]+' \
                       node_modules/@mui/system/createBreakpoints/createBreakpoints.js
                   Its Container maxWidth is not a separate scale: it resolves to
                   `theme.breakpoints.values[maxWidth]`, so the container widths
                   ARE the breakpoints.
     Bootstrap 5.3 sm 576 · md 768 · lg 992 · xl 1200 · xxl 1400
                   containers 540 · 720 · 960 · 1140 · 1320
     Tailwind 4    sm 640 · md 768 · lg 1024 · xl 1280 · 2xl 1536
                   (defined in rem; 40/48/64/80/96rem at a 16px root)
     Material 3    window size classes, in dp, not device widths:
                   compact <600 · medium 600-839 · expanded 840-1199 ·
                   large 1200-1599 · extra-large >=1600

   THE SELECTION RULE, corrected. It first read "snapped to the nearest value two
   or more systems agree on, which is why 768 and 1024 win - Bootstrap and
   Tailwind both publish them." That is false and a cold reader caught it:
   Bootstrap's lg is 992, stated correctly eleven lines above. Only 768 and 1200
   have two publishers; 640, 1024 and 1400 have one each. Worse than wrong, it
   was the DECISION PROCEDURE - anyone adding a breakpoint would have applied a
   rule the existing set does not satisfy and got a different answer.

   The rule is what the check actually encodes: SNAPPED TO A VALUE AT LEAST ONE
   NAMED SYSTEM PUBLISHES, WITH THE SYSTEM RECORDED. Two publishers is a
   tie-breaker, not a requirement. We did not adopt one vendor throughout
   because the two we already matched exactly came from different systems -
   Tailwind's sm and Bootstrap's xxl - so picking one would have moved numbers
   that were already right.

   ⚠ MATERIAL 3 IS BACKGROUND, NOT A SOURCE FOR A TOKEN. Its classes are window
   size classes in dp, and dp is not a CSS pixel. Listed above because it is what
   the platform reasons in; not admissible in PUBLISHED in the check.

   WHAT NO SYSTEM PUBLISHES: a reading measure. MUI, Bootstrap, Material 3,
   Apple's HIG and Fluent all specify CONTAINER widths - app-shell furniture -
   and none of them states how wide a column of prose should be. The only vendor
   answer is Tailwind's `max-w-prose`, which is 65ch. So --measure below is ours,
   and it is defended by measurement rather than by citation: see the comment on
   .doc in index.html, and note that `ch` is the "0" glyph advance, so 65ch
   renders roughly 70-75 real characters.
   ============================================================================ */
:root{
  /* Breakpoints. DECLARED HERE AND USED LITERALLY, and that is not laziness:
     a custom property CANNOT be used in a media query - `@media (min-width:
     var(--bp-md))` is invalid, never matches, and NEVER ERRORS. It fails
     silently, which is worse than failing. Tokenising them for real needs
     PostCSS @custom-media or a Tailwind build, and this app deliberately has no
     build step. So these five exist to be the single written source of the
     numbers, and tests/verify-breakpoints.mjs holds every @media literal in the
     tree to them - which is the enforcement a var() would have bought.

     SAID PLAINLY: `grep -r "var(--bp-" src/` returns nothing and always will.
     These five are shipped to every browser as documentation with CSS syntax.
     That is the trade - a real declaration is harder to let rot than a comment,
     and the check reads it - but nobody should discover it by grepping. */
  --bp-sm:640px;    /* Tailwind sm. The ONE rule here hides the editor's New
                       button, which pushed the avatar off a 390px viewport.
                       NOT the phone breakpoint - this comment used to say "the
                       drawer replaces the tree" and that happens at --bp-md,
                       128px later. A cold reader following this token would have
                       shipped a phone rule that fires after every other one. */
  --bp-md:768px;    /* Bootstrap md AND Tailwind md. THE PHONE BOUNDARY: the tree
                       goes, the drawer takes over, the shell becomes one column,
                       and the Desk refuses to render below it */
  --bp-lg:1024px;   /* Tailwind lg. Index views take their wider column */
  --bp-xl:1200px;   /* MUI lg AND Bootstrap xl. The outline rail collapses */
  --bp-2xl:1400px;  /* Bootstrap xxl. Prose takes its wider measure */

  /* Widths. These ARE usable as var() and every rule below now takes them.

     THE MEASURE, AND WHY IT IS 630 EVERYDAY AND 690 ON A LAPTOP. 630px is what the
     2026-08-18 type audit derived - but on a 14in MacBook (1512 logical) the shell
     hands the middle column ~1000px and the prose sat in the middle of it, reading as
     a narrow ribbon against a wide screen. The complaint was real and the fix is NOT
     to abandon the measure. MEASURED, not estimated: a rendered 446-character paragraph
     at --measure-wide breaks into 6 lines - 74 characters per line.
     WHERE 74 SITS, and the sourcing matters because the usual number here is folklore.
     The famous "45-75 characters" is Robert Bringhurst, The Elements of Typographic
     Style 2.1.2, stating what is "widely regarded" - for PRINT, serif, single column.
     It is not an empirical finding and never claimed to be. Measured on screen it goes
     the other way: Shaikh & Chaparro 2005 (HFES 49(5):701-705, n=20) found 95 CPL read
     FASTEST, while Dyson & Haselgrove 2001 found better comprehension at 55 than 100 -
     speed and comprehension point in opposite directions. The defensible band is
     60-80 CPL, which is where 74 lands, and the honest claim for this change is "inside
     a defensible band and closer to what readers report preferring", NOT "widened to the
     ceiling of a standard". There is no such standard: WCAG 1.4.8 is Level AAA and asks
     that a MECHANISM be available, not that a default be any particular width.
     An earlier version of this comment called 75 a ceiling and cited "the canon". That
     was the same uncited-authority move this repository spent a night removing from its
     own view documents, committed here an hour later by the session that removed it.
     Below 1400px nothing moves - there the column is not wide enough for the prose to
     look stranded in the first place. And do not read the `ch` CSS unit as the answer:
     it measures the "0" advance, which for a proportional serif reported 58 where the
     real rendered count is 74. The full record, with the rejected alternatives and the
     sources: docs/decisions/0019-the-layout-scale.md */
  --measure:630px;       /* prose, the everyday measure - ~67 real characters */
  --measure-wide:690px;  /* prose above --bp-2xl - measured at 74 real characters */
  --w-index:920px;       /* the Lobby, Wings, Vault, Study: indexes, not prose.
                            Between Tailwind 4xl (896) and Bootstrap lg (960) */
  --w-tree:270px;        /* the corpus tree */
  --w-rail:240px;        /* the outline rail */
  --w-tree-md:250px;     /* the tree between --bp-md and --bp-xl */
}
