/* ==========================================================================
   THE CRAFT OF TYPOGRAPHY — Crafted styling (no @font-face)
   Everything in this file describes *how* text is rendered, not *which*
   font renders it. Font selection lives in the per-font sheets
   (literata.css, jetbrains-mono.css, fraunces.css, plex-mono.css), each of
   which sets --font-body or --font-mono. body { font-family: var(...) } and
   code { font-family: var(...) } are in the inline #basics block so the
   chosen font keeps rendering even when this stylesheet is disabled.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Custom Properties & Baseline Grid
   -------------------------------------------------------------------------- */
:root {
    /* Fluid base size: 16px at 320px viewport → 18px at 820px viewport.
     Above 820px caps at 1.125rem; below 320px floors at 1rem. Body grows
     smoothly with the viewport, --baseline auto-follows (via the calc below),
     and the section-wrapper heading-slot math still resolves to N baselines
     because --h2-fontsize cancels out algebraically in the slot calc. */
    --base-size: clamp(1rem, 0.92rem + 0.4vw, 1.125rem);
    --leading-body: 1.6; /* unitless */
    --baseline: calc(var(--base-size) * var(--leading-body));
    --measure: 37em; /* ~68 characters at body size — closer to Bringhurst's 66 ideal */

    /* Modular scale: perfect fourth (1.333).
     scale-minus1 floored at 14px (0.875rem at 16px root) — the WCAG-comfortable
     minimum for body-adjacent text like the byline, footnotes, and colophon.
     Previously dipped to 13px at wide viewports; the rationale ("more px on
     small screens, less on large") doesn't survive scrutiny — the byline is
     primary metadata, not a footnote, and 13px hurts it on desktop. */
    --scale-minus1: 0.875rem;
    --scale-0: 1rem;
    --scale-1: 1.333rem;
    --scale-2: 1.777rem;
    --scale-3: 2.369rem;
    --scale-4: 3.157rem;

    /* Heading font-size + leading exposed as variables so the section-wrapper
     mb calc can derive the heading slot without duplication. h2 fluid-scales
     between scale-2 (mobile) and scale-3 (desktop) on the same envelope as
     --base-size, so the hierarchy no longer hard-steps at 640px while body
     fluid-scales. h3 stays fixed — its scale-2 size is already small enough
     that mobile compression would push it under the body. */
    --h2-fontsize: clamp(1.777rem, 0.95rem + 2.6vw, 2.369rem);
    --h2-leading: 1.18;
    --h3-fontsize: var(--scale-2);
    --h3-leading: 1.25;

    /* Colour palette — light mode */
    --color-bg: #faf9f7;
    --color-text: #2c2c2c;
    --color-text-secondary: #5a5a5a;
    --color-accent: #8b4513;
    --color-rule: #d4cfc9;
    --color-code-bg: #f0ede8;
    --color-blockquote-border: #c2b8a3;
    --color-link: #5a3f25; /* 7.6:1 on --color-bg — clears WCAG AAA */
    --color-table-stripe: rgb(0 0 0 / 3%);

    /* Code syntax tinting — pure grayscale.
     Default (un-spanned) code text sits between body and punctuation;
     keywords inherit --color-text and so appear "highlighted" by contrast. */
    --code-default: #5a5a5a;
    --code-punct: #828282;
    --code-comment: #8e8a85;
}

/* --------------------------------------------------------------------------
   1. Dark Mode
   -------------------------------------------------------------------------- */
.dark {
    --color-bg: #1c1b1a;
    --color-text: #d5d0c8;
    --color-text-secondary: #b0a99c; /* 7.37:1 — WCAG AAA */
    --color-accent: #c9956b;
    --color-rule: #3a3632;
    --color-code-bg: #2a2826;
    --color-blockquote-border: #5a5247;
    --color-link: #c9956b;
    --color-table-stripe: rgb(255 255 255 / 4%);

    /* Dark mode code tinting — same hierarchy, inverted shade direction. */
    --code-default: #a09a90;
    --code-punct: #807a72;
    --code-comment: #6f6a62;
}

/* Irradiation compensation: lighter weight in dark mode.
   Headings get the same proportional reduction — without it, h1/h2/h3/h4 stay
   at 700/800 while body drops to 350 and the weight gap widens visibly.
   No letter-spacing here: a 0.005em bump used to ride along for extra
   irradiation comp, but any non-zero letter-spacing disables optional
   ligatures across the inheritance subtree, killing fi/fl/ff/Th throughout
   the article whenever dark mode was on. */
.dark body,
.dark .article-body {
    font-weight: 350;
}

.dark h1 {
    font-weight: 700;
}

.dark h2,
.dark h3,
.dark h4 {
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   1b. High-contrast preference — recalibrate the palette without redesigning.
   Body, secondary text, rules, and links all step toward the extremes; dark
   mode mirrors the shift. Only overrides what genuinely needs more contrast,
   preserving --color-bg so the page doesn't feel inverted.
   -------------------------------------------------------------------------- */
@media (prefers-contrast: more) {
    :root {
        --color-text: #000;
        --color-text-secondary: #2c2c2c;
        --color-rule: #6a6a6a;
        --color-link: #4a3520;
        --color-blockquote-border: #6a6a6a;
    }

    .dark {
        --color-text: #fff;
        --color-text-secondary: #e6e1d6;
        --color-rule: #8c8782;
        --color-link: #e6b58a;
        --color-blockquote-border: #8c8782;
    }

    a {
        text-decoration-color: currentcolor;
    }
}

/* --------------------------------------------------------------------------
   2. Base Reset & Body
   -------------------------------------------------------------------------- */
*,
*::before,
*::after {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Body font-family is in the inline #basics block (always-on) so the chosen
   font keeps rendering when crafted-typography.css is toggled off. Everything
   else about how body text looks belongs here. */
body {
    transition:
        background-color 0.3s ease,
        color 0.3s ease;
    background-color: var(--color-bg);
    color: var(--color-text);
    font-size: var(--base-size);

    /* Smooth font rendering */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-weight: 400;
    line-height: var(--leading-body);

    /* Enable kerning */
    font-kerning: normal;

    /* Optical sizing for variable fonts */
    font-optical-sizing: auto;
}

/* --------------------------------------------------------------------------
   3. OpenType Features
   -------------------------------------------------------------------------- */
.article-body {
    /* High-level properties cover kern/liga/clig/calt/onum/pnum without the
     cascade gotcha of font-feature-settings (which replaces the entire
     value when re-declared on a descendant). Kerning is on via body's
     font-kerning: normal. */
    font-variant-ligatures: common-ligatures contextual;
    font-variant-numeric: oldstyle-nums proportional-nums;

    /* Scoped to prose. optimizeLegibility forces kern + liga on at the cost
     of layout-time work per glyph run; cheap on body-length text, but the
     wrong default to inherit into the toolbar and other always-on chrome. */
    text-rendering: optimizelegibility;
}

/* The .no-opentype kill rule lives in the always-on #basics block in
   index.html, not here. If it lived here it would vanish whenever the
   "Crafted Typography" toggle disabled this stylesheet — but we want
   that very state to *also* kill OpenType (no crafted = no refinement
   anywhere). The JS sets .no-opentype whenever crafted is OFF *or* the
   OpenType toggle is OFF, and #basics applies the kill regardless of
   whether this file is loaded. */

/* --------------------------------------------------------------------------
   4. Article Container & Measure
   -------------------------------------------------------------------------- */
.article-body {
    max-width: var(--measure);
    margin: 0 auto;
    padding: calc(var(--baseline) * 2) 1.5rem calc(var(--baseline) * 4);
}

/* --------------------------------------------------------------------------
   5. Headings
   -------------------------------------------------------------------------- */
h1,
h2,
h3,
h4 {
    color: var(--color-text);
    font-weight: 700;

    /* Even out the rag of multi-line headings — browsers lay out the heading
     once to find the natural break, then redistribute words across lines so
     the last line isn't a stub. Cheap on display sizes; falls back silently. */
    text-wrap: balance;

    /* Lining figures for headings; ligatures/kerning inherit from body. */
    font-variant-numeric: lining-nums;

    /* Half-baseline of breathing room above the heading when a #anchor jump
     lands on it. Works with html { scroll-padding-top } in #ui — that pads
     the bar, this pads the heading inside the resulting scroll viewport. */
    scroll-margin-top: calc(var(--baseline) * 0.5);
}

/* Footnote items also get breathing room when jumped to (e.g. clicking a
   footnote ref). :target matches the element after the URL fragment. */
.footnotes li:target {
    scroll-margin-top: calc(var(--baseline) * 0.5);
}

/* Heading line-heights are tuned for VISUAL QUALITY at display sizes
   (tighter than body) — they intentionally don't snap to the baseline grid.
   Forcing display headings onto body rhythm requires loosening leading to
   roughly 1.5×, which makes them look airy and disconnected from their text. */
h1 {
    /* No letter-spacing: any non-zero value disables optional ligatures
     per the CSS Text spec, which kills Literata's "Th" contextual alternate
     and friends. opsz auto already tightens display sizes via the font's
     own optical-sizing axis, so manual tracking was double-tightening anyway. */
    margin-bottom: var(--baseline);

    /* Fluid h1: scale-3 (38px) on phones → scale-4 (50.5px) on wide viewports,
     bridging the same 320–820px envelope as --base-size so the entire
     hierarchy moves together. Drops the hard step that used to live at 640px. */
    font-size: clamp(2.369rem, 1.27rem + 3.4vw, 3.157rem);
    font-weight: 800;
    line-height: 1.1;
}

/* Progressive enhancement: trim h1's half-leading.
   h2/h3 also get trimmed, but their slot-restoring margin-bottom override
   lives at the end of the wrapper rules below — see the @supports block
   right after .subsection > h3. Splitting it that way is deliberate:
   the override has to come after the .section > h2 / .subsection > h3
   base rules in source order, otherwise the un-compensated calc wins on
   equal specificity and each heading collapses to ~0.6 baseline short
   of its slot, visibly drifting every following .rhythm wrapper. */
@supports (text-box-trim: trim-both) {
    h1,
    h2,
    h3 {
        text-box-trim: trim-both;
        text-box-edge: cap alphabetic;
    }

    h1 {
        margin-bottom: calc(var(--baseline) + 0.6rem);
    }
}

h2 {
    /* No letter-spacing — see h1 for the rationale (kills ligatures). */
    margin-top: calc(var(--baseline) * 2.5);
    margin-bottom: calc(var(--baseline) * 0.5);
    padding-bottom: calc(var(--baseline) * 0.25);
    border-bottom: 1px solid var(--color-rule);
    font-size: var(--h2-fontsize);
    line-height: var(--h2-leading);
}

h3 {
    margin-top: calc(var(--baseline) * 2);
    margin-bottom: calc(var(--baseline) * 0.5);
    font-size: var(--h3-fontsize);
    line-height: var(--h3-leading);
}

h4 {
    margin-top: calc(var(--baseline) * 1.5);
    margin-bottom: calc(var(--baseline) * 0.25);
    font-size: var(--scale-1);
    line-height: 1.35;
}

/* --------------------------------------------------------------------------
   5b. Section wrappers — restore vertical rhythm around headings.
   Each .section/.subsection becomes its own self-anchored rhythm zone:
   the wrapper's content area starts at the heading, and the heading's
   margin-bottom is computed (font-size × line-height) so the heading
   occupies an integer number of baselines. Body text following the
   heading therefore lands cleanly on every baseline within the section.
   -------------------------------------------------------------------------- */
.section {
    margin-top: calc(var(--baseline) * 3); /* 3-baseline gap between sections */

    /* Off-screen sections skip layout + paint until they scroll into view.
     contain-intrinsic-size reserves vertical space so the scrollbar
     doesn't lurch as sections enter/leave the viewport — the 800px guess
     is a sensible default for our prose density; the browser refines it
     on first paint and remembers per-section. */
    content-visibility: auto;
    contain-intrinsic-size: auto 800px;
}

.section > h2 {
    margin-top: 0;

    /* h2 slot = 3 baselines. Uses font-size × leading variables instead of 1lh
     (Safari < 17.2 doesn't support the lh unit and would invalidate the calc).
     Single source of truth — mobile media query overrides --h2-fontsize and
     this calc auto-recomputes; no duplicate rule needed. */
    margin-bottom: calc(var(--baseline) * 3 - var(--h2-fontsize) * var(--h2-leading) - var(--baseline) * 0.25);
    padding-bottom: calc(var(--baseline) * 0.25);
}

/* Two-line h2 slot = 4 baselines. Pure CSS can't introspect line count, so
   the class is attached by hand to headings known to wrap at the current
   --measure (37em). If --measure or --h2-fontsize change enough that the
   wrap point shifts, re-audit which headings need the class. */
.section > h2.h2-2line {
    margin-bottom: calc(var(--baseline) * 4 - var(--h2-fontsize) * var(--h2-leading) * 2 - var(--baseline) * 0.25);
}

.subsection {
    margin-top: calc(var(--baseline) * 2); /* 2-baseline gap before subsection */
}

.subsection > h3 {
    margin-top: 0;

    /* h3 slot = 2 baselines */
    margin-bottom: calc(var(--baseline) * 2 - var(--h3-fontsize) * var(--h3-leading));
}

/* Trim re-derivation for the heading slots. The base .section > h2 and
   .subsection > h3 rules above subtract the full pre-trim line-box
   (font-size × leading) from N baselines. When text-box-trim trims the
   half-leading off, the visible box collapses to roughly font-size ×
   cap-height-ratio, so the calc has to swap the leading factor for the
   ratio to keep the slot at N × baseline.

   The ratio is *both* typeface-dependent *and* size-dependent: opsz auto
   picks a taller-cap optical variant at display sizes, so h2 (≈38px) has
   a different ratio than h3 (≈28px). Each per-font sheet exposes two
   tokens — --cap-height-display for h2/h1 sizes, --cap-height-text for h3
   sizes — with a shared --cap-height fallback for any pair that hasn't
   been migrated yet. This @supports block must come AFTER the base
   wrapper rules: same selector specificity, later one wins. */
@supports (text-box-trim: trim-both) {
    .section > h2 {
        margin-bottom: calc(
            var(--baseline) * 3 - var(--h2-fontsize) * var(--cap-height-display, var(--cap-height, 0.72)) -
                var(--baseline) * 0.25
        );
    }

    /* Two-line h2 under trim: visible height = cap-line of line 1 + full
     line-height between baselines + (the trim removes the descender below
     line 2). So subtract cap-height-once + leading-once + padding. */
    .section > h2.h2-2line {
        margin-bottom: calc(
            var(--baseline) * 4 - var(--h2-fontsize) * var(--cap-height-display, var(--cap-height, 0.72)) -
                var(--h2-fontsize) * var(--h2-leading) - var(--baseline) * 0.25
        );
    }

    .subsection > h3 {
        margin-bottom: calc(var(--baseline) * 2 - var(--h3-fontsize) * var(--cap-height-text, var(--cap-height, 0.7)));
    }
}

/* Two-column flow for the Vertical Rhythm section, so the baseline grid
   reads across both columns at once and the rhythm is doubly visible.
   `columns: 13em 2` caps at two columns and lets the browser collapse
   to one when the article narrows past the threshold (~26em + gap).
   Both columns inherit the same line-height, so every line in either
   column lands on the same baseline as its sibling across the gap.
   Gap is one baseline so the gutter shares the rhythm token; the
   trailing paragraph's margin-bottom is zeroed so the shortest column
   doesn't add a stray baseline (column-fill: balance handles the rest). */
.rhythm-columns {
    columns: 13em 2;
    column-gap: var(--baseline);
}

.rhythm-columns > p:last-child {
    margin-bottom: 0;
}

/* --------------------------------------------------------------------------
   6. Paragraphs & Prose
   -------------------------------------------------------------------------- */
p {
    margin-bottom: var(--baseline);
    hanging-punctuation: first allow-end last;
}

/* Hyphenation for body text */
.article-body > p,
.article-body blockquote p {
    /* `pretty` adjusts only the last few lines to avoid orphans/widows — cheap
     on running prose, unlike `balance` which re-lays-out the whole paragraph
     and is capped at ~6 lines. Falls back silently on older browsers. */
    text-wrap: pretty;
    hyphens: auto;
    hyphenate-limit-chars: 6 3 2;
    hyphenate-limit-lines: 2;
    hyphenate-limit-last: always;
}

/* Lede paragraph */
.lede {
    margin-bottom: calc(var(--baseline) * 1.5);
    color: var(--color-text-secondary);
    font-size: var(--scale-1);

    /* Length value (1.5 baselines per line) so the lede sits cleanly on the
     rhythm grid; using a unitless multiplier here would resolve to 2.066rem
     per line and drift body text below. */
    line-height: calc(var(--baseline) * 1.5);

    /* Avoid orphans/widows and find better break points across the lede;
     unlike `balance`, `pretty` only adjusts the last few lines so it works
     well for body-length text. */
    text-wrap: pretty;
}

.lede::first-line {
    /* all-small-caps (not small-caps) so the leading capital also drops to a
     small cap — otherwise the opening letter stays full-height and the line
     reads as a mixed-case anomaly. */
    font-variant-caps: all-small-caps;
    font-weight: 500;

    /* Small-cap texture — the standard 0.04–0.1em range. Non-zero
     letter-spacing disables optional ligatures across the inheritance
     subtree, so this would normally be too risky for body prose; but
     ::first-line is scoped to the first wrapped line only, and that line
     in this article ("Typography is the craft of endowing human
     language…") contains no fi/fl/Th candidates. Re-test if the lede
     content changes. */
    letter-spacing: 0.04em;
}

/* Subtitle */
.subtitle {
    margin-bottom: calc(var(--baseline) * 0.5);
    color: var(--color-text-secondary);
    font-size: var(--scale-2);
    font-style: italic;
    font-weight: 400;

    /* Length value (1.5 baselines per line) so the subtitle sits on the grid;
     a unitless multiplier here resolves to 2.399rem and drifts the header
     out of rhythm before the lede picks it back up. */
    line-height: calc(var(--baseline) * 1.5);

    /* Subtitle is short (1–2 lines) and heading-like — balance evens the rag
     of the wrap rather than just polishing the last few lines. */
    text-wrap: balance;
}

/* Byline.
   `all-small-caps` already smalls both upper- and lowercase, so layering
   `text-transform: uppercase` on top is at best redundant and at worst forces
   a Unicode-cased path that bypasses small-cap synthesis. Letter-spacing
   bumped to 0.05em — the standard 0.04–0.1em range for small-caps texture. */
.byline {
    margin-bottom: calc(var(--baseline) * 2);
    padding-bottom: calc(var(--baseline) * 0.5);
    border-bottom: 1px solid var(--color-rule);
    color: var(--color-text-secondary);
    font-size: var(--scale-minus1);
    letter-spacing: 0.05em;
    font-variant-caps: all-small-caps;
    font-variant-numeric: lining-nums;
}

.byline .separator {
    margin: 0 0.25em;
}

.byline .author {
    font-weight: 600;
}

/* --------------------------------------------------------------------------
   7. Inline Elements
   -------------------------------------------------------------------------- */

/* Abbreviations: true small caps + letter-spacing.
   Underline-dotted rides closer to the text than a border-bottom and shares
   the underline-offset model used for links, so the dotted hint feels like
   part of the typography rather than a frame around the word. */
abbr[title] {
    font-variant-caps: all-small-caps;
    letter-spacing: 0.06em;
    text-decoration: underline dotted var(--color-rule);
    text-underline-offset: 0.2em;
    cursor: help;
}

/* Plain abbr without title — still small-capped */
abbr:not([title]) {
    font-variant-caps: all-small-caps;
    letter-spacing: 0.06em;
}

/* Touch devices can't surface the native title tooltip, so the abbreviation's
   meaning would otherwise be hidden. Inline the expansion in a quieter style
   so taps reveal nothing the user couldn't already see. */
@media (hover: none) {
    abbr[title] {
        text-decoration: none;
        cursor: default;
    }

    abbr[title]::after {
        content: ' (' attr(title) ')';
        color: var(--color-text-secondary);
        font-size: 0.85em;
        letter-spacing: 0;
        font-variant-caps: normal;
    }
}

/* Ugly all-caps demo class */
.all-caps-ugly {
    font-variant-caps: normal;
    letter-spacing: 0;
}

/* Superscript & subscript: high-level property uses real OpenType glyphs
   when the font supplies them (digits) and synthesizes for letters (st/nd/rd).
   line-height: 0 prevents the raised glyph from disrupting line spacing.
   lining-nums because footnote markers are digits and old-style figures
   already dip below the baseline — combining that with a raised position
   produces an oddly-sized, oddly-placed glyph. */
sup,
sub {
    line-height: 0;
    font-variant-numeric: lining-nums;
}

sup {
    font-variant-position: super;
}

sub {
    font-variant-position: sub;
}

/* Emphasis */
em {
    font-style: italic;
}

strong {
    font-weight: 700;
}

/* Definitions */
dfn {
    font-style: italic;
    font-weight: 500;
}

/* Inline quotes */
q {
    quotes: '\201C' '\201D' '\2018' '\2019';
}

/* Small text */
small {
    color: var(--color-text-secondary);
    font-size: 0.85em;
}

/* Links */
a {
    transition: text-decoration-color 0.2s ease;
    color: var(--color-link);
    text-decoration: underline;
    text-decoration-color: rgb(90 63 37 / 40%);
    text-decoration-thickness: 1px;
    text-underline-offset: 0.15em;
}

a:hover {
    text-decoration-color: var(--color-link);
}

/* Numeric spans */
.num {
    font-variant-numeric: lining-nums tabular-nums;
}

/* Fractions */
.frac {
    font-variant-numeric: diagonal-fractions;
}

/* Ordinals (1st, 2nd, 3rd → real OpenType ordinal glyphs when supported) */
.ordinal {
    font-variant-numeric: ordinal;
}

/* Discretionary ligatures (ct, st, etc.) — display use, sparingly */
.dlig {
    font-variant-ligatures: common-ligatures contextual discretionary-ligatures;
}

.fraction-demo {
    font-size: 1.1em;
}

/* Old-style figure demo */
.oldstyle {
    font-variant-numeric: oldstyle-nums proportional-nums;
}

/* Lining figure demo */
.lining {
    font-variant-numeric: lining-nums tabular-nums;
}

/* Kerning demo — pair .kern-off with .kern-on to show the difference.
   Used as <span class="kern-off">AV</span>/<span class="kern-on">AV</span>.
   Size stays at body em so the demo sits in-line with surrounding prose
   instead of inflating the line-box; weight bump still makes the kerning
   gap easy to read against the body text. */
.kern-off,
.kern-on {
    font-weight: 600;
}

.kern-off {
    font-kerning: none;
    font-feature-settings: 'kern' 0; /* explicit off — overrides body's font-kerning: normal */
}

.kern-on {
    font-kerning: normal;
}

/* --------------------------------------------------------------------------
   8. Code
   -------------------------------------------------------------------------- */

/* code font-family is in the inline #basics block (always-on). Everything
   else about how code looks lives here. */
code {
    padding: 0.1em 0.35em;
    border-radius: 3px;
    background: var(--color-code-bg);
    font-size: 0.9em;
    hyphens: none;

    /* High-level off-switch for ligatures; kerning stays on (inherited from body). */
    font-variant-ligatures: none;
}

pre {
    margin-bottom: var(--baseline);
    padding: var(--baseline) 1.25rem;
    overflow-x: auto;
    border-radius: 8px;
    background: var(--color-code-bg);

    /* Layered shadow replaces the old `border-left: 3px solid var(--color-accent)`:
     a hairline ring at 6% acts as a 1px border, a tight lift sits just below,
     and a soft ambient cast fades into the page. Shadows don't occupy layout
     space, so the 1-baseline top/bottom padding (and the grid) is untouched. */
    box-shadow:
        0 0 0 1px rgb(0 0 0 / 6%),
        0 1px 2px -1px rgb(0 0 0 / 6%),
        0 2px 4px 0 rgb(0 0 0 / 4%);
    tab-size: 2;
}

.dark pre {
    /* Single white ring in dark mode — layered depth shadows aren't visible
     on dark surfaces, so they're dropped. */
    box-shadow: 0 0 0 1px rgb(255 255 255 / 6%);
}

pre code {
    padding: 0;
    background: none;

    /* Default code text is dimmed so .tok-keyword (at full body strength)
     reads as the "highlight" without any weight or hue change. */
    color: var(--code-default);

    /* 0.875em (not 0.8em) so mobile pre-code lands at 14px instead of 12.8px —
     under the readable floor for monospace at viewing distance. */
    font-size: 0.875em;

    /* Length value (not multiplier) so each code line equals one baseline,
     keeping multi-line pre blocks on the rhythm grid. */
    line-height: var(--baseline);
}

/* Syntax tinting — four shades of gray, no hues. Keywords (selectors,
   property names) inherit the page's body color, so they stay at full
   strength while everything else recedes. Italic avoided because the
   mono fonts loaded for code are roman-only at the rendered weight. */
.tok-keyword {
    color: var(--color-text);
}

.tok-punct {
    color: var(--code-punct);
}

.tok-comment {
    color: var(--code-comment);
}

/* --------------------------------------------------------------------------
   9. Blockquotes
   -------------------------------------------------------------------------- */
blockquote {
    margin: var(--baseline) 0;
    margin-left: 1.5rem;
    padding-left: 1.25rem;
    border-left: 3px solid var(--color-blockquote-border);
    font-style: italic;
}

blockquote p {
    /* 1 baseline so the next sibling (footer) lands on the grid */
    margin-bottom: var(--baseline);
}

blockquote footer {
    margin-top: 0;
    color: var(--color-text-secondary);
    font-size: var(--scale-minus1);
    font-style: normal;

    /* Length value so footer occupies exactly one baseline; mt 0 because
     blockquote p's mb already provides the grid-aligned gap. */
    line-height: var(--baseline);
}

/* --------------------------------------------------------------------------
   10. Tables
   -------------------------------------------------------------------------- */
table {
    width: 100%;
    margin-bottom: var(--baseline);
    border-collapse: collapse;
    font-size: 0.95em;
    font-variant-numeric: lining-nums tabular-nums;
}

caption {
    margin-bottom: calc(var(--baseline) * 0.5);
    color: var(--color-text-secondary);
    font-style: italic;
    text-align: left;
}

th {
    padding: calc(var(--baseline) * 0.35) 0.75rem;
    border-bottom: 2px solid var(--color-rule);
    font-weight: 600;
    letter-spacing: 0.05em;
    text-align: left;
    font-variant-caps: all-small-caps;
}

td {
    padding: calc(var(--baseline) * 0.35) 0.75rem;
    border-bottom: 1px solid var(--color-rule);
}

tbody tr:nth-child(even) {
    background: var(--color-table-stripe);
}

/* Right-align numeric columns. tabular-nums (set on `table` above) makes
   each digit advance the same width; right-aligning then locks the trailing
   edge so values like "0.75rem" and "1.333rem" line up by the final `m` and
   the eye reads down the column cleanly. Header cell shares the alignment
   so the column reads as a single right-anchored stack. */
th.col-num,
td.col-num {
    text-align: right;
}

/* --------------------------------------------------------------------------
   11. Definition Lists
   -------------------------------------------------------------------------- */
dl {
    margin-bottom: var(--baseline);
}

dt {
    /* 1-baseline gap before each term so dt+dd entries land on grid */
    margin-top: var(--baseline);
    font-weight: 600;
}

dt code {
    background: var(--color-code-bg);
    color: var(--color-accent);
    font-size: 0.9em;
    font-weight: 600;
}

dd {
    /* 0 — gap to next term comes from dt's margin-top via collapse */
    margin-bottom: 0;
    margin-left: 1.5rem;
    color: var(--color-text-secondary);
    text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   12. Footnotes & Article Footer
   -------------------------------------------------------------------------- */
.article-footer {
    margin-top: calc(var(--baseline) * 3);
    padding-top: var(--baseline);
    border-top: 1px solid var(--color-rule);
}

.article-footer h3 {
    margin-top: 0;
    margin-bottom: calc(var(--baseline) * 0.5);
    font-size: var(--scale-1);
}

.footnotes {
    padding-left: 1.5rem;
    color: var(--color-text-secondary);
    font-size: var(--scale-minus1);
}

.footnotes li {
    margin-bottom: calc(var(--baseline) * 0.5);
    text-wrap: pretty;

    /* Hyphenation matches .article-body > p; the .footnotes <li> carries text
     directly (no inner <p>) so the body-paragraph rule doesn't reach it,
     leaving footnotes with a rougher rag at narrow viewports. */
    hyphens: auto;
    hyphenate-limit-chars: 6 3 2;
    hyphenate-limit-lines: 2;
    hyphenate-limit-last: always;
}

.footnote-back {
    margin-left: 0.25em;
    text-decoration: none;
}

.colophon {
    margin-top: var(--baseline);
    color: var(--color-text-secondary);
    font-size: var(--scale-minus1);
    font-style: italic;
    text-wrap: pretty;
}

/* --------------------------------------------------------------------------
   13. Cite & Time
   -------------------------------------------------------------------------- */
cite {
    font-style: italic;
}

time {
    font-variant-numeric: lining-nums;
}

/* --------------------------------------------------------------------------
   14. Selection
   -------------------------------------------------------------------------- */
::selection {
    background: rgb(139 69 19 / 15%);
    color: inherit;
}

.dark ::selection {
    background: rgb(201 149 107 / 25%);
}

/* --------------------------------------------------------------------------
   15. Reduced motion
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    body,
    a {
        transition: none;
    }
}

/* --------------------------------------------------------------------------
   16. Print Styles
   -------------------------------------------------------------------------- */

@media print {
    .controls {
        display: none !important;
    }

    body {
        padding-top: 0;
    }

    .article-body {
        max-width: none;
        font-size: 11pt;
        line-height: 1.5;
    }

    a[href]::after {
        content: ' (' attr(href) ')';
        color: #666;
        font-size: 0.8em;
    }

    pre,
    blockquote {
        break-inside: avoid;
    }

    h2,
    h3 {
        break-after: avoid;
    }
}

/* --------------------------------------------------------------------------
   17. Responsive Adjustments
   -------------------------------------------------------------------------- */
@media (width <= 640px) {
    /* All four type scales (--base-size, --scale-minus1, --h2-fontsize, h1)
     fluid-scale via clamp() in :root and the h1 rule. This breakpoint now
     only carries layout adjustments — narrower article padding and
     full-width blockquotes. */
    .article-body {
        padding: var(--baseline) 1rem calc(var(--baseline) * 3);
    }

    blockquote {
        margin-left: 0;
    }
}
