/* The @font-face rules for Fredoka and Baloo 2 were removed — nothing
   referenced them. The woff2 files are still in public/fonts/; to use one again,
   re-declare it here AND point --font at it. The site needs no @font-face at
   all as it stands: every face it has used is web-safe or a CSS generic, which
   is what keeps it openable from file://. See "Choosing a typeface" in
   AGENT.md. */

/* ---------- DM SANS — THE FACE IN USE (2026-08-06) ----------
   Self-hosted, SIL Open Font License, latin subset, 14 KB. Nothing is fetched
   from a CDN: a Google Fonts <link> would break both the zero-dependency rule
   and opening the page from file://. To update it, download the woff2 and
   replace the file — don't link out.

   ⚠️ It is here as the closest FREE stand-in for Plain Medium (see below), and
   it is closer than it sounds. Measured against Plain on baenziger-hug.com's own
   page — x-height/cap-height and set width, at 100px:
       PlainMedium  x/cap 0.732   w/cap 15.611
       DM Sans 500  x/cap 0.729   w/cap 15.423   ← 1.7% off, the best of 12
       Space Grotesk .............................. 3.4%
       IBM Plex Sans .............................. 5.2%
       Inter ...................................... 7.6%
       Archivo, Roboto, Public Sans, Work Sans, Karla, … all worse
   ⚠️ THAT IS A METRIC MATCH, NOT A LOOKALIKE. It reproduces the COLOUR and
   rhythm of a block of text — which at this size is most of the effect — and
   not the letterforms. DM Sans is geometric where Plain is a grotesque; IBM
   Plex Sans is the closer match in genre if the shapes ever matter more than
   the texture. */
@font-face {
    font-family: "DM Sans";
    src: url("fonts/dm-sans-500.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}

/* ---------- PLAIN (Optimo) — THE REAL THING, IF IT IS EVER BOUGHT ----------
   Beth chose Plain Medium on 2026-08-06: François Rappo / Optimo, the face
   baenziger-hug.com sets its whole site in (`font-family: PlainMedium`).

   ⚠️ IT IS A COMMERCIAL LICENCE — buy it at optimo.ch. There is nothing to
   download for free, and the base64 WOFF embedded in baenziger-hug.com's own
   stylesheet is THEIR licensed copy: don't lift it.

   TO SWITCH ONCE THE FILE EXISTS — three steps, no other edits:
     1. put the woff2 at public/fonts/plain-medium.woff2
        (Optimo ships otf/woff/woff2; convert with fonttools if needed —
         `pyftsubset in.otf --flavor=woff2 --output-file=out.woff2`)
     2. uncomment the @font-face below
     3. set --font: "Plain", "DM Sans", sans-serif;  ← keep the fallbacks
        AND re-measure `.text`'s two font-sizes. Plain's x-height is 52 at
        100px against DM Sans's 51, so the sizes barely move — roughly
        `× 0.98` — but measure rather than assume.

@font-face {
    font-family: "Plain";
    src: url("fonts/plain-medium.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}
*/

/* Palette (mirrored in palette.md). ONE accent runs through links, the
   favicon and the top rule. Deliberately named --accent, not by colour,
   so swapping the hue doesn't mean renaming anything: it has been the
   Sony orange (#F07C0C), red, a danger orange (#FF4F00), hot pink
   (#FF1493), and back to red. --bg is the page ground — a light grey
   rather than pure white — and the menu panels reuse it so they read as
   the page, not as cards. */
:root {
    --accent:      #E01B1B;   /* red */

    /* ⚠️ INVERTED 2026-08-06 — the page was light (--bg #F0F0F0, black text)
       until then. These two are lifted EXACTLY from baenziger-hug.com, the
       reference the bio's setting already came from:
           .e-body-index { color: #747474; background: #181818 }
       so the ground and the ink are now theirs as well as the measure.

       ⚠️ --ink IS NOT THEIRS — only --bg is taken verbatim. Their #747474 is
       3.80:1 on this ground, which FAILS WCAG AA for body text (4.5:1), so the
       ink was lightened on 2026-08-06 for readability:
           #747474 (theirs)  116   3.80:1  — fails AA
           #8B8B8B  ×1.2     139   5.21:1  — clears AA          ← CURRENT
           #D0D0D0  ×1.5     208  11.51:1  — clears AAA
       ×1.5 was tried the same day and REVERTED: it lost the reference's muted,
       low-contrast character, which is the point of the palette. ×1.2 is the
       settled compromise — over the AA line, still quiet.
       ⚠️ Don't "restore" #747474 as a fidelity fix, and don't push past #8B8B8B
       for legibility: both have been decided.

       Everything reads this — the bio, the index labels, AND the links, which
       are now bold text with no colour of their own (see .text a). */
    --bg:          #181818;
    --ink:         #8B8B8B;

    /* Links, one step brighter than the ink — #8B8B8B ×1.25 (139 → 174), which
       is 8.00:1 on the ground against the ink's 5.21:1. Added 2026-08-06 when
       bold alone was not separating a link from the text around it.
       ⚠️ KEEP THE TWO RELATED. They are a pair: brightness is now half of what
       marks a link (weight is the other half), so moving --ink without moving
       this closes the gap and links stop reading as links. */
    --link:        #AEAEAE;

    /* ONE switch for the site's typeface — `body` reads it and everything
       inherits, links and index included. Both stacks are web-safe: nothing to
       download, and the page still opens from `file://`.

       ⚠️ CHANGING THIS IS TWO EDITS, NOT ONE. These faces do not match in size
       at the same value, so `.text`'s font-size moves with the family — in BOTH
       places, the base rule and the phone media query. The pairs are listed
       against each stack below.

       ⚠️ The old note here said "Times needs body 105%" and was STALE: `.text`
       sizes are in `rem`, which is relative to the ROOT, not to `body`. Changing
       `body { font-size }` no longer moves any visible text, because everything
       on the page now sits inside `.text`. Compensate on `.text`.

         --font: Arial, Helvetica, sans-serif;              (.text 0.9889 / 1.225)
         --font: "Times New Roman", Times, serif;           (.text 1.0383 / 1.2863)
         --font: "Courier New", monospace;                  (.text 1.1959 / 1.4814)
         --font: monospace;                                 (.text 1.0494 / 1.2997)
         --font: "DM Sans", sans-serif;              ← CURRENT (.text 1.0083 / 1.2487)
         --font: "Fredoka", "Trebuchet MS", system-ui, sans-serif;
                        (self-hosted in public/fonts/ — re-declare its @font-face)

       ⚠️ THE LIVE SIZE IS `0.9075 / 1.1238`. That chain: the mono pair above,
       ×0.9 (Beth asked for 10% smaller on 2026-08-06), then ×0.9608 when the
       face became DM Sans — the ratio of the two x-heights, so the text stayed
       exactly the same optical size through the swap. The pairs listed above are
       the X-HEIGHT-MATCHED reference values for each face; the live value is
       whatever the current face's pair is × 0.9.

       Those sizes are not guesses — they hold the X-HEIGHT steady, measured off
       a canvas at 100px, so only the FACE changes between trials:
         Arial 52.0 / 72.0   Times 45.0 / 67.0   Courier 43.0 / 58.0
         monospace 49.0 / 64.0   DM Sans 51.0    (PlainMedium 52.0 / 71.0)
       Courier New is the small one, hence its ×1.209 — and why it read as
       oversized once compensated. Bare `monospace` needs only ×1.061.

       ⚠️ EVERY MONOSPACE IS WIDER THAN ARIAL, so lines wrap earlier than the
       bio's "let them run out" setting intends: the same sample line measures
       1273 in Arial, 1484 in `monospace`, 1620 in Courier New. Judge a mono face
       on that, not on its letterforms.

       ⚠️ `monospace` IS NOT ONE FONT — it is the CSS generic, i.e. whatever the
       reader's browser calls monospace. Consolas on Chrome/Windows (the metrics
       above), Courier New on Firefox/Windows, Menlo or SF Mono on macOS, DejaVu
       Sans Mono on most Linux. So the page will NOT look identical everywhere,
       and the sizes above can only be tuned for one of them. That is either a
       defect or the entire point — see "Choosing a typeface" in AGENT.md, where
       using the reader's own default is exactly the "undesigned" signal the
       brief asks for. Taken from wlan.sybil.gg, which does the same thing.

       History: Times → Fredoka → Arial → Times → Arial over 2026-08-01, settled
       on Arial; then Times → Courier New → monospace on 2026-08-06, all at
       Beth's request. See "Choosing a typeface" in AGENT.md before arguing
       either way. */
    --font: "DM Sans", sans-serif;

    /* `--nav-clear` (180.5px) lived here until 2026-08-06. It was the left strip
       `.container` reserved to clear the fixed dropdown menu. The menu is gone —
       the links are body text now — so there is nothing to clear and everything
       sits hard left against the ordinary gutter. */

    /* The 4px bar across the top of every page (`body::before`). It is the
       WHOLE accent cycle laid out left-to-right, so the strip shows at a glance
       the colours a click walks through.
       ⚠️ This value is a FALLBACK. `app.js` builds the same ramp from `ACCENTS`
       — its single source of truth — and overwrites this property at startup,
       so editing the array is enough and this line never has to be touched.
       It matters only before the script runs, and with JS off. Keep it roughly
       in step with ACCENTS the same way `--accent` is kept in step with
       ACCENTS[0]. */
    --accent-ramp: linear-gradient(90deg,
        #E01B1B, #FF4F00, #F07C0C, #B80F6B, #A81414, #767676,
        #24DB00, #CFC87B);
}
/* ⚠️ MOBILE TEXT AUTOSIZING — OFF.
   Phone browsers inflate text per block on rotation, and they treat every
   `inline-block` as its own block. `.text a` must be inline-block for the mirror
   transform, so the links got a different inflation factor from the paragraph
   around them — in landscape they came out visibly smaller than the body text.

   NOT engine-specific, which is why all three forms are here: WebKit does it
   (and on iOS *every* browser is WebKit, Firefox included), and Gecko has its own
   equivalent, font inflation. Reported on Firefox mobile.
   (baenziger-hug.com does the same, in its own `html` rule.) */
html {
    -webkit-text-size-adjust: 100%;
    -moz-text-size-adjust: 100%;
    text-size-adjust: 100%;
}
body {
    font-family: var(--font);
    color: var(--ink);
    /* No visible effect any more — every element on the page sits inside
       `.text`, which sets its own `rem` size (root-relative, not body-relative).
       Kept as the sane default for anything added outside `.text`. */
    font-size: 100%;
    background: var(--bg);
    /* ZEROED (2026-08-06). The browser's default 8px body margin was stacking on
       top of `.container`'s padding, so the real offsets were 8px more than the
       numbers there said. Everything is set on `.container` now — one place to
       read the margins off, and the tight ones Beth asked for are only reachable
       once this is out of the way. */
    margin: 0;
}
/* THE TOP RULE — a 4px band spanning the full width of the window, carrying a
   gradient through every colour in the accent cycle (see `--accent-ramp`).
   Static: it shows the whole list at once, so it does NOT change when a click
   advances `--accent`.

   Done as `body::before` rather than a `<div>` so both pages get it from the
   stylesheet alone, with nothing to add to the markup (and nothing to forget on
   a third page).

   `fixed`, so it stays pinned to the top edge while the page scrolls — it reads
   as a border on the window, not a strip of content.

   `z-index: 20` is left over from when a draggable menu could be dropped over
   the top edge and had to pass UNDER the bar. Nothing is fixed-position any more,
   so it is doing no work — harmless, and worth keeping if anything ever floats.
   `pointer-events: none` keeps it out of the way of the site-wide click that
   flips the mirror and cycles the accent. */
body::before {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 4px;
    background: var(--accent-ramp);
    z-index: 20;
    pointer-events: none;
}
a {
    color: var(--accent);
}
.container {
    display: flex;
    flex-direction: column;
    /* HARD LEFT. Everything starts at the same place — the left edge of the
       column beside the menu — so the portrait lines up with where the bio's
       first character sits. (`center` left the half-size portrait stranded in the
       middle of a wide window while the text was hard left.) */
    align-items: flex-start;
    /* NO CAP. The bio runs the full width of the window, so a line only wraps
       when it actually reaches the right edge — never because a column ran out.
       (It was capped at 960px, which broke the degrees line onto a second line on
       a wide screen.) The only limits are the 30px gutters. */
    max-width: none;
    /* LEFT-ALIGNED, not centred. Centring put the text out in the middle of a
       wide window; everything on the page starts at the same left edge. */
    margin: 0;
    /* TIGHT — Beth's brief on 2026-08-06, with baenziger-hug's footer and a
       terminal log as the references: text hard into the top-left corner, the way
       a log starts at the window edge.

       These are the WHOLE offset now that `body { margin: 0 }` — nothing else is
       added on top.
         top   10px — the 4px top rule plus 6px of air under it
         left  10px — matching, so the corner reads as one measure
         right 24px — bigger on purpose, and invisible: it is slack for a line
                      that reaches the edge, not a margin you can see
         bottom 40px — just so the last line isn't flush against the fold

       ⚠️ Don't tighten `top` much further without moving the bar too: below
       ~8px the first line starts closing on the gradient. */
    padding: 10px 24px 40px 10px;
}

/* portrait — the whole content of headshot.html. (It replaced the spinning
   phrase-ring animation; that version is kept in
   Content/Code/index-phrasering-2026-08-01.html.)

   `min(140.25px, 23.375vw)` caps it on wide windows and lets it shrink with
   narrow ones. The two terms are equal at a 600px viewport, so the size is
   continuous straight through the breakpoint — resizing glides rather than
   snapping. **Rescale by multiplying BOTH numbers by the same factor**, or that
   stops being true. (These are the previous 280.5 / 46.75 halved.) */
.portrait-wrap {
    width: min(140.25px, 23.375vw);
    flex: 0 0 auto;
}
/* Mirrors under the SAME rule as the body links: rests flipped, and the
   site-wide click toggles `.sharp` to un-flip it. Instant, no transition. */
.portrait {
    display: block;
    width: 100%;
    height: auto;
    transform: scaleX(-1);   /* mirrored — the default on load */
    -webkit-user-select: none;
    user-select: none;
}
.portrait.sharp {
    transform: scaleX(1);    /* normal (un-mirrored) after a tap toggles it */
}

/* ---------- THE BODY TEXT (the bio, AND the link index) ----------
   Since 2026-08-06 these rules set BOTH: the bio, and the block of link lines
   that replaced the dropdown menu (`.index` below carries `class="text"` too).
   Change something here and it lands on both — which is the point.

   Modelled on baenziger-hug.com (Beth's reference): text set VERY SMALL, with
   very tight leading, hard left, running out across a wide measure in dense
   blocks. The "terminal" feel comes from that setting — not from a monospace
   face, and NOT from the references' colours. It is Arial like the rest of the
   site (mono was tried on 2026-08-01 and dropped the same day), on this
   palette's own grey ground and black text.

   The three levers, in order of how much they do the work:
     font-size  — small is the whole point
     line-height — 1.25, near-solid setting
     the width  — the full window; lines are meant to run out             */
.text {
    /* ~16.6px. The chain: the reference's 12px, +15%, +5% (= 0.9056rem, ~14.5px)
       → +1pt on 2026-08-06 (1pt = 1.333px = 0.0833rem, giving 0.9889rem)
       → scaled the same day for the face in use: this value holds the x-height
       equal to Arial's 0.9889rem. Under Arial use 0.9889, Times 1.0383 — see
       the table beside `--font`.
       Still deliberately small; the leading below is the reference's ratio, so
       the two move together.
       Kept in `rem` rather than `pt` or `px` so it still scales with a reader's
       own font-size setting — the same reason every other size here is relative.
       ⚠️ DESKTOP ONLY in effect: the phone rule overrides this. */
    font-size: 0.9075rem;
    /* ⚠️ NO `-webkit-text-stroke` — REMOVED 2026-08-06, and the reason it
       existed is gone with Arial.

       It was `0.25px currentColor`: a hairline stroke in the text's own colour,
       faking a half-step of weight, because Arial ships only 400 and 700 and
       `font-weight: 500` rounds to one or the other — 700 being far too heavy at
       this size. It was there to match the reference's *medium*, not because the
       page wanted thicker text.

       Under a monospace face it was simply making an already-even face heavier
       and closing up its counters, so Beth asked for less weight. MEASURED, as
       total ink over two rendered paragraphs:
         0.25px (as it was) ... baseline
         0.20px ............... −4.7%   ← taking 20% off the STROKE does almost
                                          nothing; it is a thin addition to a
                                          stem that is already there
         removed .............. −15.1%  ← the whole available range
       So this is as light as the page goes without changing the face or the ink
       colour. `font-weight: 300` is NOT another lever: browsers synthesise
       BOLDER weights but never lighter ones, so it would render at 400.

       If it ever comes back for a two-weight face, keep it under ~0.3px. */
    /* Taken from baenziger-hug's own stylesheet, not estimated off a screenshot:
       they set `font-size: 12px; line-height: 15px`, i.e. exactly 1.25. (Measuring
       the screenshot suggested ~1.45 — it had been captured at ~1.25× zoom, which
       scaled the 15px pitch to ~18.75px. Worth remembering before trusting a
       measurement taken off an image.)
       The density in that reference comes from the blocks running CONSECUTIVELY
       as much as from the leading — see the paragraph rule below. */
    line-height: 1.25;
    text-align: left;
    /* fills `.container` rather than shrink-wrapping inside it */
    max-width: none;
    width: 100%;
    /* REQUIRED: the phone rule adds 24px of side padding. Without
       border-box that padding lands OUTSIDE any max-width cap, making the
       column 48px wider than intended and pushing the whole document past
       the viewport on narrow phones. */
    box-sizing: border-box;
}
/* NO gap between paragraphs at all — every line sits at the same pitch as every
   other, so the bio reads as one continuous list, the way the reference's awards
   block does. This is what "match this line distance" meant: the blank line
   between blocks was the whole discrepancy, not the leading. */
.text p {
    margin: 0;
}
/* Per-line highlight — CURRENTLY INERT, kept deliberately.
   It paints each wrapped line its own band in the page's own ground colour, so
   it shows up only where text crosses an image. That was the bio riding over the
   portrait; since the 2026-08-01 split those live on separate pages and nothing
   overlaps, so this draws nothing. The `<span class="hl">` wrappers stay in the
   markup and this rule stays here so the effect is one page-layout change away —
   see AGENT.md. `box-decoration-break: clone` is what repeats the background on
   every wrapped line rather than drawing one ragged blob; an inline wrapper is
   required, as a background on the `<p>` would be a plain block rectangle. */
.hl {
    background: var(--bg);
    -webkit-box-decoration-break: clone;
    box-decoration-break: clone;
}
/* ---------- BODY LINKS — WEIGHT AND BRIGHTNESS, NOT A BAR (2026-08-06) -------
   A link is the body text set BOLD and one step brighter than the ink around it,
   going to `--accent` on hover. Nothing else marks it: no underline, no fill,
   no padding. It was weight ALONE for an hour on 2026-08-06 — `color: inherit` —
   and that was not enough separation, hence `--link`.

   ⚠️ THE REDACTION BAR IS GONE. For most of this site's life a link was white
   text on a solid `#262626` block (`#3A3A3A` once the page went dark) — the
   "redacted" look, and the strongest thing on the page. Beth replaced it on
   2026-08-06 with "just fatter text". Consequences worth knowing before
   reinstating anything:
     · the bar was doing the work of separating adjacent links, which is why
       `.index a` used to carry margins — see the index rules, where they are now
       gone as well;
     · with no fill, `--accent-ink` (the ink chosen per accent so white would not
       sit on a light bar) has nothing left to colour and was deleted from both
       style.css and app.js.

   ⚠️ `display: inline-block` STAYS, and its reason has changed. It used to be
   what let the whole `<a>` take the mirror transform; the mirror moved to the
   per-letter `.ltr` spans, but those are themselves inline-blocks, and a line
   can break between two of them. Keeping the `<a>` atomic is what stops a link
   breaking MID-WORD at the end of a line. */
.text a {
    color: var(--link);           /* a step brighter than --ink; see :root */
    font-weight: bold;            /* the other half of the signal */
    text-decoration: none;
    display: inline-block;        /* keeps the link atomic — see above */
    /* colour ONLY — the mirror flip must stay instant, so `transform` must
       never appear here */
    transition: color 0.15s ease;
}
/* ---------- THE MIRROR IS PER LETTER, NOT PER LINK (2026-08-06) ----------
   `.ltr` is one span per character, written by `splitLetters()` in app.js. Each
   glyph flips in place, so the letters stay in reading order and only their
   shapes reverse.

   It was the whole `<a>` that flipped until 2026-08-06 (`transform` on the rule
   above, which is why that rule no longer has one). That reversed the letter
   ORDER as well, and Beth wanted to see "a lesser but more interesting effect".
   The word-flip version is kept whole in
   Content/Code/site-backup-2026-08-06-word-mirror/.

   ⚠️ `.sharp` still goes on the `<a>`, not on the letters — one class per link
   either way, so the click handler and the phone `pointer-events` rule are
   untouched by this. */
.text a .ltr {
    display: inline-block;
    transform: scaleX(-1);        /* mirrored — the default on load */
}
.text a.sharp .ltr {
    transform: scaleX(1);         /* normal (un-mirrored) after a tap toggles it */
}
/* the bar goes to the accent on hover. Guarded by (hover: hover) because
   touch latches :hover after a tap — a link left stuck orange would read as
   a bug, especially since the same tap flips the mirror. */
/* Hover recolours the TEXT now that there is no bar to fill. Guarded by
   `(hover: hover)` because touch latches :hover after a tap — a link left stuck
   in the accent would read as a bug, especially since the same tap cycles it.
   ⚠️ Some accents are dark and this ground is darker: see palette.md for which
   slots go quiet on hover. */
@media (hover: hover) {
    .text a:hover { color: var(--accent); }
}

/* ---------- THE LINK INDEX ----------
   What used to be the dropdown menu (removed 2026-08-06). Every entry in
   `NAV` is now ONE LINE of the page: the title in plain body text, its links
   following on the same line. `app.js` builds it; the markup is
   `<div class="text index">`.

   ⚠️ **`.index` carries `class="text"` in the HTML, and that is the design.**
   It IS body text — everything about how it looks (size, leading, the hairline
   stroke, the redaction bars, the mirror and its `.sharp` toggle, the accent
   hover, every phone override) comes from the `.text` rules above, so there is
   exactly ONE description on this site of what a link looks like and the index
   cannot drift from the bio. Beth asked for precisely that: same formatting,
   same reaction to the mirror.

   So the ONLY things below are layout. Resist adding anything that changes the
   look of a link here — put it in `.text a` and let both have it. */
.index {
    /* one blank line between the bio and the index. In `em`, so it is one line
       of THIS text at whatever size the breakpoint sets — no phone override. */
    margin-top: 1.25em;
}
/* ⚠️ THERE IS NO HORIZONTAL SPACING RULE HERE, AND THERE MUST NOT BE.
   The title is followed by a plain space and the links are separated by `", "`,
   both written into the markup by `buildIndex()` — the bio's own separators,
   which is what makes the two read as one piece of text.

   `.index-label { margin-right: 6px }` and `.index a { margin-right: 8px }`
   existed until 2026-08-06 and were REMOVED once the commas went in. A margin on
   the link pushes the comma away from the bar it belongs to, so the punctuation
   floats in the gap — the bio never does that. Beth asked for the title/first
   link gap to come in by a space at the same time; deleting the label margin is
   what did it. If two bars ever look too close, add a separator to the copy, not
   a margin here.

   ⚠️ `.index a { margin-bottom: 4px }` lived here too, and is GONE with the
   bar (2026-08-06). It existed because a wrapped row stacked solid blocks that
   met edge to edge — worst on `Writing`, whose four titles stack at phone width.
   Bold text has no edges to collide, so the extra space was just a gap with no
   reason, and normal leading handles it. */

/* ---------- PHONE LAYOUT ----------
   Only the type size and the gutters change now. The menu used to move to the
   bottom-left, open upward, grow 20% and become draggable; all of that went with
   the menu on 2026-08-06, and with it the bulk of this block. */
@media (max-width: 600px) {
    body {
        /* ≈17.6px. Kept as a % rather than px so it still scales with a reader's
           own font-size setting. */
        font-size: 110%;
    }
    .container {
        flex-direction: column;
        max-width: none;
        margin: 0;
        /* Same tight top as desktop. The SIDES are zero here and handed to each
           block's own padding instead — see `.text` below. */
        padding: 10px 0 40px;
    }
    .text {
        /* 1.225rem (19.6px) matched the old phone menu's own size and was kept
           when the menu went; scaled on 2026-08-06 for the face in use, the same
           compensation the desktop rule takes — MOVE THE TWO TOGETHER. Under
           Arial use 1.225, Times 1.2863; see the table beside `--font`.
           A rem rather than a hard px so it still scales with a reader's own
           font-size setting. Much larger than the desktop size on purpose: the
           reference's dense small setting is a wide-screen idea, and at phone
           width those lines are too short to read as a block. */
        font-size: 1.1238rem;
        /* no width cap on phones — the viewport is the measure */
        max-width: none;
        /* 16px rather than the desktop 10px: the type is 19.6px here, and a
           thumb-width of edge is the least a phone can hold without the text
           looking like it is falling off. Still well in from the old 24px. */
        padding: 0 16px;
    }
    /* ---------- A MIRRORED LINK IS DEAD ON PHONES ----------
       PHONES ONLY (Beth, 2026-08-06). While the page rests mirrored a link
       cannot be followed; the first tap flips everything readable, and only then
       does tapping a link go anywhere. On a phone the tap that flips the page is
       the same gesture as the tap that follows a link, so without this you set
       off for a link you cannot yet read and land on it by accident. Desktop
       keeps every link live in both states — there the mirror is a toy you look
       at, not something you have to get past.

       `pointer-events: none` and NOT a JS guard: the tap then passes straight
       through to the document listener that toggles `.sharp`, so the first tap
       still flips the page. Nothing has to know it was aimed at a link.

       ⚠️ It hangs off `.sharp` — the class the mirror already toggles — so the
       two can never disagree about which state the page is in. Keyboard access
       is untouched: `pointer-events` does not affect focus or Enter. */
    .text a:not(.sharp) {
        pointer-events: none;
    }
    .portrait-wrap {
        margin-top: 0;
        /* 2× the desktop size (2026-08-06). BOTH terms doubled from
           `min(140.25px, 23.375vw)`, which is what keeps the pair meaningful —
           they are still equal at exactly 600px, so the vw term is the one that
           binds at every phone width and the px cap only guards the top of the
           range. Rescale them together or that stops being true.
           ⚠️ This puts a DELIBERATE STEP at the breakpoint: the portrait used to
           glide through 600px unchanged, and now doubles there. */
        width: min(280.5px, 46.75vw);
    }
    /* ---------- FATTER ROWS IN THE INDEX, FOR THUMBS ----------
       The bio's near-solid 1.25 leading is right for reading and wrong for
       tapping: at 19.6px it makes a ~24px row, and adjacent bars end up within a
       thumb's width of each other. This opens each row to ~36px, close to the
       44px tap target the platforms ask for.

       ⚠️ IT MUST BE `margin`, NOT `line-height`. Leading was tried first
       (`line-height: 1.9`) and is WRONG: the links are `inline-block`, so a
       single-line one takes its HEIGHT from the line-height it inherits — the
       bars grew with the rows and stopped matching the bio's, which Beth spotted
       immediately. Margin moves the rows apart and leaves the bar alone.
       The gap between the WRAPPED lines of one row is a different problem, and
       `.index a { margin-bottom }` above is what handles it.

       ⚠️ Written `.text.index p` (0,2,1), NOT `.index p`. A bare `.index p` ties
       with `.text p` on specificity and would win only on source order, so it
       would break silently if these rules were ever moved above the bio's. */
    .text.index p {
        margin-bottom: 9px;
    }
    /* ⚠️ `.text.index a { margin-bottom: 6px }` was here to hold the WRAPPED
       lines of one row apart, back when each link was a solid bar and stacked
       bars met edge to edge. It went with the bar on 2026-08-06 — bold text has
       no edges to collide. The paragraph margin above is now the only vertical
       spacing in the index, and it was cut 25% (12→9) the same day when Beth
       found the rows too far apart. */
}
