/* design-tokens.css — spaces-svc canonical design tokens (SINGLE SOURCE OF TRUTH).
   Loaded FIRST, before space.css (reader/eZine) and admin.css (dashboard), so the same
   semantic brand role carries ONE name across both properties instead of three.

   This is spaces-svc's OWN copy — it does NOT touch ~/noo/network. Values are the Niche of One
   brand set (RATION v5): warm paper stock, ink text, Poppins display, Figtree UI, Space Mono
   kickers, Lora reading. Red (--buy) is the MONEY DOOR ONLY — price, unlock, tip. Never décor.

   Consumers:
     • space.css  uses these names directly (--paper/--ink/--buy/--panel/--panel2/--rule-hard/
       --lift/--disp/--ui/--read/--mono). It keeps only genuine per-property overrides
       (--muted, --rule, --measure, --gutter, --yellow) locally.
     • admin.css  aliases its historical names onto these (--black:var(--ink); --white:var(--paper);
       --red:var(--buy); --bg-dim:var(--panel2); --font:var(--ui)) and inherits --panel/--lift/
       --disp/--read/--mono. It keeps its own --grey, --border, --min, --yellow and its denser --sN.

   Theme: follow the OS by default, but the shared network theme choice (noo_theme cookie, applied
   as data-theme on <html> before paint) wins — an explicit light choice overrides OS-dark, an
   explicit dark choice applies even on a light OS. Mirrors the structure both sheets already used
   so the cookie keeps behaving identically. font-display etc. live in the @font-face files. */

/* Anton (single weight 400) — THE header face, network-wide (apex lock 2026-07-17).
   Ultra-condensed poster caps; matches the apex band, the email masthead and the
   Dispatch headers. Self-hosted: Google egress is blocked on this box.
   Declared HERE rather than in space.css/admin.css because those two carry
   duplicate @font-face blocks and this file is the one both of them load first —
   one copy, and the reader and the dashboard cannot drift onto different faces.
   Byte-identical to network/src/fonts/display.css's declaration. */
@font-face {
  font-family: 'Anton';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/fonts/anton-v1-latin.woff2) format('woff2');
}

:root {
  /* ── Surfaces & ink ─────────────────────────────────────────────── */
  /* THE BASE PALETTE IS THE DEFAULT STOCK. layout.php:86 emits
     data-space-stock ONLY when a member's stock differs from
     SPACE_STOCK_DEFAULT, so whatever is declared here IS what the default
     renders as, and the default stock is the one with no block in space.css.
     Change these two lines and you change the default stock — keep them in
     step with SPACE_STOCK_DEFAULT or every space renders the wrong paper.

     2026-08-07: cream #f5f0e6 -> white #ffffff, i.e. newsprint -> press. The
     apex retired warm cream on 2026-07-25 for one dark design and its exact
     negative; these are the apex's values, so a Space and the main site now
     share a ground. newsprint did not disappear — it became a stamped stock
     with its own block in space.css, which is why the 37 members holding it
     see no change at all. */
  --paper:     #ffffff;   /* press — the apex ground */
  --ink:       #000000;
  --panel:     var(--paper);   /* redesign 2026-07: single cream ground (matches network) */
  --panel2:    var(--paper);   /* separation via rules/borders, not tone */
  --btn-radius: 999px;         /* redesign 2026-07: full-pill buttons (matches network) */

  /* ── Muted text + rules (reader/brand defaults; space.css restates its
        own documented copies — dashboard uses its own --grey/--border) ── */
  --muted:     #595959;   /* press muted — 7.00:1 on white */
  /* 52%, not 20%. --rule draws every card edge, table rule, form border and
     divider, which makes it non-text UI under WCAG 1.4.11 and puts its floor at
     3:1 against the paper it sits on. At 20% it resolved to 1.51:1 on newsprint
     and failed on all five stocks in both modes (worst 1.51, best 2.16 — ten
     for ten). The 80-combination suite never caught it because palette_test.php
     measures TEXT contrast (4.5:1) and a rule carries no text.
     52% light / 38% dark are the apex's own measured values (network
     design-tokens.css:142 and :198) adopted verbatim rather than re-derived, so
     the two properties draw the same weight of line. Checked against all five
     stocks x both modes: worst case 3.11:1 (slate dark), best 4.29:1 (press
     light). Measured, not eyeballed. */
  --rule:      color-mix(in srgb, var(--ink) 52%, var(--paper));
  --rule-hard: #000000;

  /* ── The money door (red) — price, unlock, tip, Delete, error only ── */
  --buy:       #d70015;
  --on-buy:    #ffffff;

  /* ── The zine offset-shadow — craft, not decoration ──────────────── */
  --lift:      5px 5px 0 var(--rule-hard);

  /* ── Type faces ──────────────────────────────────────────────────── */
  /* --disp IS THE APEX'S STACK, character for character (network/src/css/
     design-tokens.css:65). Until 2026-08-08 it was Poppins, which meant the
     wordmark in the shared band — the single element the header brief calls "the
     same-header test" — was set in a different typeface from the apex's. Poppins
     appears here as the DEEP fallback, for glyphs Anton lacks; 'Arial Narrow' is
     the deliberate condensed swap-fallback while Anton loads, so the wordmark
     does not reflow from wide to condensed on a slow connection.
     Anton ships ONE weight (400). Every rule below that asks --disp for 600/700/
     800 therefore carries font-synthesis-weight:none, or the browser fakes a
     smeared bold. Copy the whole stack, never just the family name. */
  --disp:  'Anton', 'Arial Narrow', 'Poppins', 'Poppins fallback', 'Arial Black', sans-serif;
  /* --disp2 is the apex's own answer to the same problem (network design-tokens.css:75)
     and it is why flipping --disp did not have to be an argument. Anton is poster
     caps; the apex says so in console.css:195 while deliberately NOT using it for a
     sentence. So the split here is mechanical, not taste: a rule that already sets
     text-transform:uppercase keeps --disp and becomes Anton; every sentence-case
     rule moved to --disp2 and renders exactly as it did yesterday. That is why a
     face change across 39 spaces touches only the type that was already shouting. */
  --disp2: 'Poppins', 'Poppins fallback', 'Helvetica Neue', Arial, sans-serif;
  --ui:    'Figtree', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif;
  --read:  'Lora', Georgia, 'Times New Roman', serif;
  --mono:  'Space Mono', 'Courier New', Courier, monospace;

  /* ── Readability floor ───────────────────────────────────────────── */
  --min:   11.5px;

  /* ── Spacing scale (reader/brand rhythm; the dashboard keeps a denser
        local scale of its own in admin.css) ─────────────────────────── */
  --s1: 8px; --s2: 14px; --s3: 22px; --s4: 34px; --s5: 52px; --s6: 76px;
}

/* Dark palette — OS by default, noo_theme cookie (data-theme) wins. --rule and --lift are
   defined once above in terms of --ink/--paper/--rule-hard, so they auto-flip; only the raw
   values below change. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #000000; --ink: #ffffff;
    --panel: var(--paper); --panel2: var(--paper);
    --muted: #8f8f8f;
    --rule: color-mix(in srgb, var(--ink) 38%, var(--paper));   /* was 26% = 2.16:1. See the light-mode note above. */
    --rule-hard: #ffffff;
    --buy: #ff4536; --on-buy: #000000;
  }
}
:root[data-theme="dark"] {
  --paper: #000000; --ink: #ffffff;
  --panel: #111111; --panel2: #191919;   /* neutral greys — the warm #161615/#1b1b19 belonged to the cream system */
  --muted: #8f8f8f;
  --rule: color-mix(in srgb, var(--ink) 38%, var(--paper));   /* was 26% = 2.16:1. See the light-mode note above. */
  --rule-hard: #ffffff;
  --buy: #ff4536; --on-buy: #000000;
}

/* ── BUTTON PRIMITIVE ────────────────────────────────────────────────────
   ONE button both surfaces consume. The zine button: Figtree, 2px ink frame,
   full-pill radius, 44px touch floor. The offset-lift ONLY on prominent CTAs
   (.btn--cta); --buy = money door + destructive; --dense = dashboard density.
   Reader (space.css) + dashboard (admin.css) conform their legacy button
   classes to this so the whole product reads as one component.

   2026-08-07: this block used to redeclare --btn-radius as 3px, silently
   overriding the 999px set at line 28 — two values for one token in one file,
   later-wins, so the pill declared up top never actually rendered. The apex
   ships 999px live (os.css:1222) and continuity with it is the whole job, so
   the 3px is deleted rather than the 999px. DESIGN-SYSTEM.md still says "2px
   hard radius"; it is a third disagreeing source and is stale — the token file
   is truth. Callers in space.css carry mixed var() fallbacks (some 999px, some
   3px); those are dead code, since this file always defines the token, but
   they document the old conflict and are left for a separate sweep.
   ──────────────────────────────────────────────────────────────────────── */
:root {
  --lift-hover:      8px 8px 0 var(--rule-hard);
  --btn-pad-y:       10px;  --btn-pad-x:       16px;
  --btn-dense-pad-y: 6px;   --btn-dense-pad-x: 12px;
  --btn-min-h:       44px;  --btn-dense-min-h: 36px;
}
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: .5em;
  box-sizing: border-box;
  min-height: var(--btn-min-h);
  padding: var(--btn-pad-y) var(--btn-pad-x);
  font: 700 var(--min)/1 var(--ui);
  text-transform: none; letter-spacing: normal; text-decoration: none; white-space: nowrap;
  color: var(--ink); background: var(--paper);
  border: 2px solid var(--ink); border-radius: var(--btn-radius);
  cursor: pointer;
  transition: transform .08s, box-shadow .08s, background .12s, color .12s;
}
.btn:hover { background: var(--panel2); }
.btn--cta { box-shadow: var(--lift); }
.btn--cta:hover { box-shadow: var(--lift-hover); transform: translate(-1px,-1px); background: var(--paper); }
.btn--fill { background: var(--ink); color: var(--paper); }
.btn--fill:hover { background: var(--ink); opacity: .88; }
.btn--buy { background: var(--buy); color: var(--on-buy); border-color: var(--buy); }
.btn--buy:hover { background: var(--buy); filter: brightness(1.08); }
.btn--dense { min-height: var(--btn-dense-min-h); padding: var(--btn-dense-pad-y) var(--btn-dense-pad-x); }
.btn:disabled, .btn.is-disabled, .btn[disabled] { opacity: .5; cursor: default; box-shadow: none; transform: none; }
