/* ============================================================
   Forum Design System — TOKENS
   Single source of truth for all design values. No raw hex/px/oklch
   outside this file; reference the variables everywhere else.
   Ported verbatim from the approved mockups in "Forum Designs/".
   ============================================================ */
:root{
  /* Brand */
  --nd-navy:#0C2340;
  /* --nd-navy used to be read directly as both a fill and a text colour --
     the masthead's background and a thread title's ink are the same value by
     coincidence, not by design. That coincidence breaks in dark mode: the
     chrome barely moves, but navy-as-ink has to go light or every headline
     disappears. Two role tokens, both --nd-navy in light mode so this split
     is a no-op today; dark-mode.md redefines them independently. Read one of
     these, never --nd-navy itself, outside this file.
     docs/forum/plans/dark-mode.md, "--nd-navy does two jobs". */
  --nd-navy-surface:var(--nd-navy);      /* masthead, footer, drawer, navy-filled buttons */
  --nd-navy-ink:var(--nd-navy);          /* headlines, navy links and icons, accent borders */
  --nd-navy-surface-hover:#0a1c32;       /* hover shade for --nd-navy-surface */
  --nd-green:oklch(0.48 0.115 150);
  --nd-green-hover:oklch(0.43 0.12 150);
  --nd-green-soft:oklch(0.95 0.04 148);
  --nd-green-ink:oklch(0.35 0.1 150);
  --nd-gold:oklch(0.72 0.14 82);
  --gold:var(--nd-gold);
  /* Every "gold X%, transparent" mix composites against whatever sits behind
     the element -- near-white in light mode, reading as a pale cream wash.
     This token exists so dark mode has somewhere to redefine that base
     rather than being stuck with the literal keyword. It does NOT yet fix
     the dark-mode result: checked in the browser, gold mixed toward the dark
     card (in srgb, --card-raised's own navy-leaning hue) reads as a muddy
     olive, and mixed in oklch it swings through teal instead -- gold (hue 82)
     and the dark surface (hue ~258) are far enough apart that a simple
     two-color mix passes through unrelated hues in every space tried so far.
     Still open; see "Known issue" under Dark theme in DESIGN_SYSTEM.md and
     the open questions in dark-mode.md. Not applied to the two
     fade-to-nothing gradients (.topic.is-hot, .va-card.is-op-reply), which
     want a true fade, not a flat tint, and are a separate problem. */
  --gold-mix-base:transparent;
  --gold-border:color-mix(in srgb, var(--nd-gold) 38%, var(--gold-mix-base));
  /* Visited links. --nd-gold's own hue, dropped in lightness until it clears
     WCAG AA (4.5:1) on both --bg and the white card; the accent gold itself is
     only 2.4:1 and unreadable at title size. Mixing toward navy also works but
     desaturates into olive, so darken in oklch and keep hue 82. */
  --link-visited:oklch(0.56 0.125 82);
  --accent:var(--nd-navy-ink);
  --accent-hover:#0a1c32;
  --accent-tint:rgba(12,35,64,0.08);

  /* Surfaces & ink */
  --bg:oklch(0.985 0.006 85);
  --bg-sunk:oklch(0.97 0.008 82);
  --bg-elevated:oklch(0.995 0.004 85);
  --bg-muted:#EDECEA;
  --card:oklch(0.995 0.004 85);
  --card-raised:#ffffff;
  --text:oklch(0.22 0.015 70);
  --text-muted:oklch(0.52 0.01 75);
  --ink:var(--text);
  --ink-2:oklch(0.34 0.012 72);
  --ink-3:var(--text-muted);
  --ink-inverse:#ffffff;
  --ink-4:oklch(0.62 0.008 78);
  --line:oklch(0.9 0.008 80);
  --line-soft:oklch(0.915 0.01 78);
  --line-strong:oklch(0.82 0.01 78);
  --chip-bg:#EDECEA;
  /* Row hover -- .topic, .rt-row, .indexRow, table rows.
     This was oklch(0.99) against a #fff card: one point of lightness, which
     is not a visible hover state, just a value that technically differs.
     Nobody noticed until dark mode put a properly-weighted hover (4.5 points
     against its own card) next to it and the light one read as broken.
     0.965 gives light mode ~3.5 points -- a step comparable to --bg-sunk's
     own remove-from-white, which is the weight this system already uses for
     "slightly recessed". Kept as its own token rather than pointed at
     --bg-sunk: a hover state and a recessed surface are different jobs, and
     this file already has one cautionary tale about one token doing two. */
  --row-hover:oklch(0.965 0.006 82);
  /* A reply preview row on the forum index sits INSIDE .topic, so hovering
     one necessarily hovers the topic too. At --row-hover it would paint the
     same colour its parent just painted and read as nothing at all, so the
     nested step has to clear the one above it, not the card. Same step size
     again (~3.5 points here, 4.5 in dark), so card -> topic -> reply reads
     as an even progression rather than two arbitrary greys. */
  --row-hover-nested:oklch(0.93 0.008 82);

  /* Status */
  --success:var(--nd-green);
  --success-tint:rgba(0,132,61,0.12);
  --danger:#c41e1e;
  --danger-tint:rgba(196,30,30,0.12);
  --gold-tint:color-mix(in srgb, var(--nd-gold) 22%, var(--gold-mix-base));
  /* Dark text on a gold tint -- the hot-replies chip, OP/veteran badges, the
     View All OP flag. Needs its own dark-mode value; a dark ink this dark
     stays illegible on any tint light enough to still read as "gold". */
  --gold-chip-ink:#5e4a0a;
  --gold-chip-ink-2:#8a6e10;

  /* Public-site extensions */
  --win:var(--nd-green);
  --loss:var(--danger);
  --live:var(--nd-green);
  --upcoming:var(--ink-3);
  --final:var(--ink-2);
  --ad-frame:var(--line-soft);
  --ad-label-ink:var(--ink-4);

  /* Forum-specific — alias existing tokens but named for forum semantics so
     vote/thread treatments can diverge later without rewriting call sites. */
  --vote-pos:var(--nd-green);
  --vote-neg:var(--danger);
  --thread-rule:var(--line-strong);

  /* Type */
  --font-sans:"Inter",system-ui,sans-serif;
  --font-serif:"Source Serif 4",Georgia,serif;
  --font-title:var(--font-serif);
  --font-mono:"JetBrains Mono",ui-monospace,monospace;

  --type-doc-body-size:18px;
  --type-doc-body-line:1.7;
  --type-meta-size:13px;
  --type-micro-size:11px;
  --type-micro-tracking:0.12em;
  --type-mono-label-size:10px;
  --type-mono-label-tracking:0.12em;

  --type-article-headline-size:clamp(2.25rem, 3.4vw, 3rem);
  --type-section-title-size:0.6875rem;

  /* Post content sizes -- the reader text size setting's territory
     (docs/forum/plans/user-text-size.md). One token per element per surface,
     because the setting does not move them all by the same amount. These are
     the medium values; the setting redefines them for small and large.

     Components read these and nothing else: no rule anywhere, in any media
     query, may set font-size on one of these elements directly. A literal in
     a later-loading file (pages.css loads last) beats the token, and the
     setting silently stops working at that width. */
  /* Surface 1: forum index */
  --fs-thread-subject:16px;       /* .topic-title */
  --fs-thread-meta:12.5px;        /* .topic-meta -- author, time, reply count */
  --fs-reply-subject:13px;        /* .reply-preview .pr-title */
  --fs-reply-meta:11.5px;         /* .reply-preview .pr-by */
  /* Surface 2: reply index (single post) */
  --fs-post-title:clamp(1.5rem, 2.4vw, 2rem); /* .op-title */
  --fs-post-body:17px;            /* .op-body */
  /* 2026-09-15: a reply reads the same wherever it's shown. .rt-title/.rt-meta
     used to run a pixel ahead of .pr-title/.pr-by on purpose ("the thread you
     opened, not a preview of one") -- asked to match instead, and pointing
     these at the index's own tokens makes that permanent rather than two
     numbers that happen to agree today. */
  --fs-tree-subject:var(--fs-reply-subject); /* .rt-title */
  --fs-tree-meta:var(--fs-reply-meta);       /* .rt-meta */
  /* Surface 3: View All -- the focused post is sized to its replies, not to
     the single post page, so it has tokens of its own (see pages.css) */
  --fs-va-subject:18px;           /* .va-title, .va-layout .op-title */
  --fs-va-body:16px;              /* .va-body, .va-layout .op-body */
  --fs-va-meta:13px;              /* .va-byline .who */

  /* Row and section spacing that the setting now moves too (see the small/
     large blocks below) -- medium keeps today's literals exactly; these
     tokens just give the other two steps somewhere to land. */
  --sp-topic-row-v:12px;          /* .topic vertical padding */
  --sp-topic-title-gap:6px;       /* .topic-title-row margin-bottom */
  --sp-reply-preview-gap:8px;     /* .reply-preview margin-top */
  --sp-reply-row-v:3px;           /* .reply-preview .pr-row vertical padding */
  --sp-op-title-gap:12px;         /* .op-title margin-bottom */
  --sp-op-author-pad:10px;        /* .op-author-row vertical padding */
  --sp-op-body-gap:6px;           /* .op-body margin-top, no tools row */
  --sp-op-body-gap-2:14px;        /* .op-author-row + .op-body margin-top */
  --sp-rt-row-v:var(--sp-reply-row-v); /* .rt-row vertical padding -- matches the index's reply row */
  --vote-col-arrow-h:20px;        /* .vote-col button/.upvote/.downvote height */
  --vote-col-score-pad:1px;       /* .vote-col .score vertical padding */
  --lh-reply-row:1.35;            /* .reply-preview .pr-row line-height */
  --lh-tree-subject:var(--lh-reply-row); /* .rt-title line-height -- matches the index's reply row */

  /* Geometry */
  --radius:6px;
  --r-card:8px;
  --r-input:6px;

  /* Shadows */
  --shadow-sm:0 1px 2px rgba(12,35,64,0.06);
  --shadow-card:0 1px 0 oklch(1 0 0 / .9) inset, 0 1px 2px oklch(0.2 0.02 70 / .04), 0 2px 6px oklch(0.2 0.02 70 / .035);

  /* Layout */
  --page-max:1180px;
  --page-pad:32px;
  --utility-h:32px;
  --masthead-h:88px;
}

/* Reader text size: small and large redefine the post content tokens above.
   Medium is :root itself, and the attribute is absent for it (base.twig).
   :root[...] rather than [...] so these outrank the plain :root redefinitions
   inside the phone media query below on specificity, not on source order.

   2026-09-13 rework (docs/forum/plans/user-text-size.md): small now unifies
   every "subject line" size -- a new topic and a reply are the same kind of
   thing to read at a glance, so .topic-title, .pr-title and .rt-title all
   read at one size, with the byline/meta line just slightly under it. Row
   and section spacing also moves now, tightening at small and opening up at
   large -- spacing was deliberately frozen in the original version of this
   feature, but "condensed, no empty whitespace" and "more styled, more
   pleasant" both asked for it to move too. */
:root[data-textsize="small"]{
  /* .topic-title is serif (--font-serif) against .pr-title/.rt-title's sans,
     and at the same px the serif reads visually smaller -- checked in the
     browser next to a reply directly below it. 13.5px against their 13px
     closes that gap; the two are meant to read as the same size, not
     necessarily share a literal number. */
  --fs-thread-subject:13.5px;
  --fs-thread-meta:11.5px;
  --fs-reply-subject:13px;
  --fs-reply-meta:11.5px;
  --fs-post-title:clamp(1.4375rem, 2.3vw, 1.9375rem);
  --fs-post-body:16px;
  --fs-va-subject:17px;
  --fs-va-body:15px;
  --fs-va-meta:12.5px;

  --sp-topic-row-v:8px;
  --sp-topic-title-gap:3px;
  --sp-reply-preview-gap:4px;
  /* 2026-09-14: components.css calls 2px the floor for the reply-tree row's
     hover target and its title's descenders, and that held right up until
     this was checked at small in the browser -- a subject wrapping to two
     lines there made every row's own padding read as real air on top of the
     leading it already has, and the ask was to cut it further despite the
     floor. 1px it is, here and on the index's own reply row (--sp-rt-row-v
     matches this one, tokens.css root); the connector tick position and
     hover band aren't visibly hurt by it at this size. */
  --sp-reply-row-v:1px;
  --sp-op-title-gap:8px;
  --sp-op-author-pad:6px;
  --sp-op-body-gap:4px;
  --sp-op-body-gap-2:8px;
  --lh-reply-row:1.15;
  /* 2026-09-15: the vote column's arrows sit at a fixed height regardless of
     text size, so as the row around it condensed for small, the column's own
     height stopped shrinking with it -- the downvote arrow ended up low
     enough to collide with the reply-preview thread line starting just below
     it. Bring the arrows in against the count instead of leaving them at
     medium's height. */
  --vote-col-arrow-h:15px;
  --vote-col-score-pad:0px;
}
:root[data-textsize="large"]{
  --fs-thread-subject:18px;
  --fs-thread-meta:13.5px;
  --fs-reply-subject:15px;
  --fs-reply-meta:12.5px;
  --fs-post-title:clamp(1.625rem, 2.6vw, 2.125rem);
  --fs-post-body:19px;
  --fs-va-subject:20px;
  --fs-va-body:18px;
  --fs-va-meta:14px;

  --sp-topic-row-v:16px;
  --sp-topic-title-gap:9px;
  --sp-reply-preview-gap:12px;
  --sp-reply-row-v:5px;
  --sp-op-title-gap:18px;
  --sp-op-author-pad:14px;
  --sp-op-body-gap:10px;
  --sp-op-body-gap-2:18px;
  --lh-reply-row:1.4;
  --vote-col-arrow-h:22px;
  --vote-col-score-pad:2px;
}

/* The page gutter is the one token that has to move with the viewport. At
   32px a side it eats ~16% of a 390px phone as empty margin, which is what
   made the topic cards read as cramped. Redefined rather than overridden per
   component so all four consumers travel together — .page (base.css),
   .utility-inner, .masthead-inner and .footer-inner (chrome.css) — keeping the
   search field, wordmark, breadcrumbs, cards and footer on one left edge.
   Still a real margin, so the cards keep the border and shadow that read well
   on desktop; this narrows the gutter rather than going edge-to-edge. */
@media (max-width:640px){
  :root{--page-pad:14px}
  /* Phone: the index preview's reply subject was the smallest thing on the
     card that a reader is actually meant to read — 12.5px against the topic
     title, and only 1px clear of its own 11.5px byline. On a phone that read
     as caption text rather than as a list of posts to choose from, so the
     subjects were being skipped.

     13.5px puts 2px between the subject and its byline, which is enough for
     the subject to lead. 14px was tried first and read too large against the
     rows around it. It is the only post content size that differs by width;
     the reply tree keeps its 14px everywhere — it is the post you opened
     rather than a preview of one, so its subject never competed with a byline
     the way this one did. */
  :root{--fs-reply-subject:13.5px}
  /* Each step's own phone value, after the step block above so it wins at
     equal specificity. Small pins this back to its own (now unified) desktop
     value rather than inheriting medium's phone bump, or the reply subject
     would read larger than the topic subject it's supposed to match. Large
     just matches its own desktop value for the same reason. */
  :root[data-textsize="small"]{--fs-reply-subject:13px}
  :root[data-textsize="large"]{--fs-reply-subject:15px}
}

/* ============================================================
   DARK THEME
   docs/forum/plans/dark-mode.md -- "The gate" and "The dark palette".

   Gated on BOTH data-theme and data-theme-ready: data-theme reflects the
   reader's account/localStorage choice and is rendered on every page, but
   these tokens only apply where the page's own template has opted in (once
   its CSS is converted -- see the theme_ready block in base.twig). A reader
   in dark mode who opens a page that has not opted in sees it fully light,
   never half-converted -- an admin table cell with a literal white
   background would otherwise inherit light text from this block and go
   unreadable.

   @media screen: dark is a screen-only concept here. The forum has no print
   stylesheet, and without this a reader printing in dark mode would print
   light text onto white paper -- browsers already drop backgrounds from
   prints by default, so leaving colour-scheme/these tokens active under
   print would do exactly that.

   Every value below is a STARTING point (from
   https://claude.ai/artifact/2f3LnRCbX4VgfqAwHebnhJ), not a final one --
   tuned in a browser, one commit per adjustment, the same process the
   text-size steps went through. Three are structural rather than taste:
   --bg sits below --nd-navy-surface in lightness so the masthead still
   reads as a band instead of dissolving into the page; --nd-green lifts
   because at its light-mode lightness it reads as a hole in a dark page;
   --nd-navy-ink can only go light now that it is a separate token from the
   chrome surface. */
@media screen{
  :root[data-theme="dark"][data-theme-ready]{
    color-scheme:dark;

    --bg:oklch(0.175 0.016 258);
    --bg-sunk:oklch(0.145 0.015 258);
    --bg-muted:oklch(0.285 0.016 258);
    --card-raised:oklch(0.225 0.018 258);
    --row-hover:oklch(0.27 0.018 258);
    --row-hover-nested:oklch(0.315 0.02 258);

    --text:oklch(0.93 0.008 85);
    --ink-2:oklch(0.82 0.008 85);
    --text-muted:oklch(0.68 0.01 82);
    --ink-4:oklch(0.56 0.01 82);
    --line:oklch(0.32 0.016 258);
    --line-soft:oklch(0.275 0.015 258);
    --line-strong:oklch(0.41 0.016 258);

    --nd-navy-surface:oklch(0.235 0.032 260);
    --nd-navy-ink:oklch(0.95 0.01 85);
    --accent-hover:oklch(0.85 0.01 85);
    --accent-tint:rgba(255,255,255,.07);

    --nd-green:oklch(0.62 0.12 150);
    --nd-green-hover:oklch(0.67 0.12 150);
    --nd-gold:oklch(0.78 0.13 82);
    --link-visited:oklch(0.74 0.1 82);
    --gold-chip-ink:oklch(0.88 0.07 85);
    --gold-chip-ink-2:oklch(0.8 0.09 85);
    /* Not a fix, just a labelled hook: checked in the browser, this reads as
       a muddy olive rather than a highlight -- see the long comment on this
       token's :root definition. Left set rather than reverted to transparent
       because the two read the same in practice; this way the value to tune
       has a name. */
    --gold-mix-base:var(--card-raised);

    --danger:oklch(0.7 0.19 25);
    --danger-tint:rgba(255,255,255,.07);
    --success-tint:rgba(255,255,255,.07);

    --shadow-sm:0 1px 2px rgba(0,0,0,.3);
    --shadow-card:0 1px 0 oklch(1 0 0 / .04) inset, 0 2px 8px rgba(0,0,0,.35);

    --ad-frame:var(--line);
  }
}
