/*
 * Web tier design tokens + self-hosted fonts (FND-002 → FE-300/301) — the
 * pipeline entry, linked on every web page.
 *
 * Self-hosts the three brand fonts (latin subset, font-display: swap — no runtime
 * Google Fonts fetch on the critical path; Propshaft fingerprints the font-file
 * refs at build time) and declares the FULL v3 Material-3 tonal palette (light +
 * dark), type scale, spacing/radius, and the always-on layout-shell base. Ported
 * from documents/mockups/web-app-ui-v3.html (:root + .theme-dark); component
 * classes there resolve against these variables unchanged. The editorial
 * cover-art plates live alongside in web/cover_art.css.
 */

/* ── Self-hosted fonts (latin subset, swap) — the Press type system (C-S5) ──
   One variable Newsreader file carries every display/title weight (wght
   400–800 + the full opsz axis; font-optical-sizing: auto resolves display vs
   text cuts per rendered size — the web-only trick RN can't do). Bodoni Moda
   is the masthead face behind the --font-masthead seam (C-S14 repoints ONLY
   that variable to GT Sectra). Source Serif 4 is the reading face. Pipeline:
   documents/operations/font-pipeline.md; contract: press-token-spec.md §4. */
@font-face { font-family: "Newsreader";     font-style: normal; font-weight: 400 800; font-display: swap; src: url("/assets/newsreader-var-7553c563.woff2") format("woff2-variations"); }
@font-face { font-family: "Bodoni Moda";    font-style: normal; font-weight: 400 900; font-display: swap; src: url("/assets/bodoni-moda-var-bbdddbec.woff2") format("woff2-variations"); }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/source-serif-4-400-3c410a16.woff2") format("woff2"); }
@font-face { font-family: "Source Serif 4"; font-style: italic; font-weight: 400; font-display: swap; src: url("/assets/source-serif-4-400i-17ffea12.woff2") format("woff2"); }
@font-face { font-family: "Source Serif 4"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/source-serif-4-600-0074f582.woff2") format("woff2"); }
@font-face { font-family: "DM Sans";        font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/dm-sans-400-148a763a.woff2") format("woff2"); }
@font-face { font-family: "DM Sans";        font-style: normal; font-weight: 500; font-display: swap; src: url("/assets/dm-sans-500-ea26c756.woff2") format("woff2"); }
@font-face { font-family: "DM Sans";        font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/dm-sans-700-60f105ba.woff2") format("woff2"); }

/* Metric-matched swap fallback (CLS killer): local Georgia tuned to
   Newsreader's vertical metrics + frequency-weighted a–z advance width
   (Newsreader hhea 1470/−530/0 @ 2000 upm vs Georgia 1878/−449/0 @ 2048 upm),
   referenced second in --font-display so the pre-swap paint occupies the same
   lines the webfont will. Hand-computed once — plain CSS descriptors, no
   build-time tool (recompute: scratch script noted in font-pipeline.md). */
@font-face {
  font-family: "Newsreader Fallback";
  src: local("Georgia");
  size-adjust: 91.44%;
  ascent-override: 80.38%;
  descent-override: 28.98%;
  line-gap-override: 0%;
}

/* CJK faces — web-only pre-subset builds (150–600KB each; never bundled into
   RN). unicode-range gates the DOWNLOAD: a Latin-only page never fetches a
   Hangul/Kana byte; per-glyph fallthrough covers codepoints outside the
   subsets (KR = KS X 1001 syllables + compat jamo + CJK punct + fullwidth +
   ₩; JP = kana + JIS Level-1 kanji + punct + full/half-width + ¥). */
@font-face { font-family: "Pretendard";    font-style: normal; font-weight: 400; font-display: swap; src: url("/assets/pretendard-400-subset-752a695e.woff2") format("woff2");
             unicode-range: U+20A9, U+3000-303F, U+3130-318F, U+AC00-D7A3, U+FF01-FF60, U+FFE0-FFE6; }
@font-face { font-family: "Pretendard";    font-style: normal; font-weight: 700; font-display: swap; src: url("/assets/pretendard-700-subset-fd8b8462.woff2") format("woff2");
             unicode-range: U+20A9, U+3000-303F, U+3130-318F, U+AC00-D7A3, U+FF01-FF60, U+FFE0-FFE6; }
@font-face { font-family: "Noto Serif KR"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/noto-serif-kr-600-subset-7a706245.woff2") format("woff2");
             unicode-range: U+20A9, U+3000-303F, U+3130-318F, U+AC00-D7A3, U+FF01-FF60, U+FFE0-FFE6; }
@font-face { font-family: "Noto Serif JP"; font-style: normal; font-weight: 600; font-display: swap; src: url("/assets/noto-serif-jp-600-subset-377b45b9.woff2") format("woff2");
             unicode-range: U+3000-30FF, U+31F0-31FF, U+4E00-9FFF, U+FF01-FF9F, U+FFE0-FFE6; }

/* ── v3 Material-3 tonal palette · LIGHT (default) ───────────────────────── */
:root {
  /* named M3 tokens (canonical) */
  --primary:              #522DE6;   /* deep indigo-violet — CTAs, active, links, wordmark */
  --primary-bright:       #6B4EFF;   /* hover / tint / verified */
  --primary-container:    #E5DEFF;   /* violet tint surface */
  --on-primary-container: #4109D7;   /* AA-safe violet text on the tint */
  --primary-tint:         #C8BFFF;
  --on-primary:           #FFFFFF;
  /* DM sent ("mine") bubble — a soft, desaturated COOL tint with NEUTRAL near-ink
     text. Deliberately NOT colored-text-on-colored-bg (the old violet-on-violet read
     muddy) and NOT the loud brand violet: the bubble is a quiet lavender-grey that
     still reads "mine" (cool, against the warm-cream counterpart bubble) while the
     text is near-black for maximum legibility. Fixed hexes with a dark override
     below (a muted cool block + soft near-white text) so it's readable in both. */
  --dm-mine-bg:           #ECEAF6;
  --dm-mine-text:         #211E2B;
  --ink:                  #1E1B18;   /* primary text */
  --ink-2:                #474556;   /* secondary text — cool slate-violet */
  --outline:              #787587;   /* strong hairline */
  --outline-variant:      #C9C4D9;   /* subtle hairline */
  --canvas:               #FFF8F4;   /* warm rosy cream */
  --surface-1:            #FAF2ED;
  --surface-2:            #F4ECE7;
  --surface-3:            #EFE7E1;
  --surface-4:            #E9E1DC;
  --surface-hi:           #FFFFFF;   /* highest / elevated */
  --cover:                #0A0A0C;   /* signature cover / scrim base */
  --like:                 #FF3040;   /* like / active heart only */
  --tertiary:             #DD0C2C;   /* editorial crimson — Special Report / edition tag */
  --tertiary-container:   #FFDAD7;

  /* Press seams (C-S6) — defined for EVERY brand so components can consume
     them before the flag flips. Under violet: the hairline + focus ring track
     --primary and the Signature maps to the existing verified blue (the same
     legacy-coherence move C-S4's violet.ts makes); brass is a brand-independent
     primitive (press-token-spec.md §1.3/§5). */
  --brand-hairline: var(--primary);
  --focus-ring:     var(--primary);
  --signature:      #6B4EFF;
  --brass:          #B08D57;
  --brass-text:     #77592B;

  /* Elevation shadows as tokens — the exact recipes web/components.css floated
     on (byte-identical for violet); the press block retires them to `none`
     (hairline rules + surface deltas carry elevation, spec §6). Focus/identity
     RINGS (0 0 0 Npx) are not elevation and stay literal at their call sites. */
  --shadow-pop:        0 6px 20px -8px rgba(20, 15, 10, .12);
  --shadow-menu:       0 12px 32px -12px rgba(10, 10, 12, .45);
  --shadow-drawer:     -18px 0 40px -24px rgba(10, 10, 12, .5);
  --shadow-banner:     0 -12px 30px -20px rgba(10, 10, 12, .35);
  --shadow-toast:      0 14px 34px -18px rgba(10, 10, 12, .5);
  --shadow-card:       0 1px 2px rgba(10, 10, 12, .04), 0 12px 30px -18px rgba(10, 10, 12, .16);
  --shadow-card-dark:  0 1px 2px rgba(0, 0, 0, .4), 0 14px 34px -18px rgba(0, 0, 0, .55);
  --shadow-card-focus: 0 14px 34px -16px rgba(82, 45, 230, .22);
  --shadow-chip:       0 1px 2px rgba(10, 10, 12, .12);
  --shadow-float:      0 14px 34px -16px rgba(10, 10, 12, .28);
  --shadow-dismiss:    0 2px 6px rgba(10, 10, 12, .14);
  --shadow-photo:      0 1px 3px rgba(0, 0, 0, .16);
  --shadow-copy-toast: 0 8px 28px rgba(20, 16, 40, .18);
  --shadow-dialog:     0 24px 60px -20px rgba(20, 15, 10, .35);

  /* legacy aliases remapped onto the M3 palette (keeps every v3 component class working) */
  --violet-500:      #522DE6;
  --violet-600:      #4109D7;
  --violet-400-warm: #8B7FFF;
  --heart-red:       #FF3040;
  --verified:        #6B4EFF;
  --bg:              #FFF8F4;
  --surface:         #FAF2ED;
  --surface-muted:   #EFE7E1;
  --surface-elevated:#FFFFFF;
  --text:            #1E1B18;
  --text-muted:      #474556;
  --border:          #C9C4D9;
  --accent:          #522DE6;
  --accent-subtle:   #E5DEFF;
  --cover-bg:        #0A0A0C;
  --cover-text:      #F5F5F5;
  --topic-text:      #4109D7;
  --topic-tint:      #E5DEFF;
  --badge-red:       #DD0C2C;
  /* Medium-severity / warning chip. A brand-INDEPENDENT status primitive, like
     --brass / --like / --tertiary: the press palette deliberately keeps the warm
     status colors (spec §1.1 — "crimson --tertiary and the like-heart stay"), and
     an amber already sits in the press warm scale. Its two siblings (low, high)
     were already tokenized; this one was the raw hex left behind. */
  --badge-amber:     #C77700;

  /* type — the Press system (C-S5, token spec §4): Newsreader display/titles ·
     Bodoni Moda masthead (≥28px only, spec §0 floor) · Source Serif 4 reading ·
     DM Sans UI. CJK display/UI rides the unicode-range-scoped faces above;
     the metric-matched Georgia fallback sits second so swap never shifts. */
  --font-display:   "Newsreader", "Newsreader Fallback", "Noto Serif KR", "Noto Serif JP", Georgia, serif;
  --font-masthead:  "Bodoni Moda", "Bodoni 72", Didot, Georgia, serif;  /* the ONE var C-S14 repoints to GT Sectra */
  --font-body:      "DM Sans", "Pretendard", "Noto Sans Arabic", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-read:      "Source Serif 4", Georgia, serif;
  --font-editorial: var(--font-read);   /* FND-002 back-compat alias */

  /* type scale (derived from the v3 mockup; hero step + per-step leading/
     tracking from the Press tracking table — token spec §3: tracking tightens
     as size grows, line-height loosens as it shrinks; eyebrows run tracked
     uppercase in the +12–20% band). */
  --fs-hero:       clamp(56px, 7.5vw, 104px);   /* nameplate/hero — masthead scale */
  --fs-display-xl: clamp(40px, 5.4vw, 68px);
  --fs-display:    40px;
  --fs-heading:    34px;
  --fs-title:      25px;
  --fs-subtitle:   20px;
  --fs-body-lg:    19px;
  --fs-body:       16px;
  --fs-body-sm:    15px;
  --fs-caption:    13.5px;
  --fs-eyebrow:    12.5px;

  --lh-hero: 0.98;        --ls-hero: -0.02em;
  --lh-display-xl: 1.01;  --ls-display-xl: -0.015em;
  --lh-display: 1.04;     --ls-display: -0.01em;
  --lh-heading: 1.08;     --ls-heading: -0.005em;
  --lh-title: 1.15;       --ls-title: 0;
  --lh-subtitle: 1.25;    --ls-subtitle: 0;
  --lh-body-lg: 1.6;      --ls-body-lg: 0;
  --lh-body: 1.45;        --ls-body: 0;
  --lh-body-sm: 1.45;     --ls-body-sm: 0;
  --lh-caption: 1.35;     --ls-caption: 0.005em;
  --lh-eyebrow: 1;        --ls-eyebrow: 0.16em;

  /* spacing + radius (v3 names) */
  --space-xs: 4px; --space-sm: 8px; --space-md: 12px; --space-lg: 16px;
  --space-xl: 24px; --space-xxl: 32px; --space-xxxl: 48px;
  /* --r-action (spec §2, theme-independent): primary buttons/chips/tags/outlined
     controls — "ink is what documents do". --r-pill survives for avatars only;
     the non-avatar pill sweep is the C-S7/C-S11 consumer pass. */
  --r-sm: 6px; --r-md: 10px; --r-lg: 16px; --r-xl: 28px; --r-pill: 999px; --r-action: 3px;

  /* FND-002 back-compat spacing/radius aliases */
  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-6: 24px; --space-8: 32px;
  --radius: 14px;

  --measure:      600px;   /* the boxed reading column */
  --measure-wide: 640px;
  --measure-chat: 900px;   /* DM thread: wider than the editorial reading column so
                              a chat fills the screen instead of a narrow strip */

  /* self-contained editorial grain (feTurbulence, data-URI) — no external image */
  --grain: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.82' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.4 0'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23g)'/%3E%3C/svg%3E");
}

/* ── DARK palette ────────────────────────────────────────────────────────
   Two application paths, no JS required:
   1. prefers-color-scheme: dark is the no-JS default (unless the user has
      explicitly forced light via [data-theme="light"] / .theme-light).
   2. An explicit [data-theme="dark"] / .theme-dark on <html> (FE-308's toggle)
      wins regardless of the system setting. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]):not(.theme-light) {
    --primary:              #8B7FFF;
    --primary-bright:       #C8BFFF;
    --primary-container:    #241A3E;
    --on-primary-container: #C8BFFF;
    --dm-mine-bg:           #302D4B;   /* muted cool block, distinct from the near-black canvas + warm-surface theirs */
    --dm-mine-text:         #ECEAF6;   /* soft near-white — neutral, high-contrast on the dark bubble */
    --ink:                  #F5F5F5;
    --ink-2:                #9AA4B6;
    --outline:              #5F6476;   /* control border — 3.02:1 on --surface-1 (was 2.15:1) */
    --outline-variant:      #393D4A;
    --canvas:               #000000;   /* body base — the dark twin of --bg */
    --surface-1:            #16181C;
    --surface-2:            #1F2228;
    --surface-3:            #23262D;
    --surface-4:            #2A2D38;
    --surface-hi:           #1F2228;
    --cover:                #0A0A0C;

    --violet-500:      #8B7FFF;
    --violet-600:      #C8BFFF;
    --bg:              #000000;
    --surface:         #16181C;
    --surface-muted:   #2A2D38;
    --surface-elevated:#1F2228;
    --text:            #F5F5F5;
    --text-muted:      #9AA4B6;
    --border:          #393D4A;
    --accent:          #8B7FFF;
    --accent-subtle:   #241A3E;
    --topic-text:      #C8BFFF;
    --topic-tint:      #241A3E;
    --badge-red:       #FF5A67;
  }
}

:root[data-theme="dark"],
.theme-dark {
  --primary:              #8B7FFF;
  --primary-bright:       #C8BFFF;
  --primary-container:    #241A3E;
  --on-primary-container: #C8BFFF;
  --dm-mine-bg:           #302D4B;   /* muted cool block, distinct from the near-black canvas + warm-surface theirs */
  --dm-mine-text:         #ECEAF6;   /* soft near-white — neutral, high-contrast on the dark bubble */
  --ink:                  #F5F5F5;
  --ink-2:                #9AA4B6;
  --outline:              #5F6476;   /* control border — 3.02:1 on --surface-1 (was 2.15:1) */
  --outline-variant:      #393D4A;
  --canvas:               #000000;   /* body base — the dark twin of --bg */
  --surface-1:            #16181C;
  --surface-2:            #1F2228;
  --surface-3:            #23262D;
  --surface-4:            #2A2D38;
  --surface-hi:           #1F2228;
  --cover:                #0A0A0C;

  --violet-500:      #8B7FFF;
  --violet-600:      #C8BFFF;
  --bg:              #000000;
  --surface:         #16181C;
  --surface-muted:   #2A2D38;
  --surface-elevated:#1F2228;
  --text:            #F5F5F5;
  --text-muted:      #9AA4B6;
  --border:          #393D4A;
  --accent:          #8B7FFF;
  --accent-subtle:   #241A3E;
  --topic-text:      #C8BFFF;
  --topic-tint:      #241A3E;
  --badge-red:       #FF5A67;
}

/* ── The Press — ink-and-paper override (C-S6, flag-gated) ────────────────
   The web twin of the mobile press theme. layouts/web.html.erb stamps
   data-brand="press" on <html> only while WEB_BRAND_PRESS is on (fail-closed;
   rollback = unset the ENV) — with the attribute absent, none of these blocks
   match and the violet palette above renders byte-identically. Values are the
   §1 palette of documents/branding/press-token-spec.md, never invented.
   Cascade shape: this light block (0,2,0) outranks the generic explicit-dark
   block (0,2,0) by source order, the press media twin (0,4,0) outranks the
   generic media twin (0,3,0), and the press explicit twin (0,3,0) outranks
   this block — so each scheme resolves press values on every path.

   Paper (light): cool gray stock — explicitly NOT the rosy cream #FFF8F4.
   CTAs are ink with paper labels; violet survives ONLY as --brand-hairline
   (spent on the focus ring); the Signature is the deeper brass cut #96733F
   (the panel's #B08D57 fails 3:1 on paper — decorative only); crimson
   --tertiary and the like-heart stay (kept tokens, inherited); every
   elevation shadow retires — hairline rules carry elevation. */
:root[data-brand="press"] {
  --primary:              #0F0E0C;   /* ink CTA fill (17.13 on paper ✓) */
  --primary-bright:       #33302B;   /* hover lift — warm ink, one step up */
  --primary-container:    #E9E4DB;   /* accentSubtle wash (spec §1.3 intent: "near-invisible"). NOT the spec's literal #EDE9F4 — that value is a COOL LILAC carried over from the violet palette, and on warm paper (#F4F1EC) it does not disappear, it reads as a violet pill (the active nav rail). Re-hued to the press stock step so the wash is actually near-invisible, as intended. */
  --on-primary-container: #0F0E0C;
  --primary-tint:         #857E72;   /* borderStrong stands in for the violet tint border */
  --on-primary:           #F4F1EC;   /* paper label on the ink button */
  --dm-mine-bg:           #E7E1D5;   /* warm stone "mine" bubble (the fixed-pair audit item) */
  --dm-mine-text:         #211E18;
  --ink:                  #0F0E0C;
  --ink-2:                #6B6560;
  --outline:              #857E72;   /* control border — the 3:1 hard gate (3.57 ✓) */
  --outline-variant:      #DAD4CA;   /* decorative rule */
  --canvas:               #F4F1EC;   /* paper */
  --surface-1:            #FBFAF8;   /* raised cards, sheets, composer */
  --surface-2:            #EFEBE3;   /* derived stock steps between paper and rule-faint */
  --surface-3:            #E9E4DB;
  --surface-4:            #E2DCD1;
  --surface-hi:           #FBFAF8;
  --cover:                #12100D;   /* press ink, paper-warm — not the cool-violet near-black */

  --violet-500:      #0F0E0C;   /* legacy aliases follow the CTA to ink — alias */
  --violet-600:      #0F0E0C;   /* readers must not leak violet (story rule)   */
  --violet-400-warm: #6B6560;
  --verified:        #96733F;   /* the Signature carries verification (C-S10) */
  --bg:              #F4F1EC;
  --surface:         #FBFAF8;
  --surface-muted:   #E9E4DB;
  --surface-elevated:#FBFAF8;
  --text:            #0F0E0C;
  --text-muted:      #6B6560;
  --border:          #DAD4CA;
  --accent:          #0F0E0C;
  --accent-subtle:   #E9E4DB;   /* warm paper wash — see the note on --primary-container */
  --cover-bg:        #12100D;
  --cover-text:      #EDE8DF;
  --topic-text:      #0F0E0C;
  --topic-tint:      #E9E4DB;

  --brand-hairline:  #522DE6;   /* the ONE surviving violet (6.48 on paper ✓) */
  --focus-ring:      var(--brand-hairline);   /* "the focus ring may keep violet" */
  --signature:       #96733F;   /* 3.87 on paper ✓ ≥3:1 (meaning-bearing) */
  --brass-text:      #77592B;   /* kickers/eyebrows as text (5.74 ✓) */

  /* Shadows retire in BOTH schemes at once (scheme-independent `none`). */
  --shadow-pop: none; --shadow-menu: none; --shadow-drawer: none;
  --shadow-banner: none; --shadow-toast: none; --shadow-card: none;
  --shadow-card-dark: none; --shadow-card-focus: none; --shadow-chip: none;
  --shadow-float: none; --shadow-dismiss: none; --shadow-photo: none;
  --shadow-copy-toast: none; --shadow-dialog: none;
}

/* Ink (dark) — the brand-default posture (spec §1.2): warm near-black, never
   pure #000. Twin 1: the no-JS prefers-color-scheme path (unless explicitly
   forced light), mirroring the generic media block above. */
@media (prefers-color-scheme: dark) {
  :root[data-brand="press"]:not([data-theme="light"]):not(.theme-light) {
    --primary:              #EDE8DF;   /* paper-text CTA fill (15.56 on ink ✓) */
    --primary-bright:       #FBF7EE;
    --primary-container:    #2A261F;   /* accentSubtle dark wash */
    --on-primary-container: #EDE8DF;
    --primary-tint:         #6F675B;
    --on-primary:           #12100D;   /* ink label on the paper button */
    --dm-mine-bg:           #343028;
    --dm-mine-text:         #EDE8DF;
    --ink:                  #EDE8DF;
    --ink-2:                #A79E90;
    --outline:              #6F675B;   /* control border (3.41 ✓) */
    --outline-variant:      #2E2A24;
    --canvas:               #12100D;
    --surface-1:            #1B1815;
    --surface-2:            #24211C;
    --surface-3:            #2A261F;
    --surface-4:            #322D25;
    --surface-hi:           #1B1815;
    --cover:                #12100D;

    --violet-500:      #EDE8DF;
    --violet-600:      #EDE8DF;
    --violet-400-warm: #A79E90;
    --verified:        #B08D57;   /* the classic brass reads true against ink (6.14 ✓) */
    --bg:              #12100D;
    --surface:         #1B1815;
    --surface-muted:   #24211C;
    --surface-elevated:#1B1815;
    --text:            #EDE8DF;
    --text-muted:      #A79E90;
    --border:          #2E2A24;
    --accent:          #EDE8DF;
    --accent-subtle:   #2A261F;
    --cover-bg:        #12100D;
    --cover-text:      #EDE8DF;
    --topic-text:      #EDE8DF;
    --topic-tint:      #24211C;

    --brand-hairline:  #8B7FFF;   /* --focus-ring re-resolves through it */
    --signature:       #B08D57;
    --brass-text:      #C29E67;
  }
}

/* Twin 2: the explicit [data-theme="dark"]/.theme-dark override path (FE-308's
   toggle) — wins regardless of the system setting, mirroring the generic
   explicit block above (the descendant form covers a .theme-dark container
   inside a press page, where element-own tokens beat the inherited light set). */
:root[data-brand="press"][data-theme="dark"],
:root[data-brand="press"].theme-dark,
[data-brand="press"] .theme-dark {
  --primary:              #EDE8DF;
  --primary-bright:       #FBF7EE;
  --primary-container:    #2A261F;
  --on-primary-container: #EDE8DF;
  --primary-tint:         #6F675B;
  --on-primary:           #12100D;
  --dm-mine-bg:           #343028;
  --dm-mine-text:         #EDE8DF;
  --ink:                  #EDE8DF;
  --ink-2:                #A79E90;
  --outline:              #6F675B;
  --outline-variant:      #2E2A24;
  --canvas:               #12100D;
  --surface-1:            #1B1815;
  --surface-2:            #24211C;
  --surface-3:            #2A261F;
  --surface-4:            #322D25;
  --surface-hi:           #1B1815;
  --cover:                #12100D;

  --violet-500:      #EDE8DF;
  --violet-600:      #EDE8DF;
  --violet-400-warm: #A79E90;
  --verified:        #B08D57;
  --bg:              #12100D;
  --surface:         #1B1815;
  --surface-muted:   #24211C;
  --surface-elevated:#1B1815;
  --text:            #EDE8DF;
  --text-muted:      #A79E90;
  --border:          #2E2A24;
  --accent:          #EDE8DF;
  --accent-subtle:   #2A261F;   /* warm ink wash (was a violet-ink among warm inks) */
  --cover-bg:        #12100D;
  --cover-text:      #EDE8DF;
  --topic-text:      #EDE8DF;
  --topic-tint:      #24211C;

  --brand-hairline:  #8B7FFF;
  --signature:       #B08D57;
  --brass-text:      #C29E67;
}

/* ── Always-on base + the responsive layout shell (FE-303) ───────────────── */
* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body-lg);
  line-height: 1.6;
  /* Press type (C-S5): let the variable fonts' opsz axis resolve per rendered
     size — display cuts at masthead scale, text cuts in running copy. Inherits
     everywhere; display selectors that set the `font` shorthand re-state it. */
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

/* Global visible focus ring (mockup review F12 — WCAG 2.4.7). Rides the
   --focus-ring seam: --primary under violet, the violet --brand-hairline under
   press (C-S6 — the one accent spend the press palette keeps). */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* Tabular numerals wherever a number changes in place — timestamps, folio
   numbers ("No. 214"), unread badges, counts (press-token-spec.md §6). */
.tnum { font-variant-numeric: tabular-nums; }

/* Skip-link — first focusable element, visually hidden until focused. */
.skip-link {
  position: absolute;
  /* Park OFF the top edge (not left: -9999px): a negative inline offset extends the
     document's scroll width under dir="rtl" (the scroll origin flips), so an RTL
     page would get ~10000px of phantom horizontal scroll. Parking vertically is
     direction-agnostic. inset-inline-start keeps it at the start edge (left in LTR,
     right in RTL) when it slides in on focus. */
  inset-inline-start: 0;
  top: -9999px;
  z-index: 100;
  background: var(--primary);
  color: var(--on-primary);
  font-family: var(--font-body);
  font-weight: 600;
  text-decoration: none;
  padding: var(--space-sm) var(--space-lg);
  border-radius: 0 0 var(--r-md) 0;
}
.skip-link:focus { top: 0; }

/* 3-zone shell: single column (mobile) → nav + content (tablet) → nav + content
   + Newsstand rail (desktop). Pure CSS Grid, zero JS. The nav/rail zone widths
   come from their own components; the shell just places them. */
.web-shell { max-width: 1400px; margin: 0 auto; }
.web-shell__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  align-items: start;
}
.web-shell__main {
  min-width: 0;
  width: 100%;
  max-width: var(--measure-wide);
  margin: 0 auto;
  padding: var(--space-xl) var(--space-lg) var(--space-xxxl);
}
.web-shell__main:focus { outline: none; }
/* The nav zone is present at EVERY width: it carries the desktop rail (≥768px)
   AND the mobile top bar (<768px), which swap via .rail-full / .mnav
   (web/components.css). The Newsstand rail zone stays desktop-only. */
.web-shell__nav { display: block; }
.web-shell__rail { display: none; }

/* Nav-less surfaces (the pre-auth doors — sign in / join / reset — and the legal
   + marketing doc pages) emit no nav or rail track. Without this, the lone <main>
   auto-places into the FIRST (nav) track and pins hard-left, leaving a huge empty
   right half (worst on the auth cards). Collapse the grid to a single centered
   column for them. Its (0,2,0) specificity beats the (0,1,0) media-query track
   rules below, so it wins at every width with no source-order dependence. */
.web-shell__grid:not(:has(.web-shell__nav)) { grid-template-columns: minmax(0, 1fr); }

@media (min-width: 768px) {
  .web-shell__grid { grid-template-columns: auto minmax(0, 1fr); }
}
@media (min-width: 1100px) {
  .web-shell__grid { grid-template-columns: auto minmax(0, 1fr) auto; }
  .web-shell__rail { display: block; }
}
