/* ── site-chrome.css — the ONE shared stylesheet for the static (non-React) pages. ──
 *
 *  Linked by every page in public/ (about, blog guides, ebook, legal, etc.) so the
 *  header + footer + brand variables are defined ONCE, not copy-pasted per page.
 *  Mirrors the React app's Header.jsx + Footer.jsx so navigating between the app (/)
 *  and any static page feels continuous.
 *
 *  Key design: header and footer are FULL-WIDTH bars (edge-to-edge), with their
 *  CONTENT centered and constrained to 720px — matching the React app's full-width
 *  chrome. Pages keep their own <style> for body/content rules.
 */

:root {
  /* Mirrors web/tailwind.config.js (Direction B "Exam Ink + Marks Green", 2026-07-04).
     brand = brand-600 / brand-dark = brand-700 / accent = accent-600 (marks green).
     Change the Tailwind config and this block TOGETHER — docs/DESIGN_SYSTEM.md. */
  --brand: #1d4e79;
  --brand-dark: #163d61;
  --brand-50: #eef4f9;     /* brand wash (tints/soft surfaces) */
  --accent: #0d9668;
  --accent-50: #e9f7f1;
  --accent-100: #d7efe3;
  --accent-700: #087a56;
  --ember: #e07c00;
  --ember-50: #fdf3e3;
  --ember-100: #fbe5c0;
  --ember-700: #9d5602;
  --danger: #dc2626;       /* errors / required markers */
  --ink: #0f172a;
  --ink-soft: #1e293b;     /* body text (one step lighter than headings) */
  --muted: #475569;
  --muted-faint: #64748b;  /* secondary text + captions */
  --muted-ghost: #94a3b8;  /* dividers / placeholder / footer micro */
  --line: #e2e8f0;
  --line-soft: #f1f5f9;    /* tinted backgrounds (feature chips, code bg) */
  --bg: #f8fafc;
  /* Content width — matches the body max-width on all pages. Header/footer inner
     content is constrained to this so the bar is full-width but content stays aligned. */
  --content-width: 720px;
  /* Bar/surface color — a variable so the design variant below can warm it. */
  --surface: #ffffff;
  /* Card + soft-tint defaults used by a few pages (ebook-style cards / pill chips).
     Promoted here from per-page :root blocks so the [data-design] remaps below can
     warm them too — the per-page redeclarations were overriding the variant remap. */
  --card: #ffffff;
  --soft: #eef4f9;
  /* Two tokens that serve ONE job on a light page and split in two on a dark one.
     --brand is the solid brand SURFACE (background: var(--brand), white type on it) AND
     the brand INK the pages set with color: var(--brand). --surface is the raised page
     surface AND, on a few CTAs, "the colour that reads on a brand surface". On A/C/D one
     value serves both, so these two default straight back and every arm but B is
     byte-identical. B is the one dark arm, where the ink has to invert while the surface
     must not — see the [data-design="b"] block. Pages say which job they mean by which
     token they name; docs/DESIGN_SYSTEM.md carries the rule. */
  --brand-ink: var(--brand);
  --on-brand: #ffffff;
}

/* ── Variant B — "Antigravity" (founder A/B experiment; ?design=b on the app). ──
 *  Mirrors web/src/index.css [data-design="b"]: the DARK arm — navy ground #0B0F19,
 *  #0E1424 cards, indigo brand, violet accent, emerald/amber/rose status. The static
 *  pages and the app are one journey, so this block exists to stop an arm-B visitor
 *  crossing a full light→dark brand change at the CTA — the same class of mismatch
 *  the D arm hit on the landing page (see scripts/design-boot-snippet.cjs).
 *
 *  This is the ONLY dark arm, so `color-scheme` has to be stated here as it is in the
 *  app block: without it the UA paints light native selects, scrollbars and caret on
 *  the navy ground.
 *
 *  Token NAMES here differ from the app's (--brand vs --c-brand-600) — this is the
 *  static chrome's own smaller set, so the mapping is by ROLE, with the app's hex in
 *  the comment. The attribute is stamped by the bootstrap in
 *  scripts/design-boot-snippet.cjs, which reads the same localStorage key
 *  ("deduce_design") the app persists. */
[data-design="b"] {
  color-scheme: dark;

  --brand: #4F46E5;        /* the app's solid CTA surface (bg-brand-600) */
  --brand-dark: #4338CA;
  --brand-50: #151A36;     /* --c-brand-50 — the dark wash panel, not a light tint */
  --accent: #A855F7;       /* --c-accent-500, the mock's violet */
  --accent-50: #1E1533;
  --accent-100: #291A45;
  --accent-700: #D8B4FE;   /* ink ON the wash — inverted, like the app's ramp */
  --ember: #F59E0B;
  --ember-50: #2A1C06;
  --ember-100: #3A2708;
  --ember-700: #FDE08A;
  --danger: #F43F5E;       /* rose — the mock's violation red */
  --ink: #F1F5F9;          /* --c-slate-900, the mock's body colour */
  --ink-soft: #CBD5E1;
  --muted: #9FADC4;        /* --c-slate-500 — lifted off stock slate for 7.2:1 on the card */
  --muted-faint: #8494AD;  /* --c-slate-400 — 5.3:1; stock #64748B would fail here */
  --muted-ghost: #5D6E88;  /* dividers / placeholder / footer micro */
  --line: #1E293B;         /* --c-slate-200, the hairline */
  --line-soft: #131B2E;
  --bg: #0B0F19;           /* --c-slate-50, the page ground */
  --surface: #0E1424;      /* the mock's card */
  --card: #0E1424;
  --soft: #131B2E;         /* --c-slate-100, the raised well/chip */
  /* The two split tokens. --brand-ink is the app's --c-brand-600 (#A2AAFB, "link + icon
     ink" there) — brand #4F46E5 as text on this ground measures 2.9:1 and fails AA.
     --on-brand is NOT overridden: a brand surface is still indigo here, so white still
     reads on it; it exists so a page can say "on the brand" instead of "--surface",
     which on this arm is near-black. */
  --brand-ink: #A2AAFB;
}
/* Type: the mock pairs Plus Jakarta Sans with JetBrains Mono. The static pages do NOT
 * fetch either face (only the app's index.html bootstrap injects the Google Fonts link
 * under B), so this asks for it and falls straight through to the system sans the
 * static chrome already ships on — the wordmark keeps its weight and tracking either
 * way. It replaces the retired Question-Paper serif, which belonged to a palette that
 * no longer exists. */
[data-design="b"] header.site .wordmark {
  font-family: "Deduce Deva", "Plus Jakarta Sans", "Segoe UI", system-ui, -apple-system, Roboto,
    "Helvetica Neue", sans-serif;
  letter-spacing: -0.02em;
  text-transform: none;
}

/* ── Variant C — "Bold Aspirant" (founder experiment; ?design=c on the app). ──
 *  Mirrors web/src/index.css [data-design="c"]: deep indigo brand, coral accent
 *  (the CTA), green success, amber ember, indigo-biased neutrals, chunky sans
 *  wordmark. The attribute is stamped by the bootstrap in _includes/site-header.html,
 *  which reads the same localStorage key ("deduce_design") the app persists. */
/* The CTA coral is accent-600 (#D93B41), NOT the #FF5A5F identity coral. src/index.css
   retuned this ramp on 2026-07-31 after a Lighthouse audit: #FF5A5F is 3.05:1 on white and
   is documented there as "identity/icons ONLY, not text-safe", and the old 700 (#E8434A) was
   3.94:1. The app moved; these files did not, so until 2026-08-24 every static CTA — and the
   apex's hero button — shipped white label text on a surface that fails AA, in a coral the
   app no longer uses. Two problems, one stale value. */
[data-design="c"] {
  --brand: #3D2E8F;
  --brand-dark: #2A1E6B;
  --brand-50: #EEEBFA;
  --accent: #D93B41;
  --accent-50: #FFECEC;
  --accent-100: #FFE0E0;
  --accent-700: #C73037;
  --ember: #FFB020;
  --ember-50: #FFF3DC;
  --ember-100: #FFECBF;
  --ember-700: #B87408;
  --danger: #dc2626;
  --ink: #191634;
  --ink-soft: #221E47;
  --muted: #8A87A6;
  --muted-faint: #8A87A6;
  --muted-ghost: #A09DB3;
  --line: #E8E7F1;
  --line-soft: #F0F0F6;
  --bg: #F5F5FA;
  --surface: #FFFFFF;
}
[data-design="c"] header.site .wordmark {
  letter-spacing: -0.02em;
  /* DeducePuzzles stays title-case; C colours .wm-accent amber (Header.jsx / landing). */
  text-transform: none;
}
[data-design="c"] header.site .wm-accent { color: #FFB020; }

/* Hindi (founder 2026-10-03). Devanagari ONLY — unicode-range keeps every Latin glyph on the
 * arm's own face and means the file is fetched only when Devanagari is actually rendered, so
 * English users never download it (the low-end-device no-@font-face rule's scoped exception).
 * U+20B9 (₹) is deliberately excluded so the rupee sign keeps the arm's face. Noto Sans
 * Devanagari, variable 400-800, OFL (public/fonts/Noto-OFL.txt). */
@font-face {
  font-family: "Deduce Deva";
  src: url("/fonts/noto-sans-devanagari-var.woff2") format("woff2");
  font-weight: 400 800;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF;
}

/* Variant C display face (mirrors web/src/index.css — the "change both together"
 * rule, docs/DESIGN_SYSTEM.md). ONE self-hosted Poppins-ExtraBold latin woff2
 * (~8 KB, OFL), font-display: swap, fetched ONLY under C (the @font-face resolves
 * only when a C-scoped selector matches an element). The wordmark + page headings
 * pick it up; body text stays the system sans. A/B get no @font-face. */
@font-face {
  font-family: "Deduce Display";
  src: url("/fonts/poppins-extrabold-latin.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}
[data-design="c"] header.site .wordmark,
[data-design="c"] main h1,
[data-design="c"] main h2 {
  font-family: "Deduce Deva", "Deduce Display", "Segoe UI", system-ui, -apple-system, Roboto,
    "Helvetica Neue", sans-serif;
  /* The subset woff2's space glyph advances only ~0.2em (system ≈ 0.35em), so headline
     words visually fuse ("Pickyourrung" — founder-reported). Compensate the word gap
     only. Mirrors web/src/index.css .font-extrabold (change both together). */
  word-spacing: 0.12em;
}

/* Variant C content headings on the static SEO pages: the app's Bold Aspirant
 * identity uses chunky weight-800 display type, so the static pages' <main> headings
 * step up to match (they're 700 by each page's inline style) — keeps FAQ/About/etc.
 * feeling like the same product as the app, not a separate site. Scoped to
 * [data-design="c"] so A/B pages are unchanged. Color/ground/ink are already synced
 * via the token block above; this closes the last gap (heading weight + tracking). */
[data-design="c"] main h1,
[data-design="c"] main h2 {
  font-weight: 800;
  letter-spacing: -0.015em;
}

/* Variant C page identity: indigo header band + coral CTA + energy ground, mirroring the
 * app's Bold Aspirant treatment (prototype/purple). Scoped to [data-design="c"]; A/B
 * pages keep their white-header / brand-navy / marks-green identity unchanged. */
/* Indigo header band — the prototype's 135deg indigo→indigo-lift panel with white content,
   rounded to the app-feel radii. A/B keep the white surface + border header above. */
[data-design="c"] header.site {
  border-bottom: none;
  background-image: linear-gradient(135deg, #3D2E8F, #5B49C9);
  box-shadow: 0 8px 24px rgba(61, 46, 143, 0.18);
  /* Band GEOMETRY matches the app homepage's topbar (founder, 2026-07-18: the static
     band was full-bleed while the app's floats rounded at the content width): a
     rounded band capped at the app column width (max-w-5xl = 1024px), centered,
     floating below a small top gap. The 50%-based margins recenter it from inside
     the page's narrower (720px) content column. */
  --band-w: min(1024px, 100vw - 24px);
  width: var(--band-w);
  margin-left: calc(50% - var(--band-w) / 2);
  margin-right: calc(50% - var(--band-w) / 2);
  padding-left: 20px;
  padding-right: 20px;
  margin-top: 10px;
  border-radius: 18px;
}
/* The band's content spans the band (the app topbar's row fills its column) — not
   the page's 720px article measure. */
[data-design="c"] header.site > .site-inner {
  max-width: none;
}
[data-design="c"] header.site .wordmark,
[data-design="c"] header.site .tagline,
[data-design="c"] header.site nav a {
  color: #fff;
}
[data-design="c"] header.site .tagline { color: rgba(255, 255, 255, 0.72); }
[data-design="c"] header.site nav a:hover { color: rgba(255, 255, 255, 0.8); }
[data-design="c"] header.site .acct {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}
[data-design="c"] header.site .acct.in {
  background: rgba(255, 255, 255, 0.22);
}

/* Coral CTA — the strict grammar reserves coral for THE one action per screen. The pages'
   .cta / .btn-primary source from var(--brand) (indigo under C), so under C only recolor
   those surfaces coral with the prototype's coral glow shadow. A/B keep brand-navy CTAs. */
[data-design="c"] .cta,
[data-design="c"] .btn-primary {
  background: #D93B41 !important;
  color: #fff !important;
  border-color: transparent !important;
  box-shadow: 0 6px 16px rgba(217, 59, 65, 0.32) !important;
}
[data-design="c"] .cta:hover,
[data-design="c"] .btn-primary:hover {
  background: #C73037 !important;
}
/* Secondary buttons stay a translucent-white glass on the indigo/ground identity (not
   a second coral surface — the grammar is one coral action per screen). */
[data-design="c"] .btn-secondary,
[data-design="c"] .btn-ghost {
  background: rgba(255, 255, 255, 0.7);
  color: var(--ink);
  border: 1px solid var(--line);
}

/* Energy ground — the ground background with subtle indigo/coral radial washes (the
   prototype's desktop adaptation). Applied to body via the same var the pages already
   read; here we add the wash layer under C only. */
[data-design="c"] body {
  background-color: var(--bg);
  background-image:
    radial-gradient(circle at 12% 0%, rgba(91, 73, 201, 0.08), transparent 42%),
    radial-gradient(circle at 92% 8%, rgba(255, 90, 95, 0.07), transparent 38%);
  background-attachment: fixed;
}

/* ── Variant D — "The Ledger" (the live C-vs-D arm; ?design=d on the app). ──────────
 *  Mirrors web/src/index.css [data-design="d"]: ledger gold/bronze brand, analytical
 *  violet accent, vermillion ember, cool "steel ledger paper" neutrals, monospace
 *  display face. The attribute is stamped by the design bootstrap that
 *  scripts/inject-chrome.cjs injects, which reads the same localStorage key
 *  ("deduce_design") the app persists.
 *
 *  WHY THIS BLOCK EXISTS (2026-08-24). It did not, for the first three weeks D was live.
 *  site-chrome.css knew only a/b/c, and the header bootstrap did not recognise a stored
 *  "d" either — so a visitor in the D arm got C-coloured /start, /pro and /faq chrome and
 *  then a gold-and-violet app. That is not only a broken seam: `VITE_DESIGN_EXPERIMENT=1`
 *  has been running the 50/50 split in prod since 2026-08-03, so the D arm's funnel was
 *  being measured on half-C surfaces and no result could be attributed to D.
 *
 *  D does NOT get C's structural treatment (the indigo header band, the coral CTA, the
 *  washed ground). That is deliberate and it mirrors the app exactly: in index.css those
 *  rules are [data-design="c"]-scoped too, so D renders A's chrome structure with D's
 *  tokens. The one structural change D carries — the three-card start gate — is SPA-only
 *  by design (components/StartGateCards.jsx, gated on isVariantD()).
 *
 *  Token ⇄ ramp mapping follows the annotations in index.css's C block: ink = slate-900,
 *  ink-soft = slate-800, line = slate-200, line-soft = slate-100, bg = slate-50,
 *  surface = white, brand = brand-600, brand-dark = brand-700, accent = accent-600,
 *  ember = ember-500. The muted trio is the ONE deliberate departure — see below. */
[data-design="d"] {
  --brand: #8A5A0C;
  --brand-dark: #6C4409;
  --brand-50: #FDF8EA;
  --accent: #6037BD;
  --accent-50: #F3F0FD;
  --accent-100: #E9E3FB;
  --accent-700: #4A2994;
  --ember: #E04923;
  --ember-50: #FEEDE7;
  --ember-100: #FDDCCF;
  --ember-700: #9E2E12;
  --danger: #dc2626;
  --ink: #141B26;
  --ink-soft: #26303D;
  /* A maps muted/faint/ghost to slate-600/500/400. D's slate-500 (#6C7C8E) is 4.28:1 on
     white — under the 4.5:1 AA floor, and --muted-faint is the "secondary text + captions"
     var, i.e. exactly the small-text sizes index.css's clamp rule says start at slate-600.
     So both TEXT roles take slate-600 (#4F5E70, 6.6:1) rather than a hex invented to sit
     between ramp steps — the same collapse C's block makes. Only --muted-ghost, which is
     dividers and placeholders and never readable text, stays at slate-400. */
  --muted: #4F5E70;
  --muted-faint: #4F5E70;
  --muted-ghost: #96A5B5;
  --line: #DEE5EC;
  --line-soft: #EDF1F5;
  --bg: #F6F8FA;
  --surface: #FAFBFC;
}

/* Variant D display face (mirrors web/src/index.css [data-design="d"] .font-extrabold —
 * the "change both together" rule, docs/DESIGN_SYSTEM.md). System monospace: genuinely
 * distinct from A's system sans, B's Georgia serif and C's Poppins, and zero extra font
 * payload. Body/UI text stays the system sans, same rule as B/C. */
[data-design="d"] header.site .wordmark,
[data-design="d"] main h1,
[data-design="d"] main h2 {
  font-family: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas,
    "Liberation Mono", "Courier New", monospace;
  font-weight: 700;
  letter-spacing: -0.01em;
}
/* C's wordmark rule sets text-transform: none to mirror the app's title-case lockup; the
   same applies under D (one brand lockup on every page, founder 2026-07-18). */
[data-design="d"] header.site .wordmark { text-transform: none; }

/* ── Header — full-width bar, mirrors the React app's Header.jsx ──
 *  The header.site element spans edge-to-edge; its inner content (logo + nav) is
 *  centered and capped at --content-width. This matches the app header, which is
 *  full-width with centered content — NOT trapped inside the body's max-width column. */
header.site {
  width: 100%;
  box-sizing: border-box;
  border-bottom: 1px solid var(--line);
  background: var(--surface);
  /* Break out of any parent max-width/padding: stretch to the viewport edges. */
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding-left: calc(50vw - var(--content-width) / 2);
  padding-right: calc(50vw - var(--content-width) / 2);
  /* Pinned while scrolling — mirrors the app header (browser tabs got sticky on
     2026-07-18; the static pages follow so the chrome behaves identically). */
  position: sticky;
  top: 0;
  z-index: 40;
}
/* Phone widths: the app's topbar runs edge-to-edge from the very top (the app-shell
   bleeds it over the page padding; 820px = the shell breakpoint in standalone.js).
   Match it so app ⇄ static feels like one surface on a phone. */
@media (max-width: 820px) {
  [data-design="c"] header.site {
    --band-w: 100vw;
    margin-top: 0;
    top: 0;
  }
}
/* The inner row: logo left, nav right, capped to content width. */
header.site > .site-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 20px;
  flex-wrap: wrap;
}
header.site .brand {
  display: flex;
  align-items: center;
  gap: 8px;
  text-decoration: none;
}
header.site .brand img {
  width: 30px;
  height: 30px;
  border-radius: 8px;
  display: block;
  flex-shrink: 0;
}
header.site .wordmark {
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ink);
  font-size: 18px;
  line-height: 1;
  /* DeducePuzzles — do not uppercase the whole string (DEDUCEPUZZLES). */
  text-transform: none;
}
header.site .tagline {
  display: block;
  font-size: 10px;
  color: var(--muted);
  font-weight: 500;
  letter-spacing: 0.025em;
  margin-top: 2px;
}
header.site nav a {
  margin-left: 16px;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  color: var(--muted);
}
header.site nav a:hover { color: var(--brand); }
/* The two paid links in the nav. Weight, not colour: this header renders in BOTH the light
   Variant A palette and the brand-filled Variant C (white nav text), and a colour rule here
   would sit at EQUAL specificity to `[data-design="c"] header.site nav a` while coming later
   in the file — silently repainting the C nav. Weight reads as emphasis in both. */
header.site nav a.offer { font-weight: 800; }
/* Nav + account chip travel together on the right. */
header.site .site-right {
  display: flex;
  align-items: center;
  gap: 4px;
}
/* Account entry (mirrors the app header's): grey icon when signed out, brand-gradient
 * avatar initial (.in, set by the header's session probe) when signed in. */
header.site .acct {
  margin-left: 16px;
  width: 36px;
  height: 36px;
  border-radius: 9999px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--line-soft);
  color: var(--muted-faint);
  text-decoration: none;
  font-weight: 700;
  font-size: 14px;
  flex-shrink: 0;
}
header.site .acct:hover { background: var(--line); }
header.site .acct.in {
  background: linear-gradient(to bottom right, var(--brand), var(--brand-dark));
  color: #fff;
}
header.site .acct.in:hover { opacity: 0.9; }
header.site .acct svg { display: block; }
/* Signed-in chip with a verified Google photo (mirrors the app's Avatar primitive). */
header.site .acct img {
  width: 100%;
  height: 100%;
  border-radius: 9999px;
  object-fit: cover;
  display: block;
}

/* ── Mobile nav hamburger (≤820px = the app-shell breakpoint) ──
 *  The inline links collapse behind a chip-styled toggle, mirroring the app header's
 *  More menu, and open as a white dropdown card under the band. Desktop unchanged. */
header.site .nav-toggle {
  display: none;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 9999px;
  align-items: center;
  justify-content: center;
  background: var(--line-soft);
  color: var(--muted-faint);
  cursor: pointer;
  flex-shrink: 0;
}
[data-design="c"] header.site .nav-toggle {
  background: rgba(255, 255, 255, 0.16);
  color: #fff;
}
@media (max-width: 820px) {
  header.site .nav-toggle { display: flex; }
  header.site nav { display: none; }
  /* One row like the app header: logo left, chips right (the tagline wraps inside
     the brand instead of pushing the chips to a second row). */
  header.site > .site-inner { flex-wrap: nowrap; }
  header.site .brand { min-width: 0; }
  header.site.nav-open nav {
    display: flex;
    flex-direction: column;
    gap: 2px;
    position: absolute;
    right: 12px;
    top: calc(100% + 8px);
    min-width: 180px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 14px;
    box-shadow: 0 18px 40px rgba(15, 23, 42, 0.22);
    padding: 8px;
    z-index: 50;
  }
  /* Dropdown links sit on the white card — ink, not the band's white-on-indigo. */
  header.site.nav-open nav a,
  [data-design="c"] header.site.nav-open nav a {
    margin-left: 0;
    padding: 10px 12px;
    border-radius: 10px;
    color: var(--ink);
  }
  header.site.nav-open nav a:hover,
  [data-design="c"] header.site.nav-open nav a:hover {
    background: var(--line-soft);
    color: var(--brand);
  }
}

/* ── Footer — full-width bar, mirrors the React app's Footer.jsx grouped layout ──
 *  Same break-out trick as the header: full-width background, content centered + capped.
 *  Uses the 3-group layout (Learn / Community / Legal & support) from Footer.jsx. */
footer.site {
  width: 100%;
  box-sizing: border-box;
  border-top: 1px solid var(--line);
  background: var(--surface);
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding: 20px calc(50vw - var(--content-width) / 2);
}
footer.site > .site-inner {
  max-width: var(--content-width);
  margin: 0 auto;
  text-align: center;
}
footer.site .foot-group {
  margin-bottom: 8px;
}
footer.site .foot-group:last-of-type { margin-bottom: 0; }
footer.site .foot-label {
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--muted-ghost);
  margin-bottom: 4px;
}
footer.site .foot-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 8px;
}
footer.site .foot-links a {
  font-size: 12px;
  color: var(--muted);
  text-decoration: none;
  background: var(--bg);
  border-radius: 999px;
  padding: 2px 8px;
  transition: background 0.15s, color 0.15s;
}
footer.site .foot-links a:hover {
  background: var(--line-soft);
  color: var(--brand);
}
/* Offer row (2026-08-16) — the eBook and Pro used to be two of ~25 identical grey pills, so
   the only two things we sell looked exactly like "Floor mistakes". Buttons instead: filled
   for the book (the cheaper, more forwardable entry point), `.ghost` outlined for the
   subscription so the row reads as one primary + one secondary, not two competing CTAs.
   Kept in this shared stylesheet — landing.html carries its own copy of these rules because
   it deliberately loads no external CSS; if you change one, change the other. */
footer.site .foot-cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 2px;
}
footer.site .foot-cta a {
  font-size: 12.5px;
  font-weight: 700;
  text-decoration: none;
  border-radius: 999px;
  padding: 6px 14px;
  border: 1.5px solid var(--brand);
  background: var(--brand);
  color: #fff;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
footer.site .foot-cta a:hover {
  background: var(--brand-dark, var(--brand));
  border-color: var(--brand-dark, var(--brand));
  color: #fff;
}
footer.site .foot-cta a.ghost { background: transparent; color: var(--brand); }
footer.site .foot-cta a.ghost:hover { background: var(--brand-50); color: var(--brand); border-color: var(--brand); }
footer.site .foot-copy {
  font-size: 10px;
  color: var(--muted-ghost);
  margin-top: 8px;
}
footer.site .foot-company {
  font-size: 10px;
  color: var(--muted-ghost);
  margin-top: 2px;
}

/* ── Mobile ── */
@media (max-width: 520px) {
  header.site > .site-inner { padding: 10px 16px; }
  footer.site .foot-links a { font-size: 11px; padding: 2px 6px; }
}

/* ── Testimonials ─────────────────────────────────────────────────────────────
 *  The block injected by `<!-- @include testimonials -->` (scripts/testimonials.cjs).
 *  These rules ship whether or not the marker resolves to anything: the block is empty
 *  until real, moderated, consented quotes exist, and CSS for a section that isn't there
 *  costs nothing — whereas a deploy that shipped the quotes and forgot the styling would
 *  put unstyled text under the footer of every hub page.
 *
 *  Restrained on purpose. Stars, avatars and five-column marquees are the visual grammar
 *  of bought social proof; the whole value of these lines is that they are the opposite
 *  of that, so they are set as quotations and left to be read. */
.testimonials {
  margin: 2.5rem 0 0;
  padding-top: 1.75rem;
  border-top: 1px solid var(--line);
}
.testimonials h2 {
  font-size: 1.05rem;
  margin: 0 0 1rem;
  color: var(--ink);
}
.testimonial {
  margin: 0 0 1.1rem;
  padding-left: 0.9rem;
  border-left: 3px solid var(--line);
}
.testimonial blockquote {
  margin: 0;
  font-size: 1rem;
  line-height: 1.55;
  color: var(--ink-soft);
}
.testimonial blockquote::before { content: "\201C"; }
.testimonial blockquote::after  { content: "\201D"; }
.testimonial figcaption {
  margin-top: 0.35rem;
  font-size: 0.85rem;
  color: var(--muted-faint, var(--muted));
}
.testimonials-note {
  margin: 1.1rem 0 0;
  font-size: 0.8rem;
  color: var(--muted-faint, var(--muted));
}

/* ═══════════════════════════════════════════════════════════════════════════
   ARM B — the stranded-literal corrections
   ───────────────────────────────────────────────────────────────────────────
   Every rule below fixes ONE class of defect, and the class is worth naming
   because it will keep recurring.

   These pages were written for a LIGHT site, so a rule routinely pairs a TOKEN
   with a raw LITERAL: a `var(--soft)` well holding #1e3a8a type, or a #eef4f9
   tint holding `var(--brand-ink)` type. On A, C and D that pairing holds —
   the token resolves light and the literal was chosen against a light ground.
   B is the one DARK arm: the token flips, the literal cannot, and the pair
   lands ink-on-ink or tint-on-tint. Measured with e2e/shot-landing-b.mjs
   across all 43 chrome-linked pages; every ratio quoted below is from that run.

   WHY THESE LIVE HERE AND NOT IN THE PAGES. Arm C REMAPS the wash and neutral
   tokens (--brand-50 #EEEBFA, --accent-50 #FFECEC, --ember-50 #FFF3DC,
   --bg #F5F5FA, --muted #8A87A6). Rewriting a page's literal into "the token
   that matches it on the base palette" would therefore MOVE ARM C — the
   control the live A/B is measured against. Scoping to [data-design="b"] is
   what makes these provably inert on every other arm.

   Split by reach: a component on two or more pages is corrected here; a quirk
   that exists on exactly one page is corrected in that page's own <style>,
   next to the rule it overrides.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Light tints holding brand/accent/ember ink. The tint literal is the bug —
   recolouring the INK instead would just move the failure, because on the
   other arms these pairs are correct as written. .pill measured 1.96:1,
   .tag.coach 1.93:1, .tag.mcq 1.96:1. One page already writes .pill against
   var(--brand-50); the six with the literal are stale copies of it. */
[data-design="b"] .pill { background: var(--brand-50); }
[data-design="b"] .tag.mcq { background: var(--brand-50); }
[data-design="b"] .tag.free,
[data-design="b"] .tag.board { background: var(--accent-50); color: var(--accent-700); }
[data-design="b"] .tag.coach { background: var(--ember-50); color: var(--ember-700); }
/* --accent-700 / --ember-700 are already defined on this arm as "ink ON the
   wash" (the app's inverted ramp), so the pairs come back at 9.8:1 and 12.8:1
   rather than the 3.55:1 and 1.93:1 the literals produced. */

/* Two product marks written as literal brand colours (#3D2E8F is arm C's
   indigo, #C2410C ComputePrep's orange) sitting on var(--card) — which is
   #0E1424 here, so they measured 1.74:1 and 3.54:1. The DeducePuzzles mark has a
   token on this arm; ComputePrep's does not, so it takes orange-400, the
   lightest step that still reads as that brand (8.11:1 on the card). */
[data-design="b"] .tag.d { color: var(--brand-ink); }
[data-design="b"] .tag.cp,
[data-design="b"] .from { color: #FB923C; }

/* .answer is the worked-solution panel on eight pages — the four exam hubs
   emitted by tools/exam-hubs/generate.ts plus four hand-written guides. Its
   ground is var(--soft) (#131B2E here) while its ink stayed literal navy:
   #1e3a8a measured 1.66:1 and the #172554 <strong> 1.17:1 — invisible. */
[data-design="b"] .answer { border-color: var(--line); color: var(--ink-soft); }
[data-design="b"] .answer strong { color: var(--ink); }

/* ── The five long-tail article pages ──────────────────────────────────────
   data-sufficiency-reasoning-tips, floor-puzzle-mistakes,
   linear-vs-circular-seating, puzzle-questions-bank-exams and
   solve-seating-arrangement-faster each carry the same compact inline
   stylesheet, written entirely in raw slate literals: #f8fafc ground,
   #1e293b body, #0f172a h1, #334155 h2, #475569 copy, #eef2ff table head.
   Nothing in it is a token, so under B the PAGE stays light while the chrome
   around it and every var()-driven rule inside it goes dark — a B visitor
   crossing from /pro to one of these lands on a half-inverted page.

   They are marked in the HTML with an inert data-doc="article" on <body>.
   The marker exists ONLY so this block can find them: no other rule in this
   file or in any page matches it, so adding it changed nothing on any arm.
   A blanket [data-design="b"] body would have reached all 43 pages and
   overridden the 38 that already resolve correctly. */
[data-design="b"] body[data-doc="article"] { background: var(--bg); color: var(--ink-soft); }
[data-design="b"] [data-doc="article"] h1 { color: var(--ink); }
[data-design="b"] [data-doc="article"] h2,
[data-design="b"] [data-doc="article"] h3 { color: var(--ink-soft); }
[data-design="b"] [data-doc="article"] p,
[data-design="b"] [data-doc="article"] li { color: var(--muted); }
[data-design="b"] [data-doc="article"] td,
[data-design="b"] [data-doc="article"] th { border-color: var(--line); }
[data-design="b"] [data-doc="article"] th { background: var(--soft); color: var(--brand-ink); }
/* the zebra stripe is a light literal too, and would read as a white band */
[data-design="b"] [data-doc="article"] tr:nth-child(even) { background: var(--soft); }

/* The two pages with NO body background of their own — 404.html and ebook.html
   (every other chrome-linked page sets `body { background: var(--bg) }`, and the
   five article pages are covered by the [data-doc="article"] rule above). On
   A/C/D the UA's white canvas stands in for --bg and nobody notices the gap.
   `color-scheme: dark` on THIS arm makes that canvas the UA's own dark grey and
   unstyled links Chrome's #9e9eff — so those two pages render in the BROWSER's
   dark theme rather than the arm's. One rule at (0,2,0) closes it; it is inert
   on the other 41 pages, which already resolve body to var(--bg) themselves.
   Deliberately background-only: ebook.html sets `color: var(--ink-soft)` and
   must keep it. */
[data-design="b"] body { background: var(--bg); }

/* ── Question-bank + SEO topic pages — the 2026-09-12 founder screenshots ──
   tools/question-bank-gen still emits light-tuned hex so A/C/D stay byte-identical
   (rewriting .lede #334155 → var(--ink-soft) would move arm C). B is the one arm
   those hexes invert on. Measured on the live /questions/syllogism page:
     .lede #334155 on --bg #0B0F19          = 1.85:1  (dark-on-dark lede)
     .dl-line #1e3a8a on --soft #131B2E     = 1.66:1  (dark-on-dark under the PDF CTA)
     .q-card #fff holding inherited --ink   = 1.10:1  (light-on-light question text)
   After .q-card becomes a dark surface, details summary's var(--brand) (#4F46E5)
   drops to 3.05:1 on it — the reason --brand-ink exists. .toc a is the same
   pairing .pill already remaps (brand-ink on #eef4f9 = 1.96:1) under a different
   selector, which is why the earlier remap missed it. */
[data-design="b"] .lede { color: var(--ink-soft); }
[data-design="b"] .dl { border-color: var(--line); }
[data-design="b"] .dl .dl-line { color: var(--ink-soft); }
[data-design="b"] .q-card { background: var(--card); color: var(--ink); }
[data-design="b"] details summary { color: var(--brand-ink); }
[data-design="b"] .toc a,
[data-design="b"] .topics span { background: var(--brand-50); }
[data-design="b"] main a { color: var(--brand-ink); }
[data-design="b"] .dl-btn,
[data-design="b"] .cta,
[data-design="b"] .btn-primary { color: #fff; }
