/* =============================================================================
   THE EDITION SWITCH
   -----------------------------------------------------------------------------
   One stylesheet for the control that moves a reader between the two editions
   of a page, plus the ink tokens the paper editions of the standalone pages are
   built from.

   It exists because six single-file pages need the same switch — audio.html,
   chess.html, checkers.html, tictactoe.html, logo.html and dina/ — and six
   inline copies of it would have drifted apart by the second edit. The pages
   that belong to the main site (/, /investor/, …) use .mode-pill in site.css
   instead; this is the same control at the same 13px, for pages that do not
   load site.css.

   The switch is a segmented control rather than a link because the two editions
   are alternatives to each other, not destinations alongside each other.
   Exactly one segment carries .is-on, written into the markup rather than
   worked out at runtime: on any given file the answer is a constant. The lit
   segment is a <span>, not an <a> — pointing a link at the document you are
   already reading buys a round trip to arrive back where you started — and
   because a span with nothing but an aria-label is announced by nothing, its
   words are real text behind .ed-vh.

   tools/mkpaper.py generates every *-paper file from its dark source by
   swapping the two segments. Never hand-edit a generated file.
   ========================================================================== */

/* The ink the paper editions are set in. Taken from assets/css/site.css so the
   standalone pages land on the same white as / and sitemap.html — an
   almost-matching paper reads as a different property. */
:root {
    --pa-paper:      #ffffff;
    --pa-paper-2:    #faf9f7;
    --pa-ink:        #1E262B;
    --pa-ink-77:     rgba(30, 38, 43, .77);
    /* .64 is the alpha at which this ink measures 4.7:1 on the paper, so it is
       the floor for anything carrying words. Nothing quieter than this holds a
       sentence, however tempting the hierarchy. */
    --pa-ink-64:     rgba(30, 38, 43, .64);
    --pa-ink-45:     rgba(30, 38, 43, .45);   /* glyphs and rules only */
    --pa-line:       rgba(30, 38, 43, .14);
    --pa-line-soft:  rgba(30, 38, 43, .07);
    /* Three weights of one accent. The raw orange is the mark and stays on
       fills; the middle carries rules; only the deepest is legible as type on
       white, at 7.3:1 — the raw orange manages 2.6:1 and cannot hold a word. */
    --pa-accent-raw: #ff8800;
    --pa-accent:     #C2410C;
    --pa-accent-ink: #9A3412;
    --pa-stage:      #0d0d0f;   /* boards, canvases, anything light-emitting */
    /* The reading face. Declared here and not per page, because .mast below is
       shared by all three game pages and two of them never defined it — a
       masthead falling back to the UA serif on checkers and tic-tac-toe is
       exactly the kind of drift this file exists to stop. The @font-face at the
       foot of this file is what loads it. */
    --pa-text:       'DM Sans', system-ui, -apple-system, BlinkMacSystemFont,
                     'Segoe UI', Roboto, sans-serif;
}

.ed-pill {
    position: relative;
    display: inline-flex;
    align-items: center;
    padding: 3px;
    border-radius: 9999px;
    background: rgba(255, 255, 255, .05);
    border: 1px solid rgba(255, 255, 255, .16);
    line-height: 0;
    vertical-align: middle;
}
.ed-pill__seg {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 24px;
    border-radius: 9999px;
    color: rgba(255, 255, 255, .5);
    transition: color .18s cubic-bezier(.22,.61,.36,1),
                background-color .18s cubic-bezier(.22,.61,.36,1);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
}
.ed-pill__seg:hover  { color: rgba(255, 255, 255, .85); }
.ed-pill__seg:focus-visible { outline: 2px solid var(--pa-accent-raw); outline-offset: 2px; }
.ed-pill__seg.is-on  { background: var(--pa-accent-raw); color: #1a0d00; cursor: default; }
.ed-pill__seg.is-on:hover { color: #1a0d00; }
.ed-pill__i { display: block; }

/* Visually hidden, still read aloud. */
.ed-vh {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
    white-space: nowrap; border: 0;
}

html[data-edition="paper"] .ed-pill {
    background: rgba(30, 38, 43, .04);
    border-color: var(--pa-line);
}
html[data-edition="paper"] .ed-pill__seg        { color: var(--pa-ink-45); }
html[data-edition="paper"] .ed-pill__seg:hover  { color: var(--pa-ink); }
html[data-edition="paper"] .ed-pill__seg.is-on,
html[data-edition="paper"] .ed-pill__seg.is-on:hover { background: var(--pa-accent); color: #fff; }

@media (max-width: 640px) {
    .ed-pill__seg { width: 26px; height: 22px; }
}

/* -----------------------------------------------------------------------------
   THE BAR THE SWITCH SITS IN
   The three game pages had no link back to the site at all — you arrived at a
   chessboard and the only way out was the back button. One row, above the
   masthead: where you are, and the edition you are in.
   -------------------------------------------------------------------------- */
.ed-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    margin: 0 0 .9rem;
    font-family: 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
}
.ed-home {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    font-size: .72rem;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    text-decoration: none;
    color: rgba(255, 255, 255, .55);
    transition: color .18s cubic-bezier(.22,.61,.36,1);
}
.ed-home:hover { color: var(--pa-accent-raw); }
.ed-home::before { content: "\2190"; font-size: .9em; }
html[data-edition="paper"] .ed-home       { color: var(--pa-ink-64); }
html[data-edition="paper"] .ed-home:hover { color: var(--pa-accent-ink); }

/* Printing a paper edition should not also print the way out of it. */
@media print { .ed-bar { display: none; } }

/* The right-hand end of the bar. The edition pill was the only thing in it; a
   page-level switch that belongs beside the pill rather than in the game now
   sits here too, so the bar is "where you are" on the left and "how the page
   behaves" on the right. */
.ed-bar__end { display: inline-flex; align-items: center; gap: .5rem; }

/* -----------------------------------------------------------------------------
   THE ANNOUNCEMENT CHIP
   Chess's toast switch used to be a full-size button on a line of its own
   between the masthead and the board — a preference about the page, sitting in
   the middle of the game, at the same weight as NEW GAME. It is a chip in the
   bar now: small enough to read as chrome, still a real toggle, and still
   aria-pressed so the paper sheet and a screen reader both follow one attribute.

   Scoped through .ed-bar deliberately. chess.html styles bare `button` with a
   filled orange fill and a hover that scales it, and an unscoped `.ed-toast`
   would tie with `button:hover` on specificity and lose on source order.
   -------------------------------------------------------------------------- */
.ed-bar .ed-toast {
    padding: 3px 9px;
    font-family: 'Roboto Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: .56rem; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase;
    line-height: 1.6;
    background: transparent; color: rgba(255, 255, 255, .5);
    border: 1px solid rgba(255, 255, 255, .18); border-radius: 999px;
    cursor: pointer; box-shadow: none; transform: none;
    transition: color .18s cubic-bezier(.22,.61,.36,1), border-color .18s cubic-bezier(.22,.61,.36,1);
}
.ed-bar .ed-toast:hover {
    background: transparent; color: #fff;
    border-color: rgba(255, 255, 255, .42);
    box-shadow: none; transform: none;
}
/* On is green because it is a thing that is running rather than a thing that is
   chosen — the only other green on the page. */
.ed-bar .ed-toast[aria-pressed="true"] {
    background: #00cc66; color: #04240f; border-color: #00cc66;
}
html[data-edition="paper"] .ed-bar .ed-toast {
    color: var(--pa-ink-64); border-color: var(--pa-line);
}
html[data-edition="paper"] .ed-bar .ed-toast:hover {
    color: var(--pa-ink); border-color: var(--pa-ink-45);
}
html[data-edition="paper"] .ed-bar .ed-toast[aria-pressed="true"] {
    background: rgba(4, 120, 87, .09); color: #04624a; border-color: rgba(4, 120, 87, .38);
}

/* -----------------------------------------------------------------------------
   THE MASTHEAD
   The three game pages said LAB CHESS, LAB CHECKERS and TIC-TAC-LAB in Orbitron
   with a glow behind it — the last place on labs.llc still doing that, and three
   different names for one set of pages.

   The mark is the site's own logo.svg, so the lockup reads LABS CHESS and the
   word beside it is only ever the game. One file serves both editions: the badge
   is #1E262B with white letters, which sits on the sheet as ink and on the
   near-black the games run on as a raised plate — the same file indexdark.html
   uses on the same background. The alt text is what makes "LABS CHESS" the
   accessible name of the h1 rather than just its picture.

   Set in the site's own face, not the page's: DM Sans is already self-hosted at
   the foot of this file and already loaded here, so the masthead costs nothing
   new and stops being the one heading on labs.llc in a different typeface.
   -------------------------------------------------------------------------- */
.mast { text-align: center; margin: 0 0 .85rem; }
.mast h1 {
    display: inline-flex; align-items: center; justify-content: center;
    gap: .55rem; margin: 0;
    font-family: var(--pa-text);
    font-size: clamp(1.3rem, 4.4vw, 1.95rem);
    font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--pa-accent-raw); text-shadow: none;
}
/* Sized off the word beside it, so the two always sit on one baseline whatever
   the clamp above resolves to. */
.mast__mark { display: block; width: auto; height: 1.05em; }
.mast .subtitle {
    margin: .4rem 0 0;
    font-family: var(--pa-text);
    font-size: .8rem; letter-spacing: .01em; text-transform: none;
}
html[data-edition="paper"] .mast h1 { color: var(--pa-accent-ink); }

/* On a phone the chrome above the board is the board's competitor for the only
   scarce dimension there is. The bar and the masthead give up what they can:
   the board is what the page is for, and a title that costs it a rank is a
   title that is too big. */
@media (max-width: 560px) {
    .ed-bar { margin-bottom: .5rem; }
    .mast   { margin-bottom: .5rem; }
    .mast .subtitle { font-size: .72rem; margin-top: .25rem; }
}



/* =============================================================================
   THE GAME PAGES ON PAPER
   -----------------------------------------------------------------------------
   chess.html, checkers.html and tictactoe.html were written against one another
   and share a vocabulary — .container, .status, .timer-bar, .level-card,
   .nerdy-panel, #board, .square. That is why their paper edition is one block
   here rather than three copies in three files.

   This block used to open by arguing that THE BOARD STAYS AS IT IS — that a
   board with ten piece themes is the artefact on the page, and that inverting it
   would leave ten themes meaning nothing and a set of rings nobody could read.
   All three boards have since been inverted, and the argument turned out to be
   wrong in the same way each time: it treated a theme's COLOUR as the thing worth
   protecting, when what a board actually has to protect is the reader's ability
   to tell one side from the other. Keep every identity hue, give each piece a
   contour, and both survive — see the three board blocks below.

   What remains true is the split: everything a reader plays keeps the game's own
   palette, and everything a reader reads turns to ink.
   ========================================================================== */
html[data-edition="paper"] body {
    background: var(--pa-paper);
    color: var(--pa-ink);
}
html[data-edition="paper"] .container {
    background: var(--pa-paper);
    border-color: var(--pa-line);
    box-shadow: 0 1px 2px rgba(30, 38, 43, .04), 0 14px 40px -18px rgba(30, 38, 43, .22);
}
html[data-edition="paper"] h1 {
    color: var(--pa-accent-ink);
    text-shadow: none;   /* a glow is a light source; paper has none */
}
html[data-edition="paper"] .subtitle,
html[data-edition="paper"] .captured-title { color: var(--pa-ink-64); }

html[data-edition="paper"] .status,
html[data-edition="paper"] .timer-bar,
html[data-edition="paper"] .level-card,
html[data-edition="paper"] .nerdy-panel,
html[data-edition="paper"] .captured-box,
html[data-edition="paper"] .lethal {
    background: var(--pa-paper-2);
    border-color: var(--pa-line);
    color: var(--pa-ink);
    text-shadow: none;
    box-shadow: none;
}
html[data-edition="paper"] .status { border-color: rgba(194, 65, 12, .30); }
html[data-edition="paper"] .timer  { color: var(--pa-accent-ink); text-shadow: none; }
html[data-edition="paper"] .timer-label { color: var(--pa-ink-64); }

html[data-edition="paper"] .level-card:hover,
html[data-edition="paper"] .level-card.active,
html[data-edition="paper"] .level-card[aria-pressed="true"] {
    border-color: var(--pa-accent);
    background: rgba(194, 65, 12, .06);
    box-shadow: none;
}
/* tictactoe.html's cards are <button>s marked by a left edge rather than divs with
   a full border, so the edge is the one thing that needs naming here. Their type
   colours are NOT set here on purpose: this page's tokens are re-pointed further
   down, which inks the card's heading, its subtitle and its blunder figure
   together. Rules for those would have had to be unscoped to reach them, and an
   unscoped .level-card rule repaints chess and checkers too. */
html[data-edition="paper"] .level-card[aria-pressed="true"] { border-left-color: var(--pa-accent); }

/* -----------------------------------------------------------------------------
   BUTTONS
   Keeping the orange fill was the wrong call. On black one saturated button is
   the brightest thing on the panel and reads as emphasis; on white, eleven of
   them in a row — orange, orange, orange, crimson, crimson, green — read as a
   toolbar shouting all of its options at once, and nothing in the row is
   emphasised because everything is.

   So the row inverts: quiet by default, loud only where it means something.
   Every button is paper with a hairline and ink type. Colour is reserved for
   three things and nothing else — the primary action, a destructive one, and a
   toggle that is currently ON. That is the whole system.
   -------------------------------------------------------------------------- */
html[data-edition="paper"] button {
    background: var(--pa-paper);
    color: var(--pa-ink);
    border: 1px solid var(--pa-line);
    box-shadow: 0 1px 1px rgba(30, 38, 43, .04);
    font-weight: 600;
}
html[data-edition="paper"] button:hover {
    background: var(--pa-paper-2);
    border-color: rgba(30, 38, 43, .28);
    box-shadow: 0 1px 3px rgba(30, 38, 43, .10);
    transform: none;   /* the scale(1.05) was a screen flourish */
}
html[data-edition="paper"] button:active {
    background: #f0ede7;
    transform: none;
    box-shadow: inset 0 1px 2px rgba(30, 38, 43, .10);
}
html[data-edition="paper"] button:disabled { opacity: .4; }
html[data-edition="paper"] button:focus-visible { outline: 2px solid var(--pa-accent); outline-offset: 2px; }

/* NEW GAME is the one thing a reader is most likely to want, and the only
   filled button in the row. The two attribute selectors match chess.html and
   checkers.html, which still bind their buttons with inline onclick; `.primary`
   is for markup that binds listeners in script and so has no such attribute to
   match — tictactoe.html is the first of those. Matching an event handler to
   decide a colour was always a bit of a trick; the class is the honest version
   and the attribute selectors stay only until the other two catch up. */
html[data-edition="paper"] button[onclick*="newGame"],
html[data-edition="paper"] button[onclick*="startGame"],
html[data-edition="paper"] button.primary {
    background: var(--pa-accent);
    border-color: var(--pa-accent);
    color: #fff;
}
html[data-edition="paper"] button[onclick*="newGame"]:hover,
html[data-edition="paper"] button[onclick*="startGame"]:hover,
html[data-edition="paper"] button.primary:hover {
    background: #a8380a;
    border-color: #a8380a;
    color: #fff;
}
/* A toggle that is on, and a chosen opponent card. Both used to be a class the
   script added (.on / .active); both are aria-pressed now, which means the state
   is announced as well as painted, and the sheet can follow the same attribute
   rather than a parallel class. */
html[data-edition="paper"] button[aria-pressed="true"] {
    background: rgba(194, 65, 12, .09);
    border-color: var(--pa-accent);
    color: var(--pa-accent-ink);
}
html[data-edition="paper"] button.primary[aria-pressed] { color: #fff; }

/* Destructive, and the two thinking-limit overrides that share its class:
   crimson type on paper, filled only once the thing is actually on. */
html[data-edition="paper"] button.force {
    background: var(--pa-paper);
    color: #be123c;
    border-color: rgba(190, 18, 60, .40);
}
html[data-edition="paper"] button.force:hover {
    background: #fdf2f4;
    border-color: #be123c;
    color: #9f0f32;
}
html[data-edition="paper"] button.force.on {
    background: #be123c;
    border-color: #be123c;
    color: #fff;
}

/* A toggle that is on says so with a tint and a rule, not a fill — and the
   compact strip runs the full width of the panel, so a solid green bar across
   it was the loudest object on the page by a distance. */
html[data-edition="paper"] #compactBtn {
    /* A pill, not a banner. It was width:100% — a strip the full measure of the
       panel, for a preference. Sized to its own label and put on the same line
       the masthead ends on. */
    width: auto;
    display: inline-flex;
    align-items: center;
    padding: 5px 14px;
    border-radius: 9999px;
    margin: 0 0 12px;
    font-size: .68rem;
    letter-spacing: 1.2px;
    background: var(--pa-paper-2);
    color: var(--pa-ink-77);
    border-color: var(--pa-line);
    font-weight: 600;
}
html[data-edition="paper"] #compactBtn.on {
    background: rgba(4, 120, 87, .09);
    color: #04624a;
    border-color: rgba(4, 120, 87, .38);
    box-shadow: none;
}
html[data-edition="paper"] #compactBtn:hover {
    background: rgba(4, 120, 87, .16);
    box-shadow: none;
}

/* checkers already has a ghost variant; on paper the default IS the ghost, so
   the two only need to stop disagreeing about which is which. */
html[data-edition="paper"] button.ghost {
    background: var(--pa-paper);
    color: var(--pa-accent-ink);
    border-color: rgba(194, 65, 12, .40);
}
html[data-edition="paper"] button.ghost:hover { background: rgba(194, 65, 12, .07); }

/* The two buttons that carry their colour inline — RECOMMEND (green) and the
   piece-theme cycler (orange) — keep their meaning and lose their fill. */
html[data-edition="paper"] button[style*="#00cc66"] {
    background: var(--pa-paper) !important;
    color: #04624a !important;
    border-color: rgba(4, 120, 87, .40) !important;
}
html[data-edition="paper"] button[style*="#00cc66"]:hover { background: rgba(4, 120, 87, .09) !important; }
html[data-edition="paper"] button[style*="#ff8800"] {
    background: var(--pa-paper) !important;
    color: var(--pa-accent-ink) !important;
    border-color: rgba(194, 65, 12, .40) !important;
}
html[data-edition="paper"] button[style*="#ff8800"]:hover { background: rgba(194, 65, 12, .07) !important; }

/* The promotion picker shows four pieces at 46px; it is the board's vocabulary,
   so it takes the board's treatment rather than the toolbar's. */
html[data-edition="paper"] .promo-btn {
    background: var(--pa-paper);
    color: var(--pa-ink);
    border: 2px solid var(--pa-accent);
}
html[data-edition="paper"] .promo-btn:hover { background: rgba(194, 65, 12, .08); }
html[data-edition="paper"] select,
html[data-edition="paper"] #levelSelect {
    background: var(--pa-paper);
    color: var(--pa-ink);
    border-color: var(--pa-line);
}

/* The plate. A frame and a shadow, so the board sits on the sheet rather than
   reading as a hole cut in it. */
html[data-edition="paper"] #board {
    box-shadow: 0 0 0 1px var(--pa-line), 0 10px 30px -16px rgba(30, 38, 43, .55);
}
/* .square, .piece-*, the theme classes and the highlight rings are deliberately
   untouched — see the note above. */

html[data-edition="paper"] .coord         { color: var(--pa-ink-45); }
html[data-edition="paper"] .captured-list { color: var(--pa-ink-77); }
html[data-edition="paper"] .overlay,
html[data-edition="paper"] #winScreen {
    background: rgba(255, 255, 255, .93);
    color: var(--pa-ink);
}
html[data-edition="paper"] ::selection { background: rgba(194, 65, 12, .22); color: var(--pa-ink); }


/* =============================================================================
   THE LOGO PAGE ON PAPER  (logo.html — "Logo" in the register)
   -----------------------------------------------------------------------------
   Tailwind's slate ramp, inverted. The page is a specimen sheet — twenty sizes
   of the mark in twenty-four typefaces — and a specimen sheet on white is what
   a type foundry would have printed in the first place.

   THE PILLS THEMSELVES DO NOT CHANGE. Every one of them is the orange gradient
   mark being specified, and the whole purpose of the page is to show what it
   looks like and export it as a transparent PNG. Repainting the specimen would
   make the page a picture of something the brand is not, and the exported PNG
   would no longer match what was on screen.
   ========================================================================== */
html[data-edition="paper"] .bg-slate-950,
html[data-edition="paper"] .bg-slate-900       { background-color: var(--pa-paper) !important; }
html[data-edition="paper"] .bg-slate-950\/95   { background-color: rgba(250, 249, 247, .92) !important; }
html[data-edition="paper"] .bg-slate-800       { background-color: var(--pa-paper-2) !important; }
html[data-edition="paper"] .text-slate-200     { color: var(--pa-ink) !important; }
html[data-edition="paper"] .text-slate-400,
html[data-edition="paper"] .text-slate-500     { color: var(--pa-ink-64) !important; }
html[data-edition="paper"] .text-slate-900     { color: var(--pa-ink) !important; }
html[data-edition="paper"] .border-slate-800,
html[data-edition="paper"] .border-slate-700   { border-color: var(--pa-line) !important; }
html[data-edition="paper"] .border-slate-600   { border-color: rgba(30, 38, 43, .22) !important; }
/* amber and emerald at weights that hold on white; #fbbf24 measures 1.8:1 */
html[data-edition="paper"] .text-amber-400     { color: #a16207 !important; }
html[data-edition="paper"] .text-emerald-400,
html[data-edition="paper"] .text-emerald-400\/80 { color: #047857 !important; }
html[data-edition="paper"] .bg-emerald-400     { background-color: #047857 !important; }
html[data-edition="paper"] .text-orange-400\/80 { color: var(--pa-accent-ink) !important; }
html[data-edition="paper"] .bg-amber-500\/10   { background-color: rgba(161, 98, 7, .09) !important; }
html[data-edition="paper"] .border-amber-500\/30 { border-color: rgba(161, 98, 7, .30) !important; }
/* .bg-white is the transparent-PNG checkerboard behind an exported specimen —
   on paper it needs an edge, or the swatch and the sheet are the same white. */
html[data-edition="paper"] .bg-white           { box-shadow: 0 0 0 1px var(--pa-line); }
/* Two section eyebrows the first sweep missed — they are the only sky and
   purple on the page, so they were not in the slate ramp above. */
html[data-edition="paper"] .text-sky-400    { color: #0369a1 !important; }
html[data-edition="paper"] .text-purple-400 { color: #6d28d9 !important; }


/* =============================================================================
   THE BOARDS ON PAPER
   -----------------------------------------------------------------------------
   The boards were kept dark at first, on the argument that a board with fifteen
   piece themes is the artefact and inverting it would leave fifteen themes that
   all mean nothing. That was wrong on one point: a chessboard is the single most
   printed object in this whole site, and every chess book in existence proves it
   reads better in ink than in neon.

   What makes it work is that the glyph set already carries the distinction.
   ♖♘♗♕♔♙ are the OUTLINE characters and ♜♞♝♛♚♟ are the FILLED ones, which is
   exactly the convention a printed diagram uses. So a white piece is filled white
   and given an inked outline — it reads as white on a near-white square, the way
   it does on the dark board — and a black piece is the solid one. The theme still
   shows: it tints the outline on one side and the fill on the other.

   The fifteen chess piece themes at the end of this block are generated, not
   hand-picked, by the same lightness mapping assets/js/paper-ink.js applies at
   runtime — pieces get a darker band than canvas strokes, because a 40px solid
   glyph and a 1px line do not want the same ink.
   ========================================================================== */

/* ---- the board ---------------------------------------------------------- */
html[data-edition="paper"] #board {
    border-color: var(--pa-ink) !important;
    background: var(--pa-ink);
    box-shadow: 0 1px 2px rgba(30, 38, 43, .10), 0 14px 34px -20px rgba(30, 38, 43, .45);
}
/* Chess and checkers shared these two rules and are now written out separately —
   they hold the same values, but the two boards no longer have to move together,
   and tictactoe's own pair lives further down. #ffffff over #e6e2da was 1.29:1,
   a tint of white rather than a second colour; #c8bca4 is 1.88:1, which is where
   a printed diagram actually sits. In checkers every man stands on a .dark square
   and never on a .light one, so this is the value its two fills are measured
   against. */
html[data-edition="paper"] body[data-game="chess"] .square.light { background-color: #ffffff; }
html[data-edition="paper"] body[data-game="chess"] .square.dark  { background-color: #D9DDE2; }
html[data-edition="paper"] body[data-game="checkers"] .square.light { background-color: #ffffff; }
html[data-edition="paper"] body[data-game="checkers"] .square.dark  { background-color: #D9DDE2; }
html[data-edition="paper"] .square:hover { filter: brightness(.965); }

/* The move marks. These four selectors used to be unscoped, which was safe only
   as long as both boards drew a mark the same way — and chess no longer does, so
   they are split. Scoping them is not tidying: chess.html now puts its marks on
   ::before and ::after, checkers.html still uses an inset ring, and an unscoped
   ::after rule would have left every checkers ring falling back to the neon it
   carries for the dark edition, on a white board.

   On black these were saturated neon at full brightness; the mark has to stay
   the loudest thing on the square, which on paper means darker, not brighter.
   Only the two that still share box-shadow need ordering against each other,
   and neither needs !important any more. */
html[data-edition="paper"] body[data-game="chess"] .square.highlight::after  { background: rgba(30, 38, 43, .5); }
html[data-edition="paper"] body[data-game="chess"] .square.capture::after    { border-color: #be123c; }
html[data-edition="paper"] body[data-game="chess"] .square.last-move::before { background: rgba(194, 65, 12, .20); }
html[data-edition="paper"] body[data-game="chess"] .square.selected  { box-shadow: inset 0 0 0 4px #9A3412; }
html[data-edition="paper"] body[data-game="chess"] .square.recommend { box-shadow: inset 0 0 0 4px #047857; }

/* Checkers, now split the same four ways. Its `.selected` used to lose outright:
   the source carried `inset ... #00ffcc !important`, and an !important
   declaration beats any specificity, so the paper ring here never applied and a
   selected man was outlined in cyan on a white board. The source has dropped
   that !important, so this rule is the first version of it that actually paints.
   There is no .recommend rule any more — checkers never adds that class, and the
   one it inherited from the shared block was only ever chess's. */
html[data-edition="paper"] body[data-game="checkers"] .square.highlight::after { background: rgba(30, 38, 43, .5); }
html[data-edition="paper"] body[data-game="checkers"] .square.capture::after   { border-color: #be123c; }
html[data-edition="paper"] body[data-game="checkers"] .square.lastmove::before { background: rgba(4, 116, 96, .16); }
html[data-edition="paper"] body[data-game="checkers"] .square.selected { box-shadow: inset 0 0 0 4px #04756a; }
html[data-edition="paper"] .square.playable:hover { box-shadow: inset 0 0 0 4px rgba(194, 65, 12, .55); filter: none; }
html[data-edition="paper"] .square:focus-visible { outline-color: var(--pa-accent); }
html[data-edition="paper"] .coord { color: rgba(30, 38, 43, .42); }

/* ---- chess pieces -------------------------------------------------------- */
/* The note that used to sit here said an outline was redundant because ♖♘♗♕♔♙
   ARE the hollow characters, and that was half right: they are, and drawn in ink
   they do read as white pieces. What it missed is that hollow means the SQUARE
   shows through, so on the dark square a white piece had a beige body — and once
   that square is a real stock at #c8bca4, a beige body is not a white piece.

   The source now draws every piece as two layers of one font, so the values here
   are a fill and a contour per side. The white piece is filled paper-white and
   contoured in the theme's ink; the black piece is the reverse. Nothing is
   stroked — -webkit-text-stroke was the wrong tool, for exactly the reason the
   old note gives — the contour is a second glyph.

   The values below are the old lightness mapping re-derived against #c8bca4
   instead of #e6e2da, which is why several of them are a step darker: whichever
   layer carries a side has to clear 4:1 on the dark square, and the dark square
   moved. Theme 9 is the one real repair — it had BOTH sides at #1E262B, so in
   the paper edition the two players were the same colour. */
html[data-edition="paper"] body[data-game="chess"] {
    --pw-glow: none;
    --pb-glow: none;
}
html[data-edition="paper"] body[data-game="chess"].theme-0 { --pw-fill:#ffffff; --pw-line:#1E262B; --pb-fill:#963312; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-1 { --pw-fill:#ffffff; --pw-line:#675200; --pb-fill:#6f4f00; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-2 { --pw-fill:#ffffff; --pw-line:#005e5e; --pb-fill:#940094; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-3 { --pw-fill:#ffffff; --pw-line:#006140; --pb-fill:#0c620c; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-4 { --pw-fill:#ffffff; --pw-line:#4b007b; --pb-fill:#540b90; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-5 { --pw-fill:#ffffff; --pw-line:#1E262B; --pb-fill:#625300; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-6 { --pw-fill:#ffffff; --pw-line:#1E262B; --pb-fill:#9d0000; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-7 { --pw-fill:#ffffff; --pw-line:#1E262B; --pb-fill:#09228a; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-8 { --pw-fill:#ffffff; --pw-line:#1E262B; --pb-fill:#016301; --pb-line:#fdfbf7; }
html[data-edition="paper"] body[data-game="chess"].theme-9 { --pw-fill:#ffffff; --pw-line:#1E262B; --pb-fill:#1E262B; --pb-line:#fdfbf7; }

/* ---- checkers ------------------------------------------------------------ */
/* The note that used to sit here said both sides keep their hue and only their
   weight changes, because the UI names them by colour — "YOU (Red)" against the
   orange opponent. The naming argument still holds. What it got wrong is that
   weight was never enough: #be123c against #9A3412 measures 1.16:1, so on the
   paper board the two players were the same colour in greyscale. Not a niche
   theme — the default and only board.

   So paper takes the printed-draughts convention: one side is a SOLID man, the
   other a HOLLOW one. Red is the hollow side — filled paper and rimmed in its own
   red, which is exactly what chess.html does with a white piece, so a man reads
   the same way on both boards. That takes the two sides from 1.16:1 to 7.82:1,
   and both hues survive: the rim carries red, the fill carries rust.

   Red's rim is the only thing holding that man, so it is a step darker than the
   old fill — 5.1:1 on the square it stands on. Rust goes to #8f3010 for 4.3:1.
   The white text-shadow both sides used to wear is gone: it was there to lift a
   solid disc off a near-white square, and a rim does that job properly. */
html[data-edition="paper"] body[data-game="checkers"] {
    --pc-red-fill:   #fdfbf7;  --pc-red-line:   #8c0d23;
    --pc-amber-fill: #8f3010;  --pc-amber-line: #fdfbf7;
    --pc-red-glow:   none;
    --pc-amber-glow: none;
}
/* The crown is an emoji, and an emoji is a colour glyph — `color` cannot touch
   it, which is why the rule this replaces (color: #ffffff) did nothing at all and
   the paper board carried a full-colour 👑 on an ink diagram. The dark edition
   keeps its crown; paper inks a star instead, in whatever the man's rim is, so it
   lands dark on the hollow man and light on the solid one. */
html[data-edition="paper"] body[data-game="checkers"] .king::after {
    content: "★";
    color: var(--pc-line);
    text-shadow: none;
    -webkit-text-stroke: 0;
}

/* ---- tic-tac-toe --------------------------------------------------------- */
/* The note that used to sit here explained that this board's grid, its O and its
   accent were one CSS variable written inline onto <html> by applyTheme(), where
   no rule can reach them, so the paper build had to convert them at runtime
   through paperInk(). That whole problem is gone: the nine visual themes are
   gone, tictactoe.html sets no colour from script at all, and four tokens now
   describe the board in both editions.

   It also had a `.square.light` / `.square.dark` pair at #ffffff over #f7f5f1 —
   1.03:1, an invisible checkerboard on a game that should never have had one.
   A tic-tac-toe board is three rules and nine cells. The rule is the same stock
   as the dark square on the other two boards, which is what ties the three
   together on the sheet. */
/* tictactoe.html is the first of the three built entirely on its own tokens, and
   that changes what its paper edition has to be. The other two need a rule per
   thing — a rule for the panel, a rule for the strong tags inside the panel, a
   rule for every colour that was written inline — because their colours are
   spelled out at the point of use. This page spells them once, so re-pointing the
   tokens inks the whole of it: heading, eyebrow, panels, buttons, the ladder, the
   board. One block instead of a dozen overrides, and nothing can be missed
   because nothing is named twice.

   The accent is the reason this matters most. --accent is the raw #ff8800, which
   measures 2.6:1 on white and cannot legibly hold a word — so every rule reading
   it for type had to be caught individually before. Re-pointed to --pa-accent-ink
   it is 7.3:1 everywhere at once. */
html[data-edition="paper"] body[data-game="tictactoe"] {
    --accent:       var(--pa-accent-ink);
    --accent-quiet: rgba(194, 65, 12, .09);
    --ink:          var(--pa-ink);
    --ink-70:       var(--pa-ink-77);
    --ink-45:       var(--pa-ink-64);
    --page:         var(--pa-paper);
    --panel:        var(--pa-paper);
    --panel-2:      var(--pa-paper-2);
    --line:         var(--pa-line);
    --danger:       #be123c;

    --tt-cell:      #ffffff;
    --tt-rule:      #c8bca4;
    --tt-x:         #1E262B;
    --tt-o:         #9A3412;
}
/* #board carries the rule colour as its own background, so it has to opt out of
   the shared board rule further up — that one paints an ink frame for chess and
   checkers, whose boards have a 10px border to colour. This one has none. */
html[data-edition="paper"] body[data-game="tictactoe"] #board {
    background: var(--tt-rule) !important;
    border: 0 !important;
    box-shadow: 0 1px 2px rgba(30, 38, 43, .10), 0 14px 34px -20px rgba(30, 38, 43, .45);
}
/* The hover state is a value nudge in the dark edition, so on paper it has to go
   the other way — down toward the ink, not up toward the light. */
html[data-edition="paper"] body[data-game="tictactoe"] .square { background: var(--tt-cell); }
html[data-edition="paper"] body[data-game="tictactoe"] .square.playable:hover {
    background: #f2efe7;
    box-shadow: none;
    filter: none;
}
html[data-edition="paper"] body[data-game="tictactoe"] .square.win {
    background: color-mix(in srgb, currentColor 12%, #ffffff);
}

/* ---- the chess piece themes ---------------------------------------------- */
/* Twenty one-property rules used to live here, mapping each theme's two
   colours through a lightness function. They are gone: a theme is four
   values now, not two, and they are set as variables up in the pieces block
   above. These rules also out-specified anything the source could say, which
   is why they had to go rather than be edited — a fill and a contour cannot
   be expressed as one `color`. */


/* ---- the last of the type on the game pages ------------------------------ */
/* Three source colours accounted for every remaining contrast failure on the
   three boards: the raw accent used as type (2.6:1 on paper) and two greys
   that were quiet on black and invisible on white. Collected from the three
   stylesheets rather than typed out, so this cannot drift from what it
   mirrors. */
/* chess: 1 rule(s) */
html[data-edition="paper"] body[data-game="chess"] { color: var(--pa-ink); text-shadow: none; }
/* chess: 2 rule(s) */
html[data-edition="paper"] body[data-game="chess"] .timer,
html[data-edition="paper"] body[data-game="chess"] h1 { color: var(--pa-accent-ink); text-shadow: none; }
/* chess: 2 rule(s) */
html[data-edition="paper"] body[data-game="chess"] .coord,
html[data-edition="paper"] body[data-game="chess"] .subtitle { color: var(--pa-ink-64); text-shadow: none; }
/* chess: 1 rule(s) */
html[data-edition="paper"] body[data-game="chess"] .nerdy-panel { color: var(--pa-ink-77); text-shadow: none; }
/* checkers: 2 rule(s) */
html[data-edition="paper"] body[data-game="checkers"] .level-card,
html[data-edition="paper"] body[data-game="checkers"] { color: var(--pa-ink); text-shadow: none; }
/* checkers: 9 rule(s) */
html[data-edition="paper"] body[data-game="checkers"] .level-card strong,
html[data-edition="paper"] body[data-game="checkers"] .nerdy-panel strong,
html[data-edition="paper"] body[data-game="checkers"] .timer,
html[data-edition="paper"] body[data-game="checkers"] .timer-bar .side.active .timer-label,
html[data-edition="paper"] body[data-game="checkers"] .win-stats strong,
html[data-edition="paper"] body[data-game="checkers"] .win-title,
html[data-edition="paper"] body[data-game="checkers"] /* ---- Sidebar ---- */ .sidebar h2,
html[data-edition="paper"] body[data-game="checkers"] button.ghost,
html[data-edition="paper"] body[data-game="checkers"] h1 { color: var(--pa-accent-ink); text-shadow: none; }
/* checkers: 9 rule(s) */
html[data-edition="paper"] body[data-game="checkers"] .captured-col h4,
html[data-edition="paper"] body[data-game="checkers"] .captured-title,
html[data-edition="paper"] body[data-game="checkers"] .level-card .meta,
html[data-edition="paper"] body[data-game="checkers"] .level-card small,
html[data-edition="paper"] body[data-game="checkers"] .subtitle,
html[data-edition="paper"] body[data-game="checkers"] .timer-label,
html[data-edition="paper"] body[data-game="checkers"] .vs,
html[data-edition="paper"] body[data-game="checkers"] .win-stats .row span:first-child,
html[data-edition="paper"] body[data-game="checkers"] .win-subtitle { color: var(--pa-ink-64); text-shadow: none; }
/* checkers: 1 rule(s) */
html[data-edition="paper"] body[data-game="checkers"] .win-stats { color: var(--pa-ink-77); text-shadow: none; }
/* tictactoe: 1 rule(s) */
html[data-edition="paper"] body[data-game="tictactoe"] { color: var(--pa-ink); text-shadow: none; }
/* tictactoe: 2 rule(s) */
html[data-edition="paper"] body[data-game="tictactoe"] .timer,
html[data-edition="paper"] body[data-game="tictactoe"] h1 { color: var(--pa-accent-ink); text-shadow: none; }
/* tictactoe: 1 rule(s) */
html[data-edition="paper"] body[data-game="tictactoe"] .subtitle { color: var(--pa-ink-64); text-shadow: none; }
/* tictactoe: 1 rule(s) */
html[data-edition="paper"] body[data-game="tictactoe"] .nerdy-panel { color: var(--pa-ink-77); text-shadow: none; }


/* ---- inline style="color:…" on the game pages ---------------------------- */
html[data-edition="paper"] body[data-game="chess"] [style*="color:#666"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #666"] { color: var(--pa-ink-64) !important; text-shadow: none !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#888"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #888"] { color: var(--pa-ink-64) !important; text-shadow: none !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#aaa"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #aaa"] { color: var(--pa-ink-64) !important; text-shadow: none !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:var(--accent)"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: var(--accent)"] { color: var(--pa-accent-ink) !important; text-shadow: none !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ff8800"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ff8800"] { color: var(--pa-accent-ink) !important; text-shadow: none !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ccc"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ccc"] { color: var(--pa-ink-77) !important; text-shadow: none !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ffd700"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ffd700"] { color: #8a6d00 !important; text-shadow: none !important; }

/* --red and --orange are checkers' two danger/warning tokens, and they are still
   remapped rather than the elements: button.force, .win-title.loss and the
   capture ring all read them. They used to carry the captured-piece tallies too,
   which the script wrote as style="color:var(--red|--orange)" — that is gone,
   because a man is a fill plus a rim now and one colour cannot describe it. The
   tallies wear .piece-red / .piece-black and are handled with the board. */
html[data-edition="paper"] body[data-game="checkers"] {
    --red:    #be123c;
    --orange: #9A3412;
}


/* ---- inline style="background:#111" and friends -------------------------- */
/* The captured-pieces panel, the opponent blurb and the engine-error box all
   carry their dark ground as an inline style, which no stylesheet rule can
   reach. Their type had already turned to ink, so on paper they were ink on
   near-black — 1.1:1, the worst reading on the page. Overridden by the same
   attribute-match used for the inline colours above. */
html[data-edition="paper"] body[data-game="chess"] [style*="background:#111"],
html[data-edition="paper"] body[data-game="chess"] [style*="background: #111"] { background: var(--pa-paper-2) !important; }


/* ---- every remaining inline colour that fails on white ------------------- */
/* Swept from the three files by measuring each inline colour against the
   paper rather than by listing the ones already known about — #ddd and
   #ffaa00 were both still there after the first pass. */
html[data-edition="paper"] body[data-game="chess"] [style*="color:#888"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #888"] { color: var(--pa-ink-64) !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#aaa"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #aaa"] { color: var(--pa-ink-64) !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ff8800"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ff8800"] { color: #9e5400 !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ddd"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ddd"] { color: var(--pa-ink-64) !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ccc"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ccc"] { color: var(--pa-ink-64) !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ffd700"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ffd700"] { color: #9e8500 !important; }
html[data-edition="paper"] body[data-game="chess"] [style*="color:#ffaa00"],
html[data-edition="paper"] body[data-game="chess"] [style*="color: #ffaa00"] { color: #8a5c00 !important; }

/* The rank and file letters printed in the corner of every square measured
   4.25:1 at the .64 ink — close, and still under the floor. They are 8px type
   on a near-white square, so they take the heavier ink. */
html[data-edition="paper"] body[data-game="chess"] .coord { color: var(--pa-ink-77); }


/* =============================================================================
   DM SANS ON THE PAPER EDITIONS
   -----------------------------------------------------------------------------
   The rest of the paper site is set in DM Sans; the standalone pages were left
   on system-ui, so a track list read in a different typeface from every other
   list on labs.llc. Declared here rather than in site.css because these pages
   do not load site.css — same two files, same unicode-ranges.

   Dark keeps system-ui. It is a console and the system stack is the right voice
   for one; this is about the pages that are pretending to be printed.
   ========================================================================== */
@font-face {
    font-family: 'DM Sans';
    font-style: normal;
    font-weight: 200 700;
    font-display: swap;
    src: url('../fonts/dm-sans-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'DM Sans';
    font-style: normal;
    font-weight: 200 700;
    font-display: swap;
    src: url('../fonts/dm-sans-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* The file names, and the panel prose around them. Not the numbers: the row
   index, the durations, the tempo and pitch read-outs and the status line are
   tabular and stay in the mono face, which is what keeps a column of times
   lining up. */
html[data-edition="paper"] .track-item .font-medium,
html[data-edition="paper"] #track-title,
html[data-edition="paper"] #track-artist,
html[data-edition="paper"] #track-album,
html[data-edition="paper"] #empty-state,
html[data-edition="paper"] .drop-zone {
    font-family: 'DM Sans', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    letter-spacing: -0.005em;
}
html[data-edition="paper"] .track-item .font-medium { font-weight: 500; }

/* -----------------------------------------------------------------------------
   SLIDERS
   The filled track was taking --pa-accent, #C2410C, which is the rule colour —
   correct for a hairline and far too red for a 400px bar. Sliders are UI
   components rather than type, so the floor is 3:1 rather than 4.5:1, and at
   that floor the accent can stay the orange it is called: #E2620E measures
   3.3:1 on the paper and reads warm instead of alarming.
   -------------------------------------------------------------------------- */
:root { --pa-slider: #E2620E; }
html[data-edition="paper"] input[type="range"],
html[data-edition="paper"] .slider,
html[data-edition="paper"] .progress-slider,
html[data-edition="paper"] .accent-\[\#ff6b00\] { accent-color: var(--pa-slider) !important; }
html[data-edition="paper"] .slider::-webkit-slider-thumb { background-color: var(--pa-slider); }
html[data-edition="paper"] .slider::-moz-range-thumb    { background-color: var(--pa-slider); }
/* The checkboxes down the track list take it too — they are the same control. */
html[data-edition="paper"] .track-checkbox { accent-color: var(--pa-slider) !important; }

/* The three source segments, as a control rather than three buttons. */
html[data-edition="paper"] .src-seg { color: var(--pa-ink-77); }
html[data-edition="paper"] .src-seg:hover { color: var(--pa-ink); }


/* The status line and the captured-pieces panel were outlined in the accent at
   full strength, which on paper is a red rule around a quiet box. A rule that
   is only there to say "this is a box" should be the hairline everything else
   on the sheet uses. */
html[data-edition="paper"] .status { border-color: var(--pa-line); }
html[data-edition="paper"] .captured-panel,
html[data-edition="paper"] [style*="border:1px solid var(--accent)"] { border-color: var(--pa-line) !important; }
html[data-edition="paper"] .minimal-border { border-color: var(--pa-line); }
