/* ============================================================================
   The Playground · design tokens (single source of truth)
   Every text/background pair below was measured against WCAG 2.x.
   Ratios in comments are the verified contrast against the relevant surface.
   AA = 4.5:1 normal text · AAA = 7:1 normal text · large text 3:1 / 4.5:1.
   Production: link this file, and self-host the woff2 files named below.
   ========================================================================== */

/* ---- self-hosted fonts (ship the woff2 files; do NOT hotlink Google) -------
   Hotlinking Google Fonts transmits the visitor IP to Google and was ruled a
   GDPR violation in Germany (LG München I, 20 Jan 2022, 3 O 17493/20).
   Self-hosting removes that entirely and pins the exact faces on every device.
   Only the weights that actually exist are declared (no faux weights):
   Spectral 400/500/600 + italic 400 · Space Mono 400/700 · Roboto Mono 400/500 */

@font-face{font-family:'Spectral';font-style:normal;font-weight:400;font-display:swap;
  src:url('/fonts/spectral-400.woff2') format('woff2')}
@font-face{font-family:'Spectral';font-style:italic;font-weight:400;font-display:swap;
  src:url('/fonts/spectral-400-italic.woff2') format('woff2')}
@font-face{font-family:'Spectral';font-style:normal;font-weight:500;font-display:swap;
  src:url('/fonts/spectral-500.woff2') format('woff2')}
@font-face{font-family:'Spectral';font-style:normal;font-weight:600;font-display:swap;
  src:url('/fonts/spectral-600.woff2') format('woff2')}

@font-face{font-family:'Space Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url('/fonts/space-mono-400.woff2') format('woff2')}
@font-face{font-family:'Space Mono';font-style:normal;font-weight:700;font-display:swap;
  src:url('/fonts/space-mono-700.woff2') format('woff2')}

@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url('/fonts/roboto-mono-400.woff2') format('woff2')}
@font-face{font-family:'Roboto Mono';font-style:normal;font-weight:500;font-display:swap;
  src:url('/fonts/roboto-mono-500.woff2') format('woff2')}

:root{
  /* type roles ------------------------------------------------------------- */
  --serif:'Spectral', Georgia, 'Times New Roman', serif;     /* prose, content */
  --mono:'Space Mono', ui-monospace, Menlo, Consolas, monospace; /* structure  */
  --code:'Roboto Mono', ui-monospace, Menlo, Consolas, monospace; /* code only */

  /* measure + rhythm ------------------------------------------------------- */
  --measure:33rem;          /* reading column, set in rem so it scales with text */
  --gutter:max(24px,5vw);   /* never let text touch the edge on phones          */
  --leading:1.6;            /* body line-height >=1.5 satisfies WCAG 1.4.8 AAA  */
  --leading-tight:1.3;      /* display / wordmark                               */

  /* ---- LIGHT (default) --------------------------------------------------- */
  --bg:#ffffff;
  --ink:#0a0a0a;            /* body + headings · 19.80:1 on bg · AAA            */
  --secondary:#595959;      /* metadata, margin rail · 7.00:1 on bg · AAA       */
                            /* (was #8a8a8a = 3.45:1, failed AA, corrected)   */
  --decoration:#b4b4b4;     /* DECORATION ONLY (marks/ticks). Never set on text */
  --hair:#e6e6e6;           /* dividers · decorative, exempt from contrast      */
  --wf:#1f3a8f;             /* wayfinding blue · 10.19:1 on bg · AAA            */

  /* code surface (light): fill deepened so the fenced block actually reads:
     #efece5 = 1.18:1 vs page (was #f6f5f2 = 1.09, near-invisible)             */
  --code-bg:#efece5;
  --code-edge:#d4cfc4;      /* 1.55:1 vs page · visible border, non-text        */
  --code-ink:#0a0a0a;       /* 16.78:1 on code-bg · AAA                         */
  --c-keyword:#5d4e86;      /* 6.14:1 · AA  (violet)                            */
  --c-func:#2f6f6a;         /* 4.94:1 · AA  (teal)                              */
  --c-string:#8a5a3c;       /* 4.93:1 · AA  (clay)                              */
  --c-number:#7f6526;       /* 4.69:1 · AA  (amber)                             */
  --c-comment:#5e5e5e;      /* 5.50:1 · AA  italic (was #8a8a8a = 3.17, failed) */
}

/* ---- DARK -----------------------------------------------------------------
   Applied when the visitor's OS prefers dark, OR when [data-theme] forces it.
   A parallel palette held to the same bar, not a filter.                      */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]){
    --bg:#0f1217;
    --ink:#e8e7e3;          /* 15.16:1 on bg · AAA                             */
    --secondary:#8c9299;    /* 5.97:1 on bg · AA                               */
    --decoration:#3a4048;   /* decoration only                                 */
    --hair:rgba(255,255,255,.10);
    --wf:#86a8f2;           /* 7.95:1 on bg · AAA                              */

    --code-bg:#161b22;      /* locked dark surface                             */
    --code-edge:rgba(255,255,255,.10);
    --code-ink:#e8e7e3;     /* 13.98:1 on code-bg · AAA                        */
    --c-keyword:#ab9ee0;    /* 7.17:1 · AAA                                    */
    --c-func:#5cb6ab;       /* 7.19:1 · AAA                                    */
    --c-string:#cf9472;     /* 6.70:1 · AA                                     */
    --c-number:#d8b46e;     /* amber, remixed for dark                         */
    --c-comment:#7f868d;    /* 4.69:1 · AA  italic                            */
  }
}
:root[data-theme="dark"]{
  --bg:#0f1217; --ink:#e8e7e3; --secondary:#8c9299; --decoration:#3a4048;
  --hair:rgba(255,255,255,.10); --wf:#86a8f2;
  --code-bg:#161b22; --code-edge:rgba(255,255,255,.10); --code-ink:#e8e7e3;
  --c-keyword:#ab9ee0; --c-func:#5cb6ab; --c-string:#cf9472;
  --c-number:#d8b46e; --c-comment:#7f868d;
}

/* ============================================================================
   WAYFINDING POLICY (one job for blue: where am I, where can I go)
   - current page / item: blue AT REST, and bold (colour is never the only cue)
   - link: ink with underline at rest; blue only on hover/focus
   - focus: a VISIBLE ring (not colour alone), >=3:1, never removed
   - emphasis in prose = italic, never blue
   ========================================================================== */
a{
  color:var(--ink);
  text-decoration:underline;
  text-decoration-color:var(--ink);
  text-underline-offset:3px;
  text-decoration-thickness:1px;
  transition:color .14s ease, text-decoration-color .14s ease;
}
a:hover{ color:var(--wf); text-decoration-color:var(--wf); }

/* visited recedes to the secondary grey. Browsers only let :visited change
   colour, so the recede is colour, not a thinner underline. Hover/focus still
   win, because a visited link is still somewhere you can go. */
a:visited{ color:var(--secondary); text-decoration-color:var(--secondary); }
a:visited:hover, a:visited:focus-visible{ color:var(--wf); text-decoration-color:var(--wf); }

a:focus-visible{
  color:var(--wf); text-decoration-color:var(--wf);
  outline:2px solid var(--wf); outline-offset:3px; border-radius:1px;
}
[aria-current]{ color:var(--wf); font-weight:700; text-decoration:none; }

/* Focus ring meets WCAG 2.2 Focus Appearance (2.4.13 AAA): >=2px thick, and the
   wayfinding blue clears 3:1 against the background in both themes (10.2:1 light,
   7.95:1 dark). Never remove it (2.4.7 / 2.4.11). */
button:focus-visible, [tabindex]:focus-visible{
  outline:2px solid var(--wf); outline-offset:2px;
}

em, i{ font-style:italic; }   /* emphasis is italic, not colour */

/* external links carry a small mark, plus a screen-reader label so the cue is
   never glyph-only. Markup:
   <a href data-external>text<span class="ext-mark" aria-hidden="true">\2197</span><span class="sr-only"> (external)</span></a> */
.ext-mark{ font-size:.78em; line-height:1; vertical-align:.08em; margin-left:.12em; }
.sr-only{ position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }

/* emphasis: italic carries it; strong renders italic too, so bold never enters
   prose and weight stays a structural signal only. */
strong, b{ font-style:italic; font-weight:400; }

/* figures reuse the code container exactly (same tint, same edge) so a chart and
   a code block read as one hand. Charts draw only from the four code hues;
   photographs sit in the same frame and keep their own colour. Default holds the
   measure; .wide breaks out a little for dense charts, never full-bleed.
   forced-color-adjust:none keeps chart colour through high-contrast, because
   there the colour is data, not decoration. Alt text is mandatory. */
figure{ margin:1.9em 0; }
figure.wide{ width:min(46rem,92vw); margin-left:50%; transform:translateX(-50%); }
.frame{ background:var(--code-bg); border:1px solid var(--code-edge); border-radius:8px; padding:18px 18px 12px; }
.frame svg, .frame img{ display:block; width:100%; height:auto; }
.frame svg{ forced-color-adjust:none; }
figcaption{ margin-top:10px; font-size:14.5px; line-height:1.5; color:var(--secondary); }
figcaption .lbl{ font-family:var(--mono); font-size:11px; letter-spacing:.08em; text-transform:uppercase; margin-right:.5em; }
figcaption .desc{ font-family:var(--serif); }

/* tables: a mono header over one rule, hairlines between rows, no vertical lines.
   Number columns sit right on tabular figures so digits stack. Wide tables
   scroll rather than break the layout. Caption matches figures (set below). */
.scroll{ overflow-x:auto; }
table{ border-collapse:collapse; width:100%; font-family:var(--serif); }
thead th{ font-family:var(--mono); font-size:11px; letter-spacing:.1em; text-transform:uppercase;
  color:var(--secondary); font-weight:400; text-align:left; padding:0 16px 9px 0;
  border-bottom:1px solid var(--secondary); white-space:nowrap; }
tbody td{ font-size:16px; line-height:1.5; color:var(--ink); padding:10px 16px 10px 0; border-bottom:1px solid var(--hair); }
.num{ text-align:right; padding-right:0; font-variant-numeric:tabular-nums; font-feature-settings:"tnum"; }
tbody tr:last-child td{ border-bottom:0; }

/* section break inside a post: a short hairline that fades out at both ends. */
hr{ border:0; height:1px; width:min(160px,40%); margin:2.8em auto;
  background:linear-gradient(90deg, transparent, var(--decoration), transparent); }

/* math: write in LaTeX, render with KaTeX at BUILD time so the page ships static
   markup (KaTeX HTML + MathML) with no runtime script or third-party call. Math
   inherits the ink colour (currentColor), no syntax tint, and the MathML output
   keeps it readable to screen readers. KaTeX brings its own CSS; these are the
   only project-side hooks. */
.katex{ font-size:1.04em; }              /* match inline math to the serif weight */
.katex-display{ margin:1.4em 0; overflow-x:auto; overflow-y:hidden; }

/* reduced motion (WCAG 2.3.3): honour the OS request, drop non-essential motion.
   Everything we animate is decorative (colour fades, the theme swap). */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    transition-duration:.001ms !important;
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    scroll-behavior:auto !important;
  }
}

/* forced-colors (Windows high contrast): don't fight the user's palette. Let
   system colours through, keep container borders and link underlines so
   structure survives, and exempt only data graphics (charts keep their hues via
   forced-color-adjust:none on the figure svg). */
@media (forced-colors: active){
  pre, blockquote, table, figure .frame, .toggle, .refs, footer{ border-color: currentColor; }
  a, a:visited{ text-decoration-line: underline; }
}

/* print: a text site is saved as PDF, so force the light palette regardless of
   theme, drop interactive chrome, keep blocks from breaking across pages, and
   reveal external reference URLs so sources survive the print. */
@media print{
  :root, :root[data-theme="dark"]{
    --bg:#fff; --ink:#000; --secondary:#333; --decoration:#999; --hair:#bbb; --wf:#000;
    --code-bg:#f4f4f4; --code-edge:#999; --code-ink:#000;
  }
  body{ background:#fff; color:#000; }
  a{ color:#000; }
  h1, h2, h3{ break-after: avoid; }
  pre, blockquote, figure, tr{ break-inside: avoid; }
  .refs a[data-external]::after{ content:" <" attr(href) ">"; font-family:var(--code); font-size:.85em; }
}
