/* ============================================================
 * imjustdex.com — tokens.css
 * Design tokens, font-face declarations, base styles.
 *
 * Single source of truth for the editorial design system.
 * Edit a token here and every page updates on next deploy.
 *
 * Loaded BEFORE every other stylesheet on every page.
 * ============================================================ */

/* ── Fonts: self-hosted, Latin-subset WOFF2 ────────────────── */

@font-face {
  font-family: 'Anton';
  src: url('/fonts/Anton-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/fonts/IBMPlexSans-Regular.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

@font-face {
  font-family: 'IBM Plex Sans';
  src: url('/fonts/IBMPlexSans-Bold.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ── Design tokens: light mode (default) ───────────────────── */

:root {
  /* Surfaces */
  --bg: #f4f4f1;
  --panel: #f8f8f6;
  --ink: #050505;
  --muted: #202020;

  /* Edges */
  --border: #0a0a0a;
  --rule: rgba(0, 0, 0, .14);
  --shadow: none;

  /* Meta rail */
  --meta-bg: #0a0a0a;
  --meta-ink: #f5f5f1;

  /* Accent doctrine — one structural red, one functional exception.
     --accent         : THE brand red (#c00). Used for borders, fills,
                        backgrounds, underline decoration, and large
                        display text. Identical in light and dark mode —
                        this is the single visual red of the system.
     --accent-text    : red for SMALL text only. Flips to --accent-on-dark
                        in dark mode (the ONE exception) because #c00 on
                        #060606 = 3.45:1 which fails AA normal for small
                        text. Use this token only on sub-18px red text
                        or hover states on body copy. Everything else
                        uses --accent.
     --accent-on-dark : brighter red (#ff4d4d) — 7.54:1 on #060606. The
                        functional red for small text on dark. Do not
                        use directly; it powers --accent-text's dark flip.
     --focus-ring     : focus outline color. Always #c00 to match --accent. */
  --accent: #c00;
  --accent-text: #c00;
  --accent-on-dark: #ff4d4d;
  --focus-ring: #c00;

  /* Body text tonal ladder */
  --body-color: rgba(5, 5, 5, .82);
  --body-muted: rgba(5, 5, 5, .56);
  --body-faint: rgba(5, 5, 5, .18);
  --body-tint: rgba(5, 5, 5, .04);

  /* Plate-scoped surface tokens (Phase 10)
     Image plates and the identity plate stay dark in both modes.
     These tokens give that doctrine a name so plates.css never
     needs raw hex for mode-specific surfaces.
     --plate-overlay-ink : white text forced on image overlays.
     --identity-plate-bg : identity plate background — in light mode
                           inherits --ink; dark mode overrides to #0a0a0a
                           (slightly lighter than page --bg #060606 so
                           the plate doesn't vanish into the background).
     --identity-plate-ink: identity plate text — inverts against bg.
     --plate-image-meta-* : meta rail on image plates — light-mode
                            colors forced in both modes because image
                            plates never invert. */
  --plate-overlay-ink: #fff;
  --identity-plate-bg: var(--ink);
  --identity-plate-ink: var(--bg);
  --plate-image-meta-bg: #f5f5f1;
  --plate-image-meta-ink: #0a0a0a;
  --plate-image-meta-border: #0a0a0a;

  /* Layout
     --outer-pad is a scope-excluded layout primitive; it lives
     outside the spacing scale because the responsive step-down
     (28→18 at 760px) is tuned to the page frame, not rhythm.
     --grid-gap points at the scale because it matches naturally. */
  --grid-gap: var(--space-sm);
  --outer-pad: 28px;
  --tile-min: 190px;

  /* ── Spacing scale (Phase 6) ────────────────────────────────
     Single-tier semantic system. No primitive/semantic split —
     spacing has no fluid/fixed or display/sub-display distinction
     to encode, so one layer is enough.

     Strict 4px base with one deliberate slot (--space-chrome at
     14px) for the chrome micro-padding tier sitting between sm
     and md. Chrome breathes differently from content; naming
     the tier makes that legible.

     Consumers call --space-* aliases. Raw pixel values only
     appear in three documented optical corrections (uppercase
     chrome pulling 1px up from visual center on brand-block,
     mode-toggle, footer-strip) — inline commented where they
     live. Zero other exceptions. */
  --space-0:        0;
  --space-hairline: 4px;   /* icon gutters, tiny offsets */
  --space-xs:       8px;   /* tight internal padding */
  --space-sm:       12px;  /* standard internal, grid gaps */
  --space-chrome:   14px;  /* chrome tier — buttons, share bar, meta rails */
  --space-md:       16px;  /* primary component padding */
  --space-lg:       24px;  /* generous component padding */
  --space-xl:       32px;  /* block gaps */
  --space-2xl:      48px;  /* section gaps, closing block */
  --space-3xl:      80px;  /* page rhythm, article bottom */

  /* Border weights — codify the "content vs chrome" doctrine.
     Content frames (plates, article header/body/closing, pull
     quotes, nav) use 3px. Chrome (buttons, tags, brand, footer)
     uses 2px. Scoped so a single edit rebalances the whole system. */
  --border-weight-content: 3px;
  --border-weight-chrome: 2px;

  /* Focus ring width — outline thickness for :focus-visible states.
     Matches --border-weight-chrome (2px) so focus rings feel tuned
     to the same measurement system. outline-offset stays local per
     selector: .plate and .notfound-plate inset the ring (-5px/-4px)
     so it rides inside the 3px content border without competing. */
  --focus-ring-width: 2px;

  /* ── Motion scale (Phase 8) ─────────────────────────────────
     Three-rung duration ladder + one easing curve. Durations
     and easing are authored as orthogonal tokens so hover
     chrome, reading progress, and mode transitions compose
     from the same vocabulary.

     --motion-fast  : 120ms — hover chrome (borders, backgrounds),
                      reading progress (paired with linear).
     --motion-med   : 180ms — mode toggle body flip, opacity
                      transitions on small chrome.
     --motion-slow  : 300ms — section indicator fade, ambient
                      reveals where a shorter duration would
                      feel abrupt.
     --ease-default : cubic-bezier(.2, .8, .2, 1) — the single
                      curve. Used anywhere a non-linear feel is
                      desired. Linear stays linear (progress bar).

     Doctrine: if a duration isn't in this scale, it doesn't
     belong in the product. --motion-tiny was collapsed into
     --motion-fast + --ease-default to keep the vocabulary
     orthogonal. Everything else stays instant. */
  --motion-fast:   120ms;
  --motion-med:    180ms;
  --motion-slow:   300ms;
  --ease-default:  cubic-bezier(.2, .8, .2, 1);

  /* Typography stacks
     Anton: open-source condensed display (Impact-equivalent).
     Impact is preserved as a native fallback for macOS/iOS/Windows
     users who already have it — identical anatomy, no layout shift. */
  --display: 'Anton', Impact, Haettenschweiler, 'Arial Narrow Bold', sans-serif;
  --body: 'IBM Plex Sans', system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  --serif: Georgia, 'Times New Roman', serif;
  --mono: 'SF Mono', 'Fira Code', 'Cascadia Code', 'Courier New', monospace;

  /* ── Typography scale ───────────────────────────────────────
     Two-tier system: primitives define the scale, semantic
     aliases define intent. Consumers should prefer aliases;
     drop to primitives only when intent doesn't fit.

     Primitives (--size-*) are empirically derived from existing
     editorial values. Sub-display sizes are consolidated where
     differences sat below the perception threshold (sub-pixel
     noise). Display tier preserves every distinct clamp —
     those were tuned by eye and collapsing them would visibly
     shift the site. Sub-display stays fixed rem; display tier
     uses clamp() for fluid scaling. */

  /* Primitive scale — sub-display, fixed
     .7/.72 consolidated to .72, .8/.82 to .82, .84/.88 to .88,
     1/1.02 to 1. All other distinctions preserved. */
  --size-1: .6rem;        /* 9.6px   — meta rail */
  --size-2: .72rem;       /* ~11.5px — micro label */
  --size-3: .75rem;       /* 12px    — chip / eyebrow */
  --size-4: .82rem;       /* ~13.1px — citation */
  --size-5: .88rem;       /* ~14.1px — nav */
  --size-6: .92rem;       /* ~14.7px — chrome-md */
  --size-7: 1rem;         /* 16px    — body base */
  --size-8: 1.08rem;      /* ~17.3px — lede */
  --size-9: 1.15rem;      /* ~18.4px — pull quote */

  /* Primitive scale — display, fluid
     Every distinct editorial clamp preserved as its own step. */
  --size-10: clamp(1.2rem, 2vw, 1.6rem);       /* h3, article-nav */
  --size-11: clamp(1.35rem, 2.4vw, 2.4rem);    /* identity tagline */
  --size-12: clamp(1.6rem, 4vw, 2.4rem);       /* pull quote, h2 */
  --size-13: clamp(1.6rem, 5vw, 2.6rem);       /* stat block */
  --size-14: clamp(2rem, 4vw, 4rem);           /* plate-title base */
  --size-15: clamp(2.35rem, 5vw, 5rem);        /* plate-title.med */
  --size-16: clamp(2.8rem, 7vw, 5rem);         /* article h1 */
  --size-17: clamp(3rem, 10vw, 9rem);          /* 404 display */
  --size-18: clamp(3.5rem, 7vw, 7.2rem);       /* plate-title.lg */
  --size-19: clamp(3.8rem, 10vw, 7.5rem);      /* identity name */

  /* Semantic aliases — role-based intent, point at primitives.
     Added only when there's an immediate consumer — do not
     speculate. New intents earn a new alias; one-offs drop to
     the primitive directly. */
  --text-meta: var(--size-1);
  --text-micro: var(--size-2);
  --text-label: var(--size-3);
  --text-citation: var(--size-4);
  --text-nav: var(--size-5);
  --text-chrome: var(--size-6);
  --text-body: var(--size-7);
  --text-lede: var(--size-8);
  --text-quote: var(--size-9);
  --text-h3: var(--size-10);
  --text-tagline: var(--size-11);
  --text-h2: var(--size-12);
  --text-stat: var(--size-13);
  --text-plate: var(--size-14);
  --text-plate-med: var(--size-15);
  --text-h1: var(--size-16);
  --text-display: var(--size-17);
  --text-plate-lg: var(--size-18);
  --text-identity: var(--size-19);

  /* Line-height ladder — decoupled from size. One ratio repeats
     across many sizes (body at 1.7 works for 1rem paragraphs
     and 1.08rem lede); coupling it to font-size would force
     one-off tokens for no gain. Four ratios + one exception. */
  --lh-display: .86;   /* headlines, plate mega, 404 */
  --lh-tight:   1.05;  /* h2, h3, pull quote */
  --lh-snug:    1.4;   /* chrome, nav, meta */
  --lh-normal:  1.7;   /* article body */
  --lh-loose:   1.85;  /* article lede */

  /* ── Letter-spacing scale (Phase 7) ─────────────────────────
     12 semantic slots preserving every distinct editorial
     tracking value on the site. Each value is tuned to a
     specific text role; consolidating near-neighbors would
     introduce visible drift on uppercase chrome where a .03em
     delta IS perceptible at tag-chip sizes.

     Negative tracking is for display type only (h1, plate
     titles, pull-quotes, stat blocks, taglines) where tight
     letterforms read as editorial confidence. Positive
     tracking is for uppercase chrome — brand, nav, labels,
     badges — where the spacing gives the caps room to breathe.

     Zero exceptions. Every letter-spacing site on the
     site points at a --track-* token. */
  --track-display:    -.03em;  /* article h1, plate titles, stat block, 404 headline */
  --track-title:      -.02em;  /* pull-quote, article-nav title, identity tagline */
  --track-brand:       .03em;  /* brand-mark / brand-word only */
  --track-nav:         .04em;  /* mode toggle, article eyebrow date */
  --track-footer:      .06em;  /* footer-strip items */
  --track-badge:       .08em;  /* meta-rail, identity-sub, 404 actions */
  --track-chrome:      .1em;   /* article meta, article-back, share bar */
  --track-tag:         .12em;  /* tag chip, stat microlabel, article-nav label */
  --track-teaser:      .14em;  /* identity meta-rail, teaser-date */
  --track-label:       .15em;  /* section-head h2, scripture cite, research-cta */
  --track-indicator:   .18em;  /* section-indicator (fixed editorial mark) */
  --track-eyebrow:     .22em;  /* 404 eyebrow (widest tracking on the site) */

  /* Font weights — binary system. 400 for all body and display
     type, 700 for all chrome, labels, tags, and strong emphasis.
     No intermediate weights. Tokens earn their keep by making
     the binary legible at the call site. */
  --weight-regular: 400;
  --weight-bold:    700;

  color-scheme: light dark;
}

/* ── Design tokens: dark mode ──────────────────────────────── *
 * Two layers defend against dark-mode FOUC:
 *
 *  1. CSS fallback — @media (prefers-color-scheme: dark) fires
 *     before any JS, giving OS-dark users dark tokens instantly.
 *     Scoped to :root:not(.mode-resolved) so it yields once JS
 *     has read the cookie and made its decision.
 *
 *  2. JS class — html.dark-mode is set by the blocking <head>
 *     script (mode.js). Cookie overrides OS preference. JS adds
 *     .mode-resolved after resolving, retiring the CSS fallback.
 *
 * Together: CSS covers the JS-fails / JS-delayed gap. JS covers
 * explicit user preference. No flash in either scenario.
 * ------------------------------------------------------------ */

@media (prefers-color-scheme: dark) {
  :root:not(.mode-resolved) {
    --bg: #060606;
    --panel: #0d0d0d;
    --ink: #f3f0e8;
    --muted: #d7d3ca;
    --border: #f3f0e8;
    --rule: rgba(255, 255, 255, .16);
    --meta-bg: #f3f0e8;
    --meta-ink: #0a0a0a;
    --accent-text: var(--accent-on-dark);
    --body-color: rgba(243, 240, 232, .82);
    --body-muted: rgba(243, 240, 232, .52);
    --body-faint: rgba(243, 240, 232, .14);
    --body-tint: rgba(243, 240, 232, .04);
    --identity-plate-bg: #0a0a0a;
    --identity-plate-ink: #f3f0e8;
  }
}

html.dark-mode {
  --bg: #060606;
  --panel: #0d0d0d;
  --ink: #f3f0e8;
  --muted: #d7d3ca;
  --border: #f3f0e8;
  --rule: rgba(255, 255, 255, .16);
  --meta-bg: #f3f0e8;
  --meta-ink: #0a0a0a;
  /* Accent stays #c00 in dark mode — one structural red across both modes.
     ONLY --accent-text flips to --accent-on-dark (#ff4d4d) because #c00
     on #060606 = 3.45:1 which fails WCAG AA normal for small red text.
     Borders, fills, underline decoration, and large display text all
     stay #c00 and read correctly against dark because non-text contrast
     only requires 3:1 — which #c00/#060606 clears at 3.45:1. */
  --accent-text: var(--accent-on-dark);
  --body-color: rgba(243, 240, 232, .82);
  --body-muted: rgba(243, 240, 232, .52);
  --body-faint: rgba(243, 240, 232, .14);
  --body-tint: rgba(243, 240, 232, .04);
  /* Identity plate: #0a0a0a bg (not #060606) so the dark plate
     lifts off the dark page background by ~4 LAB units. */
  --identity-plate-bg: #0a0a0a;
  --identity-plate-ink: #f3f0e8;
}

/* ── Base reset + root typography ──────────────────────────── */

*,
*::before,
*::after {
  box-sizing: border-box;
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--body);
  font-size: var(--text-body);
  font-weight: var(--weight-regular);
  line-height: var(--lh-snug);
  min-height: 100vh;
  min-height: 100dvh;
  transition: background var(--motion-med) ease, color var(--motion-med) ease;

  /* 46px ruler grid background — locked to the typographic grid */
  background-image:
    linear-gradient(to right, transparent 0, transparent calc(100% - 1px), var(--rule) calc(100% - 1px)),
    linear-gradient(to bottom, transparent 0, transparent calc(100% - 1px), var(--rule) calc(100% - 1px));
  background-size: 46px 46px;
}

a {
  color: inherit;
  text-decoration: none;
}

img,
svg {
  max-width: 100%;
  display: block;
}

button {
  font-family: inherit;
}

::selection {
  background: var(--ink);
  color: var(--bg);
}

/* ── Print ─────────────────────────────────────────────────── */

@media print {
  body {
    background: #fff !important;
    color: #000 !important;
    background-image: none !important;
  }

  .ruler-top,
  .ruler-left,
  .mode-toggle,
  .share-bar,
  .reading-progress,
  .section-indicator,
  .research-cta {
    display: none !important;
  }

  .plate,
  .article-header,
  .article-body {
    border-color: #000 !important;
    break-inside: avoid;
  }

  .masthead {
    position: static;
    background: none;
  }
}

/* ── Reduced motion ────────────────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0s !important;
    animation-duration: 0s !important;
  }
}
