
@font-face{font-family:'Geologica';font-style:normal;font-weight:200 600;font-display:swap;src:url(Geologica-200-600.b2505d330272.woff2) format('woff2')}
@font-face{font-family:'LTL Cyrillic';font-style:normal;font-weight:200 600;font-display:swap;unicode-range:U+0300-0301,U+0400-052F,U+1C80-1C8F,U+2DE0-2DFF,U+A640-A69F;src:url(Geologica-200-600.b2505d330272.woff2) format('woff2')}
@font-face{font-family:'LTL Cyrillic';font-style:italic;font-weight:200 600;font-display:swap;unicode-range:U+0300-0301,U+0400-052F,U+1C80-1C8F,U+2DE0-2DFF,U+A640-A69F;src:url(Geologica-200-600.b2505d330272.woff2) format('woff2')}
@font-face{font-family:'Oswald';font-style:normal;font-weight:600;font-display:swap;unicode-range:U+0462-0463;src:url(Oswald-600.185881ef1bc7.woff2) format('woff2')}
@font-face{font-family:'Sofia Sans Extra Condensed';font-style:normal;font-weight:400 900;font-display:swap;src:url(SofiaSansExtraCondensed-400-900.eea4bcd61439.woff2) format('woff2')}
@font-face{font-family:'Spectral';font-style:normal;font-weight:400;font-display:swap;src:url(Spectral-400.5d40805319f7.woff2) format('woff2')}
@font-face{font-family:'Spectral';font-style:italic;font-weight:400;font-display:swap;src:url(Spectral-400i.e53661ab2ef7.woff2) format('woff2')}

/* ============ motion tokens — every animation inherits these ============ */
:root {
  --motion: 1;                                   /* global scalar: 1 full · .5 calm · .12 reduce */
  --d-xs: calc(100ms * var(--motion));
  --d-s:  calc(150ms * var(--motion));
  --d-m:  calc(250ms * var(--motion));
  --d-l:  calc(400ms * var(--motion));
  --d-xl: calc(700ms * var(--motion));
  --e-std:   cubic-bezier(.2, 0, 0, 1);          /* standard */
  --e-enter: cubic-bezier(.05, .7, .1, 1);       /* arriving: decelerate, must be read */
  --e-exit:  cubic-bezier(.3, 0, .8, .15);       /* leaving: accelerate, don't wait on it */
  --e-snap:  cubic-bezier(.2, 0, 0, 1.4);        /* small overshoot for marks/blooms */
  /* THE FIVE CURVES OF A LIVING THING (research/brand-3d/characters.md §3).
     Named once, used everywhere a body — a figure, a flame, a pressed
     button — moves. --weight is the only overshoot besides the wax seal,
     and it is rationed to one per moment. */
  --settle:     cubic-bezier(.16, 1, .3, 1);     /* arrival, no overshoot — the default */
  --gather:     cubic-bezier(.55, 0, 1, .45);    /* anticipation, accelerating away */
  --snap-to:    cubic-bezier(.2, .9, .25, 1);    /* saccades, corrections */
  --weight:     cubic-bezier(.34, 1.2, .64, 1);  /* ~20% overshoot; ONE per moment */
  --breath-in:  cubic-bezier(.4, 0, .4, 1);
  --breath-out: cubic-bezier(.3, 0, .6, 1);
}
@media (prefers-reduced-motion: reduce) { :root { --motion: .12; } }
/* ============ Language through Literature — the palette law in force ========
   (rewritten 2026-08-22, cascade concordat clause e, to describe the law the
   values below actually keep — the old text still recited «warm paper + sepia
   + one vermilion ink», three palettes ago.)
   The ground is a COOL near-white room (#f7f9ff family) with ONE warm surface
   in the product: the sheet the prose sits on (#fffdf5). Ink is carbon.
   Structure and chrome are pine/lazur navy (#244177 — the playbill, the game
   rail); they never touch prose. The working pigments, each a material with
   one job: SIGNAL #f6c445, the reward yellow — the next action and earned
   everyday moments; MALACHITE, growth; LAZUR, knowledge held; VERMILION
   (--verm/--wax), the brand's red — change, wax, errata, ≤2 accents a screen,
   never judgement; GOLD LEAF, a dull foil DISTINCT from signal by law, worn
   at seal / ceremony / finished-spine beats ONLY. Semantics are servants,
   displaced from the brand hue. The gold/signal split and the ceremony-only
   grep gate are enforced by tests/cascade.test.js.
   ============================================================================ */
:root {
  /* ==========================================================================
     НАБОР — THE COMPOSING ROOM.  Palette: КЛИН, the wedge.

     Not a study with the dead. A 1923 composing bench where the Russian
     language is a machine being assembled part by part — and the one warm
     orthogonal sheet in the middle of that hard red room is the thing it is
     all being built for.

     THE SPLIT IS THE SYSTEM, not a compromise: the shell is 1923, the page is
     1842. Constructivism was engineered to move crowds; figure-and-ground is
     what lets it serve one adult reading alone at one in the morning.

     Chosen over three other complete identities. The distinctiveness judge
     found that the other three had independently converged on the same
     picture — dark plane, one mint-teal glow, warm sheet, bloom — which is
     precisely the register already rejected here as looking AI-made.
     Convergence was the evidence.

     Every pairing below is APCA-measured. Thresholds: Lc 90 body preferred,
     75 body minimum, 60 non-body, 45 headline, 15 divider floor.
     ========================================================================== */

  /* --- THE ROOM. A mid-tone hue-carrying neutral — the decision that keeps
     the product out of the register three of the four candidate identities
     converged on by themselves (saturated dark plane, one glowing accent),
     which is the register already rejected here for looking machine-made.
     A neutral that carries a hue makes the red a RELATIONSHIP instead of
     the only colour in the room.
     THE HUE MOVED ONCE: the original oxidised green-grey (#adb4ab) sat COOL
     around the warm sheet — a cold room wrapped around lamplit paper, and
     the whole product read clinical for it. The neutral now carries the
     same patina hue warmed toward olive-stone: it still holds the red at
     arm's length, and it agrees with the paper instead of arguing with it.
     Luminance is held, so every Lc figure below survives the shift.
     LAW: room and case carry display type only. --- */
  /* БИБЛИОТЕКА. The same luminance ladder, rehung in the reading room's own
     materials: the green baize of the desk, library cloth, paper, iron-gall
     ink. The oak-and-tea browns are retired entirely — the house colours are
     now paper, pine and scarlet, the way the best-set rooms are two dominant
     colours and one accent. Hue moves, the Lc arithmetic survives. */
  /* THE AIR IS COOL AGAIN — THE OWNER'S CALL, 2026-08-25, AND IT OVERRULES
     THE READING OF THE PALETTE LAW BELOW. Earlier the same day the ground was
     warmed to #f4f1e8 (b* +4.7) on the argument that «warm paper neutrals»
     admits no exemption for the daylight room. Tom looked at the result and
     said plainly that he preferred the cool near-white and wanted it back
     everywhere. That is the whole reason, it is a sufficient one, and it is
     recorded here so nobody re-warms this token from first principles a third
     time without asking him.

     What the warm reading got right and what it missed: --galley (b* +12.8)
     and --sheet (b* +4.0) are indeed warm, so the air is now cooler than the
     paper standing on it. That is a deliberate difference, not an oversight —
     it is what makes a sheet read as a sheet laid ON something.

     Two facts that make the revert the safer value anyway:
       · The public Django pages never took the warm value at all
         (server/pages/templates/pages/base.html declares --room: #f7f9ff).
         While this token was warm, the reader and the marketing site were two
         different colours. They agree again.
       · 95-coherence.css's light-room contrast figures were measured on
         2026-08-24 against exactly this ground, so restoring it restores the
         conditions those numbers were taken under.

     The rest of the law is untouched: this is not a pure-white theme, gold is
     still sacred, and the sepia room stays warm — it is a lamp. */
  --room:      #f7f9ff;   /* near-white room: colour belongs to objects, not the air */
  /* THE RECESSED BOARD FOLLOWS THE AIR. `:root[data-lights="down"]` derives
     the whole dimmed ground from --case (30-choreo §the step), so this is the
     token the reader actually looks at most, and it has to move with --room or
     the lights-down room drifts away from the lights-up one. */
  --case:      #e8eefc;   /* recessed game board */
  --galley:    #fffef9;   /* clean proof sheet for interface text */
  --rule:      #d2d9e8;   /* paper rules */
  --rule-room: #8798b8;   /* structural rules on the room */
  --steel:     #46536f;   /* metadata ink */
  /* PINE: the second dominant — but cut to PLAYBILL strength, not archive
     gloom. The emerald of a theatre bill, the baize of the card table: bright
     enough to feel like a game, dark enough to carry cream type. Structure
     and chrome, never prose. */
  --pine:      #244177;
  --pine-deep: #152747;
  --on-pine:   #fffdf5;

  /* --- THE SHEET. Warm, and the only warm surface in the product.
     Rello & Bigham (ACM ASSETS 2017, N=341) measured warm grounds reading
     faster than cool, with a larger effect for dyslexic readers. Brightest
     surface in the mode: nothing in the room outshines the words. Y 2.10× room
     — inside ISO 9241-303's 1:10 task-to-surround band. --- */
  --sheet:  #fffdf5;
  --carbon: #17213a;

  /* --- SIGNAL, THE REWARD YELLOW. (Comment rewritten 2026-08-22: the old
     text here still described сурик red lead as "the only saturated hue in
     the product" while the value under it had been yellow for a full skin —
     the law's text must describe the law in force.) Signal is the everyday
     call to action: the next thing to press and the small earned moments —
     the same #f6c445 in every room, which is why --on-signal never flips.
     It is NOT ceremony: gold leaf (below) is a separate, duller foil, so a
     sealed evening can out-dress a button. Red lives on as --verm/--wax
     (brand, wax, errata) and the arcade's --wrong; saturation is otherwise
     rationed to the pigment case below. --- */
  --signal:     #f6c445;  /* reward yellow: the next action and earned moments */
  --signal-ink: #8a5a00;  /* reward colour when it must carry text */

  /* Labels on a signal fill take a DARK ink, in every room. That is not the
     law this token was written for — signal used to be red lead, and then the
     ink had to flip by room — but signal is yellow now and it is the same
     yellow at noon and at midnight, so the ink over it never has cause to
     move. See the dark room's entry for what the inherited flip cost. */
  --on-signal: var(--carbon);

  /* There IS a light source in this room now — the lamp over the desk. A
     shadow is soft, falls downward, and stays cheap: two layers, no spread
     abuse, painted by the compositor. */
  --shadow: 0 6px 0 #243a70;
  --grain-op: 0;   /* see the kill below */

  /* Russian uses upright Geologica through a Cyrillic-only alias in every
     stack. Latin retains the room's display, reading and interface faces. */
  --bill:  'LTL Cyrillic', 'Sofia Sans Extra Condensed', 'Oswald', 'Arial Narrow', sans-serif;
  --serif: 'LTL Cyrillic', 'Spectral', 'Charter', Georgia, 'Times New Roman', serif;
  --sans:  'LTL Cyrillic', 'Geologica', 'Arial', sans-serif;
  --mono:  'LTL Cyrillic', 'Geologica', ui-monospace, monospace;

  /* nine sizes on a 4px baseline; anything off this list is a bug */
  --t-00: 11px; --t-0: 13px; --t-1: 15px; --t-2: 17px; --t-3: 20px;
  --t-4: 24px; --t-5: 29px; --t-6: 35px; --t-7: 44px;

  /* --- what every existing rule in this file already speaks.
     The old palette had three saturated hues; this one has one. Rather than
     hunt every call site, the retired hues are aliased onto the single-hue
     system, so a rule that used to reach for cobalt now reaches for ink and
     the law holds even where the rule was never revisited. --- */
  --paper: var(--room); --panel: var(--galley);
  --canvas: var(--room); --gesso: var(--galley); --size: var(--rule); --foot: var(--rule-room);
  --paper-0: var(--galley); --paper-1: var(--room); --paper-2: var(--case);
  --paper-hi: var(--sheet); --card: var(--galley);
  --line: var(--rule); --line-2: var(--rule-room);
  --ink: var(--carbon); --ink-0: var(--carbon); --ink-1: var(--carbon);
  --ink-2p: var(--steel); --ink-3p: #656d54;
  --ink-2: var(--ink-2p); --ink-3: var(--ink-3p);
  /* --faint was CONSUMED in a dozen places and never declared, so every
     line meant to sit back — the tour's skip, the frontier denominators,
     the locked panel's note — fell back to inherit and printed at full
     text colour. It is the third ink by another name; declaring it here
     keeps it following the room the way its callers assumed it did. */
  --faint: var(--ink-3p);
  --minium: var(--signal); --minium-t: var(--signal-ink);
  --accent: var(--signal); --accent-deep: var(--signal-ink); --accent-text: var(--signal-ink);
  --accent-tint: rgba(188,62,31,.10);
  --cobalt: var(--carbon); --flare: var(--signal);      /* retired: one hue only */
  --paste: var(--carbon); --brass: var(--steel); --brass-t: var(--steel);
  /* GOLD LEAF, MINTED ITS OWN FOIL — 2026-08-22, cascade concordat clause c.
     The token has drifted twice: first aliased to --signal-ink red when the
     play layer was struck (every gilded thing printed сурик), then parked on
     the SIGNAL HEX ITSELF — #f6c445, identical to the everyday call to
     action — so ceremony gold could not out-dress a primary button and the
     word "gold" meant nothing the reward yellow didn't already mean.
     The masstone is now #b8860b: dull blocked leaf, the exact hex a dozen
     sheets already carried as their var(--gold-leaf, #b8860b) fallback — the
     value the gilded sites were DRAWN against — sitting inside the house
     gold family (#8a5a00 signal-ink → #b8860b foil → #e0ad25 old brass →
     #f6c445 signal → #ffe28a highlight) and measuring APCA Lc 56 on the
     room, so foil can carry small ceremony text, which screen-gold never
     could. Real leaf on paper is a dull olive-brass until the light catches
     it; --gold-hi stays the catch.
     THE RATION: --gold-leaf appears at seal / ceremony / finished-spine
     beats ONLY. Every other gilded thing — wordmark hinge, almanac ornament,
     standing marks, meters — prints --signal, the identical hex those sites
     rendered before the mint, so nothing everyday moved on screen. The two
     tokens differing, and the ceremony-only grep gate, are asserted by
     tests/cascade.test.js.
     --gold (the rgba triple) is NOT touched. It is a wash under a tapped word
     and behind a marked cloze, and it must stay the room's ink — gold that
     means something would be gold made semantic, which the palette law
     forbids outright. */
  --gold: 246,196,69;
  --gold-leaf: #b8860b; --gold-hi: #ffe28a;
  /* THE PIGMENT CASE. The one-hue law is amended on purpose: a small case of
     period pigments joins the vermilion, each a material with one job, never
     decoration. MALACHITE — the Petersburg stone, ground for green since the
     Winter Palace columns — is growth: a right answer, a word settling, a
     meter filling. LAZUR (iron-gall over cobalt glass) is knowledge held: the
     bill, the record of lines. GOLD LEAF (cased above) stays ceremony only.
     Vermilion keeps the signal. Anything not on this list prints carbon. */
  --malachite: #22a06b; --malachite-ink: #167a50; --on-malachite: var(--carbon);
  --malachite-tint: rgba(34,160,107,.14);
  --lazur: #244177; --lazur-ink: #152747; --on-lazur: #fffdf5;
  --lazur-tint: rgba(36,65,119,.12);
  --zelyonka: var(--malachite); --ok: var(--malachite); --good: var(--malachite-ink);
  --limelight: var(--sheet);
  --info: var(--lazur-ink); --warn: #8a5a00; --err: #c83d31;
  --verm: #e9543f; --verm-soft: rgba(233,84,63,.12); --wax: #c83d31;
  --new-tint: rgba(36,65,119,.12); --cold: var(--steel);
  /* the five drawers, as ink density on one hue */
  --hm0: var(--rule); --hm1: #e8b8a0; --hm2: #d08762; --hm3: #ab4a28; --hm4: var(--signal-ink);
}

/* THE PRESS ROOM AT NIGHT — dark is a different room, not an inversion. */
[data-theme="dark"] {
  /* Leaf lifts on a dark ground: the same pigment that reads as brass on
     paper reads as mud at night, so the masstone moves toward the highlight —
     measured: the day foil #b8860b holds only Lc −40 on this room, the old
     brass #e0ad25 holds −60. Still the foil, never the signal hex: the
     gold/signal split (concordat clause c) binds every room. */
  --gold-leaf: #e0ad25; --gold-hi: #ffe28a;
  /* The inherited dark mode had the reading sheet at #1c1a12 on a #0a0a0c
     ground: Lc 0.0, WCAG 1.15:1. The page had NO elevation over the room, and
     a cast shadow on near-black is invisible, so nothing recovered it. This
     sheet sits at Y 6.74× room — measured luminance, not a promise about
     shadows. */
  --room:      #121d38;   /* midnight blue game board */
  --case:      #0b1328;   /* LAW: the sheet is never adjacent to case (Y 14.18
                             would break ISO's 1:10). Its surround is room. */
  --galley:    #1c2a4c;
  --rule:      #53658c;
  --rule-room: #42547d;
  --pine:      #5d8cff;
  --pine-deep: #38599f;
  --on-pine:   #10182d;
  --steel:     #c4cde2;   /* raised deliberately: Lc −75.0 on the lifted sheet,
                             exactly the body floor, so the in-column small-caps
                             byline is TEXT and not chrome */
  --sheet:  #253454;      /* warmest blue surface in the room, with elevation */
  --carbon: #fff8df;      /* warm off-white, never #fff — pure white on near-black
                             maximises halation, and halation hurts Cyrillic more
                             than Latin because ж щ ы ф have more strokes to smear */
  --signal:     #f6c445;
  --signal-ink: #ffe28a;  /* reward colour as text AND as the 2px mark: `signal` at 2px on
                             the lifted sheet measures only Lc −18.6 */
  /* THE POLARITY FLIP DIED WITH СУРИК. When signal was red lead the flip was
     real: carbon on a light red was illegal and sheet on a dark red was
     illegal, and the pair had to swap by room. Signal is now #f6c445 in every
     room — it is the one token that does NOT move between light and dark —
     while --carbon does, so pinning on-signal to carbon quietly turned every
     gold fill at night into warm cream on gold: measured 1.53:1 on the desk's
     own TONIGHT banner, and the same on the primary button, an earned seal
     and the best plan. A fill that does not change cannot want an ink that
     does, so it takes the same dark ink the other lifted pigments take. */
  --on-signal: var(--on-pine);
  --ink-3p: #94a2c2;
  --accent-tint: rgba(246,196,69,.14); --new-tint: rgba(93,140,255,.16);
  --gold: 246,196,69;
  --shadow: 0 6px 0 #0b1328;
  --grain-op: 0;
  --hm0: var(--rule-room); --hm1: #7a3a22; --hm2: #a2502f; --hm3: #cf7452; --hm4: #ffb59a;
  /* pigments lift at night the way the leaf does — toward their highlight */
  --malachite: #38c98a; --malachite-ink: #82e5b7; --on-malachite: #0b2b1c;
  --malachite-tint: rgba(56,201,138,.15);
  --lazur: #628ce3; --lazur-ink: #a0beff; --on-lazur: #0e1526;
  --lazur-tint: rgba(98,140,227,.18);
  --err: #ff7d70;
}

/* LAMPLIGHT kept as a third room, warmed toward the sheet. */
/* LAMPLIGHT — a third room, the composing bench under a working lamp. */
[data-theme="sepia"] {
  /* the lamp room already kept honest foil — old brass — through both of the
     token's drifts; it is untouched by the 2026-08-22 mint. Its highlight
     alone shares the signal hex: the glint of light catching leaf, never a
     masstone, so the gold/signal split holds here too. */
  --gold-leaf: #e0ad25; --gold-hi: #f6c445;
  /* THE LAMP ROOM'S GROUND, RE-GROUND (2026-08-25, from the mode audit).

     It was #f4dca3 — chroma 31, and only ΔE 37 from --signal #f6c445, which
     is the same gold the palette law reserves for celebration and
     illumination alone. Spread across a whole viewport that is not a lamplit
     room: it is the sacred colour used as wallpaper. Two harms, and the
     second is worse than the first. It breaks the law outright; and it takes
     the gold's MEANING with it, because the gold READ tab and the gold reward
     were being asked to read as special against a screen already full of gold.

     Re-ground to chroma 15 — ΔE 53 from the signal, a 43% wider gap — which is
     warm parchment under a lamp rather than brass. The room still reads
     unmistakably as lamplight beside the cool near-white of daylight, which is
     where the distinction actually belongs: paper is COOL, the lamp is WARM.
     The gold gets to mean something again. */
  /* deepened once more when the DAYLIGHT room was warmed to join the paper
     family: two warm grounds only ΔE 11.8 apart is one light with a mood, not
     two lights. #e8dabb puts them 14.5 apart — chroma 17, still well under the
     ceiling, and still ΔE 51 from the signal. A lamp does not only warm a
     room; it dims it, and the lightness gap is doing as much work here as the
     hue is. */
  --room: #e8dabb; --case: #d8c9a4; --galley: #fff8df;
  --rule: #cdb894; --rule-room: #8e7240; --steel: #554b3d;
  --sheet: #fffaf0; --carbon: #17213a;
  --signal: #f6c445; --signal-ink: #7a5000; --on-signal: var(--carbon);
  --pine: #244177; --pine-deep: #152747; --on-pine: var(--sheet);
  --ink-3p: #716858;
  --hm0: var(--rule); --hm1: #ffd7a6; --hm2: #ff9f82; --hm3: #e9543f; --hm4: #c83d31;
  /* under the lamp the pigments sit down, toward the stone they were ground
     from. The ARCHITECTURE sits down with them too — see the masthead override in
     90-chromatic-game.css, which has to live there because that sheet owns
     `--play` and loads after this file. */
  --malachite: #22a06b; --malachite-ink: #166d49; --on-malachite: var(--carbon);
  --malachite-tint: rgba(34,160,107,.12);
  --lazur: #244177; --lazur-ink: #152747; --on-lazur: var(--sheet);
  --lazur-tint: rgba(36,65,119,.12);
  --err: #b9342a;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0; background: var(--paper); color: var(--ink); font-family: var(--sans);
  transition: background var(--d-m) var(--e-std), color var(--d-m) var(--e-std); min-height: 100vh;
  font-kerning: normal; text-rendering: optimizeLegibility;
}
/* THE AMBIENT LAYERS ARE GONE, AND THIS IS A MEASUREMENT, NOT A TASTE.
   Over a 20-second window at ~2,778 frames, a reading page alone costs 1.1
   CPU-seconds; the same page with one cheap fullscreen grain behind the text
   costs 6.3. That is 5.7x, +1.87 ms/frame, about 11% of one core held
   continuously, roughly 4.5 extra core-minutes per 40-minute reading session
   — on a laptop ten times faster than the phone the reader owns. It is a fan
   complaint and a battery complaint, and a fan spinning up during Turgenev is
   the fastest way to make someone not come back.
   Removed here: the drifting laid-paper grain, the dark-mode vignette, and the
   lamp flicker. Nothing in this product animates behind or beside running
   text, ever. */

/* THE LAMP. One static pane of light over the whole room — a radial falloff
   from above the desk, painted once and composited, never animated. This is
   not the vignette that was killed above: that one drifted per-frame; this
   one costs what a fixed background costs, which is nothing measurable. */
body::before {
  content: ''; position: fixed; inset: 0; z-index: 60; pointer-events: none;
  background: radial-gradient(130% 105% at 50% 6%,
    rgba(255,213,150,.05) 0%, transparent 44%,
    rgba(38,22,8,.08) 78%, rgba(38,22,8,.17) 100%);
}
[data-theme="dark"] body::before {
  background: radial-gradient(120% 95% at 50% 8%,
    rgba(255,190,120,.05) 0%, transparent 40%,
    rgba(0,0,0,.24) 78%, rgba(0,0,0,.44) 100%);
}

button { font-family: inherit; }
/* Display type is the book's, not the poster's: Spectral carries every
   heading. The condensed bill face survives only at small sizes — running
   heads, plate labels, the playbill chips — where wood type actually lived. */
h1,h2,h3 { font-family: var(--serif); font-weight: 600; letter-spacing: .01em; }
.smallcaps { text-transform: uppercase; letter-spacing: .14em; font-size: .72rem; color: var(--ink-2); font-family: var(--sans); }
.muted { color: var(--ink-2); } .faint { color: var(--ink-3); }

/* ============ header ============ */
header { position: sticky; top: 0; z-index: 40; background: color-mix(in srgb, var(--paper) 94%, transparent);
  border-bottom: 1px solid var(--line); }
.bar { max-width: 900px; margin: 0 auto; padding: 9px 16px; display: flex; align-items: center; gap: 10px; }
@media (max-width: 900px) { .bar { gap: 7px; } .brand { display: none; } }
@media (max-width: 620px) { .nav-tabs button { padding: 6px 5px; font-size: .8rem; } }
.brand { font-family: var(--serif); font-size: 1.05rem; letter-spacing: .18em; cursor: pointer; user-select: none; }
.brand b { color: var(--verm); font-weight: 600; }
.nav-tabs { display: flex; gap: 3px; }
.nav-tabs button { border: none; background: none; color: var(--ink-2); padding: 6px 9px; font-size: .86rem;
  cursor: pointer; border-radius: 8px; transition: color var(--d-s) var(--e-std); }
.nav-tabs button:hover { color: var(--verm); }
.nav-tabs button.active { color: var(--verm); font-weight: 600; }
.spacer { flex: 1; }
.streak-chip { display: flex; align-items: center; gap: 5px; font-size: .85rem; color: var(--ink-2);
  border: 1px solid var(--line); padding: 3px 10px; border-radius: 20px; background: var(--card); cursor: default; }
.streak-chip .fl { color: var(--verm); display: inline-block; transform-origin: 50% 90%; }
@media all {
  body.motion-full .streak-chip .fl { animation: flamewob 3.4s ease-in-out infinite; }
  @keyframes flamewob { 0%,100%{transform:rotate(0) scaleY(1)} 30%{transform:rotate(-3deg) scaleY(1.04)} 60%{transform:rotate(2.5deg) scaleY(.97)} 80%{transform:rotate(-1.5deg) scaleY(1.02)} }
}
.theme-dots { display: flex; gap: 7px; }
.theme-dots button { width: 21px; height: 21px; border-radius: 50%; cursor: pointer; border: 1.5px solid var(--line); padding: 0;
  transition: border-color var(--d-s) var(--e-std), box-shadow var(--d-s) var(--e-std); }
.theme-dots button.on { border-color: var(--verm); box-shadow: 0 0 0 2px var(--verm-soft); }
/* A SWATCH SHOWS THE ROOM IT OPENS. These three still carried the warm-paper
   identity from two skins ago — #f6efe1 / #e8d9b5 / #241c14 — while the rooms
   they switch to are the near-white page, the lamp's amber and the midnight
   board. All three dots misdescribed their own door.
   Then the lamp dot sat out the 2026-08-25 re-grind (see --room at :335) and
   went on painting #f4dca3 — the very near-signal gold that re-grind removed,
   here as an actual painted surface. These three hexes ARE the three --room
   values: change a ground, change its dot, or the door lies again. */
.t-paper { background: #f7f9ff; } .t-sepia { background: #e8dabb; } .t-dark { background: #121d38; }
.icon-btn { border: 1px solid var(--line); background: var(--card); color: var(--ink); border-radius: 8px;
  padding: 5px 10px; font-size: .85rem; cursor: pointer; transition: border-color var(--d-s) var(--e-std), background var(--d-s) var(--e-std); }
.icon-btn:hover { border-color: var(--verm); }
.icon-btn.on { background: var(--verm); border-color: var(--verm); color: #f6efe1; }

/* ============ views ============ */
/* THE TAIL IS A BAND, NOT A FIELD (30 Aug audit): 30vh of blanket clearance
   left every view trailing 240–700px of bare ground with one stray glyph
   before the legal foot. Clearance that size exists for fixed furniture,
   and only the reading route has any — it keeps its deep tail below. */
.view { display: none; max-width: 900px; margin: 0 auto; padding: 26px 20px calc(var(--band, 40px) * 2); }
#v-read.view { padding-bottom: 30vh; }
.view.show { display: block; }
.masthead { text-align: center; margin: 22px 0 26px; }
.masthead .t0 { letter-spacing: .3em; font-size: .72rem; color: var(--steel); }

.masthead .rule { width: 132px; margin: 18px 0 0; border-top: 3px solid var(--signal); position: relative; }
.masthead .rule span { position: absolute; top: -0.72em; left: 50%; transform: translateX(-50%);
  background: none; padding: 0 10px 0 0; color: var(--signal); display: inline-block; }
@media all {
  body.motion-full .masthead .rule span { animation: breathe 7s ease-in-out infinite; }
  @keyframes breathe { 0%,100%{transform:translateX(-50%) scale(1)} 50%{transform:translateX(-50%) scale(1.05)} }
}
/* .grid2 is struck: the cabinet it laid out was recomposed into the record
   sheet (65-home.css), and no other view ever set anything in two columns. */
.card { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; margin-bottom: 16px; }
.card h3 { margin: 0 0 10px; font-size: 1.06rem; }
.btn { border: 1px solid var(--line); background: var(--paper-hi); color: var(--ink); padding: 9px 16px;
  border-radius: 9px; cursor: pointer; font-size: .92rem;
  transition: border-color var(--d-s) var(--e-std), background var(--d-s) var(--e-std), transform var(--d-xs) var(--e-std); }
.btn:hover { border-color: var(--verm); }
.btn:active { transform: scale(.97); }
.btn.primary { background: var(--verm); border-color: var(--verm); color: #f8f2e4; }
.btnrow { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }

/* meters: transform-based (compositor-only), two layers: storage under retrieval */
.meter { height: 8px; border-radius: 4px; background: var(--hm0); position: relative; overflow: hidden; }
.meter > i { position: absolute; inset: 0; border-radius: 4px; transform-origin: 0 50%;
  transform: scaleX(var(--v, 0)); transition: transform calc(900ms * var(--motion)) var(--e-enter); }
.meter > i.storage { background: var(--signal); opacity: .55; }
.meter > i.retr { background: var(--verm); }
.meter .tick { position: absolute; top: -3px; width: 1px; height: 14px; background: var(--ink-3); }
.meter .tick.t98 { background: var(--good); }
.meter-legend { display: flex; justify-content: space-between; font-size: .72rem; color: var(--ink-3); margin-top: 3px; }

/* «осталось» ink dots + downward ticker */
.dots { line-height: 1.9; }
.dots svg { vertical-align: middle; margin: 0 1px; }
.dots .strike { stroke: var(--ink-2); stroke-width: 1.6; stroke-linecap: round;
  stroke-dasharray: 14; stroke-dashoffset: 14; }
.dots .struck .strike { transition: stroke-dashoffset calc(180ms * var(--motion)) var(--e-std); stroke-dashoffset: 0; }
.tick-n { font-variant-numeric: tabular-nums; font-weight: 650; display: inline-block; }
.tick-n.flash { animation: numflash calc(900ms * var(--motion)) var(--e-enter); }
@keyframes numflash { 0%{color:var(--verm); transform:scale(1.18)} 100%{color:inherit; transform:scale(1)} }

/* bookshelf */
.shelf { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.book { position: relative; border: 1px solid var(--line); border-radius: 6px 12px 12px 6px; padding: 18px 16px 14px 22px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--verm) 14%, var(--card)) 0 8px, var(--card) 8px);
  cursor: pointer; transition: transform var(--d-s) var(--e-std), box-shadow var(--d-s) var(--e-std); min-height: 158px;
  display: flex; flex-direction: column; }
.book:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(43,33,24,.14); }
.book .b-author { font-size: var(--t-00); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3); }
.book .b-title { font-family: var(--serif); font-size: 1.24rem; font-weight: 600; margin: 4px 0 1px; }
.book .b-year { font-size: .74rem; color: var(--ink-2); font-style: italic; }
.book .b-desc { font-size: .78rem; color: var(--ink-2); margin: 8px 0 auto; line-height: 1.45; }
.book .lvl { display: inline-block; font-size: var(--t-00); letter-spacing: .1em; text-transform: uppercase; padding: 2px 8px;
  border-radius: 10px; border: 1px solid var(--line); color: var(--ink-2); }
.book .lvl.l1 { border-color: var(--good); color: var(--good); }
.book .ribbon { position: absolute; top: -4px; right: 14px; width: 14px; height: 34px; background: var(--verm);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 78%, 0 100%); }
.book .b-seal { position: absolute; bottom: -8px; right: -6px; width: 40px; height: 40px; transform: rotate(10deg); font-size: .95rem; }
/* resumption: half-open book on home */
.resume-card { border-left: 4px solid var(--verm); }
.resume-card .gutter { font-family: var(--serif); font-size: 1.15rem; line-height: 1.6; font-style: italic; margin: 8px 0; }
.resume-card .gutter .dim { opacity: .38; }

/* hearth + words */
.hearth { display: flex; gap: 16px; align-items: flex-start; }
.hearth .stove { font-size: 2.1rem; filter: drop-shadow(0 0 12px rgba(255,150,50,.45)); }
@media all {
  body.motion-full .hearth .stove.lit { animation: glowpulse 3.2s ease-in-out infinite; }
  @keyframes glowpulse { 0%,100%{filter:drop-shadow(0 0 10px rgba(255,150,50,.35))} 50%{filter:drop-shadow(0 0 18px rgba(255,150,50,.6))} }
}
.cold-note { color: var(--cold); }
.warmbar { display: block; height: 4px; border-radius: 2px; background: var(--hm0); overflow: hidden; position: relative; }
.warmbar > i { position: absolute; inset: 0; transform-origin: 0 50%; transform: scaleX(var(--v, 0));
  background: linear-gradient(90deg, #d8674b, #e6a23c); border-radius: 2px; }
.warmbar.cold > i { background: linear-gradient(90deg, var(--cold), #9db2c8); }
.frost { position: relative; border-color: var(--cold) !important;
  background: linear-gradient(140deg, rgba(160,195,225,.14), rgba(160,195,225,.03) 55%), var(--card) !important; }
.frost::after { content: '❄'; position: absolute; top: 6px; right: 10px; color: var(--cold); font-size: .85rem; }
.melt { animation: melt calc(900ms * var(--motion)) var(--e-std) forwards; }
@keyframes melt { from { filter: saturate(.5) brightness(1.06); } to { filter: none; } }

/* The GitHub-style ledger heatmap is struck: the month now stands in the
   record sheet as a register of vertical ink strokes (65-home.css), and the
   frost markers went with the streak they served. */
.seal { border-radius: 50%; background: radial-gradient(circle at 35% 32%, #c4544a, var(--wax) 68%);
  box-shadow: inset 0 2px 5px rgba(255,255,255,.28), inset 0 -3px 6px rgba(0,0,0,.38), 0 3px 8px rgba(0,0,0,.3);
  display: flex; align-items: center; justify-content: center; color: #f0d9c0; font-family: var(--serif); font-weight: 700; }
.seals { display: flex; flex-wrap: wrap; gap: 10px; }
.ach { width: 54px; height: 54px; font-size: 1.15rem; opacity: .28; filter: grayscale(.8); cursor: default; }
.ach.got { opacity: 1; filter: none; }
/* the impression is engraved into the wax by JS (MARK → ui/icons.js) */
.ach .ic { width: 1.35em; height: 1.35em; }
/* ---- the engraver's case: every cut icon inherits the line's ink ---- */
.ic { width: 1.08em; height: 1.08em; display: inline-block; vertical-align: -.18em; flex: none; }
.mark-t { font-style: normal; }
/* .find-card is struck: curiosities are marginalia on the record sheet now
   (65-home.css), and the blurred "not found yet" teasers went unmourned. */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 10px; }
.tile { background: var(--paper-hi); border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px; }
.tile .v { font-size: 1.45rem; font-weight: 650; font-variant-numeric: tabular-nums; }
.tile .l { font-size: .72rem; color: var(--ink-2); margin-top: 1px; }
.chartbox svg { display: block; width: 100%; height: auto; }

/* issues (story view) */
.issues { display: grid; grid-template-columns: repeat(auto-fill, minmax(235px, 1fr)); gap: 12px; }
.issue { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px;
  cursor: pointer; position: relative; transition: transform var(--d-s) var(--e-std), border-color var(--d-s) var(--e-std); }
.issue:hover { border-color: var(--verm); transform: translateY(-2px); }
.issue .no { font-size: var(--t-00); letter-spacing: .16em; color: var(--ink-3); }
.issue .ti { font-family: var(--serif); font-size: 1.12rem; margin: 3px 0 1px; }
.issue .en { font-size: .78rem; color: var(--ink-2); font-style: italic; }
.issue .meta { font-size: .74rem; color: var(--ink-3); margin-top: 8px; }
.issue.locked-look { opacity: .82; }
.continue-card .sc-title { font-family: var(--serif); font-size: 1.5rem; margin: 4px 0 2px; }
.continue-card .sc-sub { font-style: italic; color: var(--ink-2); font-family: var(--serif); margin-bottom: 10px; }

/* ============ reading ============ */
#read-wrap { max-width: 34em; margin: 0 auto; display: flex; flex-direction: column; }
/* THE BOOK FIRST, FOR SOMEBODY WHO HAS NEVER READ HERE.
   Photographed on a 390px phone, a stranger's first reading page put eleven
   blocks of furniture above the first sentence of Chekhov: the gift plate, the
   scene head, an ornament, a margin note, the guide's bubble, a card reading
   "You already recognise 47% of this instalment's words", a warm-up button and
   a button offering to take 117 words into a deck. The text began two and a
   half screens down. 58.6% of all visits end on this page.

   A warm-up and a deck are for somebody who has decided to study. They are not
   what decides it. On a first visit they wait below the text; the moment the
   reader has marked a single word they come back to the top, where they are
   the right tools in the right order. Nothing is removed and nothing is
   hidden — the page is dealt in the order the reader needs it. */
#read-wrap.first-read #gift-plate { order: 3; }
#read-wrap.first-read #prep-slot  { order: 4; }
#read-wrap.first-read #scene-end-slot { order: 5; }
/* The guide's card is 154px of the fold and it is counsel about a book the
   reader has not begun. The class above already existed for exactly this
   reader and moved nothing that stood above the text; now it does. */
#read-wrap.first-read #guide-slot { order: 2; }
.scene-head { text-align: center; margin: 18px 0 30px; }
.scene-head .vy { letter-spacing: .3em; font-size: .7rem; color: var(--ink-2); }
.scene-head h2 { font-size: 2rem; margin: 10px 0 2px; letter-spacing: .04em; }
.scene-head .sub { font-family: var(--serif); font-style: italic; color: var(--ink-2); }
.scene-head .orn { color: var(--verm); font-size: 1.2rem; margin-top: 12px; }
.prep-banner { border: 1px solid var(--line); border-left: 3px solid var(--verm); background: var(--card);
  border-radius: 10px; padding: 12px 16px; margin-bottom: 24px; font-size: .9rem; }
#text { font-family: var(--serif); font-size: var(--fs, 1.28rem); line-height: 1.68; text-wrap: pretty; }
#text p { margin: 0 0 1.1em; text-align: left; text-wrap: pretty; content-visibility: auto; contain-intrinsic-size: auto 120px; }
#text p.dropcap::first-letter { color: var(--book-ink, var(--verm)); font-weight: 700; float: left; font-size: 3.1em;
  line-height: .84; padding: .04em .12em 0 0; }
.sent { border-radius: 3px; cursor: pointer; transition: background var(--d-m) var(--e-std), opacity var(--d-m) var(--e-std); }
/* THE ONLY PINK IN THE READING ROOM. --verm-soft is vermilion at low alpha,
   which on paper reads as salmon, and the palette law is two inks and paper.
   The line being spoken is a state, not a warning, so it takes the room's own
   ink at the weight a highlighter would have. */
/* the estate's links: on the desk, under everything, quiet */
#estate { display: none; }
/* TWO COLUMNS AT MOST — the books and the rest of the house, side by side,
   exactly as wide as they always were. The sentence above them and the other
   rooms below span the foot; spanned by a line, an auto-fit track can no
   longer collapse, so a plain 240px minimum left the navs in three narrow
   columns of four and an empty fourth. 40% admits two tracks and never three,
   and below 514px there is one, as there always was. */
body.at-desk #estate {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(max(240px, 40%), 1fr));
  gap: 10px 34px; max-width: var(--desk-w, 1180px); margin: 46px auto 30px;
  padding: 24px 20px 8px; border-top: 1px solid var(--rule);
}
#estate .est-rooms { grid-column: 1 / -1; }
#estate nav { display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: baseline; }
#estate .est-h { flex: 1 0 100%; margin: 0 0 4px; font-family: var(--bill);
  font-size: var(--t-00); letter-spacing: .16em; text-transform: uppercase; color: var(--steel); }
#estate a { font-size: .84rem; color: var(--steel); text-decoration: none;
  border-bottom: 1px solid transparent; }
#estate a:hover { color: var(--carbon); border-bottom-color: var(--signal); }
/* the house in one sentence, across the whole foot and in the links' own ink */
#estate .est-about { grid-column: 1 / -1; margin: 0 0 6px; max-width: 70ch;
  font-size: .84rem; line-height: 1.6; color: var(--steel); }
/* the same sentence under the plate, which only a browser with scripting off
   ever draws (#room-about): set as the masthead sets its lines, centred, and
   in the masthead's own ink, whichever ground the skin has laid under it */
.desk-about { max-width: 36em; margin: 14px auto 0; text-align: center;
  font-family: var(--serif); font-size: 1rem; line-height: 1.6; color: inherit; }
.desk-about a { color: inherit; }
/* READ, NOT SEEN: words a screen reader and a crawler need that the eye is
   already given elsewhere on the screen — the house's name inside the room's
   heading, which the wordmark on the rail says in type. Clipped to nothing,
   out of flow, so the line the plate is set on cannot move by a pixel. */
.vh { position: absolute !important; width: 1px !important; height: 1px !important;
  padding: 0 !important; margin: -1px !important; overflow: hidden !important;
  clip: rect(0 0 0 0) !important; clip-path: inset(50%) !important;
  white-space: nowrap !important; border: 0 !important; }

.rev-empty { max-width: 34em; margin: 10px auto 0; text-align: center;
  font-size: .93rem; color: var(--steel); line-height: 1.6; }

/* the account argument in the Words room — a slip laid on the case, in the
   room's own ink, never a coloured banner asking for something */
.words-keep { border: 1px solid var(--rule); border-left: 3px solid var(--signal);
  background: var(--galley); padding: 13px 16px; margin: 14px 0 4px; border-radius: 3px; }
.words-keep p { margin: 0 0 9px; font-size: .92rem; color: var(--carbon); }
.words-keep .wk-row { margin: 0; display: flex; align-items: center; gap: 14px; }
.words-keep .lnk { background: none; border: none; padding: 0; cursor: pointer;
  color: var(--steel); font-size: .86rem; text-decoration: underline; }

.sent.playing { background: color-mix(in srgb, var(--book-ink, var(--ink)) 9%, transparent); }
body.immersion .w { background: transparent !important; box-shadow: none !important; cursor: default; }
/* Words wear a hairline, not a highlighter. A page of unknown words must
   still read as a page of literature — the mark sits under the baseline
   and never touches the letterforms. */
.w { cursor: pointer; padding: 0 .5px; margin: 0 -.5px; }
/* THE MARK IS DRAWN BY THE ПОМЕТА BLOCK BELOW, AND ONLY THERE.
   A two-state underline on a --wmark property used to be declared here, and
   the comment beside it already said it was superseded — but saying so is not
   removing it. It kept setting background-image and background-size on every
   word, so the real mechanic below was overriding a live rule rather than
   standing alone, and a third system later grew on the same dead property in
   a feature sheet. Measured consequences in the default configuration: the
   memory gauge drew at 1px instead of 2, and a word marked Known drew nothing
   at all. A superseded rule that is still in the file is not superseded. */
body.hint-none .w { background-image: none !important; }
.w.bloom { animation: bloom calc(420ms * var(--motion)) var(--e-snap); }
@keyframes bloom { 0%{transform:scale(1)} 30%{transform:scale(1.14); color:var(--verm)} 100%{transform:scale(1)} }
.w.kglow { animation: kglow calc(620ms * var(--motion)) var(--e-std); }
@keyframes kglow { 0%{background-color:rgba(var(--gold),.5); border-radius:3px} 100%{background-color:rgba(var(--gold),0)} }
/* beginner mode: the English rides along under every sentence, quietly */
.tr-para { display: block; font-family: var(--sans); font-size: .78rem; line-height: 1.45;
  color: var(--ink-3p); margin: 1px 0 9px; padding-left: 10px; border-left: 2px solid var(--line); }
.tr-reveal { display: block; font-family: var(--sans); font-size: .84rem; line-height: 1.5; color: var(--ink-2);
  background: var(--card); border-left: 2px solid var(--verm); border-radius: 0 8px 8px 0;
  padding: 8px 12px; margin: 6px 0 10px; }
.tr-reveal .tr-tools { margin-top: 5px; display: flex; gap: 10px; }
.tr-reveal .tr-tools button { border: none; background: none; color: var(--verm); cursor: pointer; font-size: .78rem; padding: 0; }
.divider { text-align: center; color: var(--verm); margin: 30px 0; font-size: 1.1rem; }
#readbar { position: fixed; bottom: 0; left: 0; right: 0; z-index: 30;
  background: color-mix(in srgb, var(--paper) 96%, transparent); border-top: 1px solid var(--line); }
#readbar .inner { max-width: 900px; margin: 0 auto; padding: 8px 14px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rb-extra { display: contents; }
.rb-more { display: none; }
/* THE READING BAR, ON THE SCREEN THE TRAFFIC ARRIVES ON.
   Nine controls and flex-wrap made this three rows and 149px tall at 390px —
   a sixth of the screen, standing under a page whose first Russian sentence
   was already below the fold. The bar keeps the three things a reader in the
   middle of a page actually reaches for (back, the voice, the ribbon) and the
   rest becomes a shelf that opens when it is wanted. Nothing is removed.
   AND NOTHING IS PUSHED OFF THE EDGE. Held to one row at any cost, the bar
   in the Russian interface — «СОДЕРЖАНИЕ», «СЛУШАТЬ», «ОСТАНОВИТЬСЯ ЗДЕСЬ»,
   Cyrillic set in the wide face — was 588px wide on a 375px phone, and the
   ··· that opens the shelf stood at 544px, off the screen: speed, hints, the
   lamp and the type size could not be reached at all (walk of 26 Sep 2026).
   With the shelf folded there are only four plates left to place, so the
   row may break when they truly do not fit — one row in English, German and
   Spanish, two in Russian — rather than lose a control. The shelf above it
   breaks the same way: nowrap, its Russian row ran 118px off the left edge
   and took the speed slider with it. */
@media (max-width: 560px) {
  #readbar .inner { flex-wrap: wrap; gap: 6px; padding: 6px 10px; }
  #readbar .inner > .icon-btn { flex: 0 0 auto; }
  /* the three dots sit in the middle of their plate: an inline-flex box with
     no alignment laid its one line at the top, 11px above the centre */
  .rb-more { display: inline-flex; align-items: center; justify-content: center; margin-left: auto; }
  .rb-extra { display: none; position: absolute; bottom: 100%; right: 8px;
    background: var(--paper); border: 1px solid var(--line); border-bottom: none;
    border-radius: 0; padding: 8px 10px; gap: 8px;
    flex-wrap: wrap; justify-content: flex-end;
    align-items: center; box-shadow: 0 -6px 18px rgba(0,0,0,.10); }
  #readbar.rb-open .rb-extra { display: flex; }
  #readbar .left-note { display: none; }
  #readbar.rb-open .rb-extra #rate { width: 92px; }
  /* the shelf is paper while the bar under it is ink: the word over the
     slider takes the paper's ink, not the bar's */
  #readbar .rb-extra .rb-rate-lab { color: var(--carbon); }
}
/* THE SPEED HAS A NAME YOU CAN SEE. The slider was a bare track whose only
   name was a hover tooltip — nothing at all on a phone, and nothing for a
   screen reader. A small word stands over the track in the bar's own wood
   type; the aria-label carries the full «Reading speed», which contains the
   word shown, so a voice command for what is on screen still finds it.
   The word costs the bar nothing: set beside the track it pushed the count
   onto a second row at 1280px, and stacked over it the bar grew by a line.
   So it sits in the slider's own 44px box, whose top the track never used —
   the track is let down by that much padding, and the whole box, word
   included, is still the thing a thumb or a pointer presses. */
.rb-rate { position: relative; display: inline-block; margin: 0; cursor: pointer; }
.rb-rate-lab { position: absolute; top: 2px; left: 0; pointer-events: none; white-space: nowrap;
  font-family: var(--bill); font-size: var(--t-00); font-weight: 600;
  letter-spacing: .12em; text-transform: uppercase; line-height: 1; color: inherit; }
.rb-rate #rate { display: block; box-sizing: border-box; padding-top: 14px; }
#readbar .left-note { font-size: .76rem; color: var(--ink-2); margin-left: auto; font-variant-numeric: tabular-nums; }
#readbar .fill { position: absolute; top: -1px; left: 0; right: 0; height: 2px; background: var(--verm);
  transform-origin: 0 50%; transform: scaleX(var(--v, 0)); transition: transform var(--d-l) var(--e-std); }
#rate { accent-color: var(--verm); width: 70px; }
.scene-end { border: 1px solid var(--line); border-radius: 14px; background: var(--card); padding: 22px 24px; margin-top: 30px; }
.quiz-q { margin: 14px 0; }
.quiz-q .qq { font-family: var(--serif); font-size: 1.05rem; margin-bottom: 8px; }
.quiz-opt { display: block; width: 100%; text-align: left; margin: 5px 0; padding: 9px 13px; border-radius: 9px;
  border: 1px solid var(--line); background: var(--paper-hi); color: var(--ink); cursor: pointer; font-size: .92rem;
  transition: border-color var(--d-s) var(--e-std); }
.quiz-opt:hover { border-color: var(--verm); }
.quiz-opt.right { border-color: var(--good); background: color-mix(in srgb, var(--good) 12%, var(--paper-hi)); }
.quiz-opt.wrong { border-color: var(--verm); opacity: .6; }

/* seal ceremony: press-and-hold */
.seal-hold { position: relative; width: 84px; height: 84px; margin: 12px auto; cursor: pointer; user-select: none;
  -webkit-user-select: none; touch-action: none; }
.seal-hold .seal-body { position: absolute; inset: 0; font-size: 1.6rem;
  transition: transform var(--d-s) var(--e-std); }
.seal-hold.pressing .seal-body { transform: scale(.92) rotate(3deg); transition: transform 1100ms var(--e-std); }
.seal-hold .ring { position: absolute; inset: -6px; border-radius: 50%; border: 2px solid var(--verm);
  opacity: 0; transform: scale(.8); }
.seal-hold.pressing .ring { opacity: .7; transform: scale(1); transition: all 1100ms linear; }
.seal-stamp { animation: sealstamp calc(900ms * var(--motion)) var(--e-std); }
@keyframes sealstamp {
  0%   { transform: scale(1) rotate(0); }
  20%  { transform: scale(.86) rotate(-4deg); }          /* anticipation */
  32%  { transform: scale(1.22) rotate(14deg) scaleY(.88); }  /* impact overshoot + squash */
  55%  { transform: scale(.96) rotate(9deg); }
  75%  { transform: scale(1.05) rotate(11deg); }
  100% { transform: scale(1) rotate(10deg); }            /* settle */
}
.wax-particle { position: fixed; width: 7px; height: 7px; border-radius: 50%; background: var(--wax);
  pointer-events: none; z-index: 300; }
/* spilled type — the house's confetti; gilded every third glyph on strikes */
.type-spill { position: fixed; z-index: 300; pointer-events: none; user-select: none;
  font-family: var(--serif); font-weight: 600; color: var(--carbon); line-height: 1; }
.type-spill.ts-gold { color: var(--signal, #f6c445);
  text-shadow: 0 0 7px color-mix(in srgb, var(--signal, #f6c445) 55%, transparent); }

/* ============ review (study card) ============ */
#rev-card { background: var(--card); border: 1px solid var(--line); border-radius: 16px; padding: 34px 28px;
  text-align: center; min-height: 300px; display: flex; flex-direction: column; justify-content: center; gap: 13px; margin-top: 14px; }
#rev-card .cloze { font-family: var(--serif); font-size: 1.22rem; line-height: 1.7; }
#rev-card .cloze .blank { color: var(--verm); font-weight: 600; letter-spacing: .05em; border-bottom: 2px solid var(--verm); padding: 0 4px; }
#rev-card .cloze mark, .ex-main mark, .ex-more mark { background: rgba(var(--gold), .38); color: inherit; border-radius: 3px; padding: 0 2px; }
.produce-gloss { font-size: 1.12rem; line-height: 1.4; }
.type-input { width: 100%; max-width: 320px; text-align: center; font-family: var(--serif); font-size: 1.5rem;
  border: none; border-bottom: 2px solid var(--verm); background: transparent; color: var(--ink);
  padding: 4px 8px; outline: none; letter-spacing: .03em; }
.reveal-word { font-family: var(--serif); font-size: 2rem; font-weight: 600; }
.reveal-word.ok { color: var(--good); }
.reveal-word.bad { color: var(--verm); }
.ink-redraw { animation: inkredraw calc(700ms * var(--motion)) var(--e-enter); }
@keyframes inkredraw { 0%{opacity:0; filter:blur(4px)} 100%{opacity:1; filter:blur(0)} }
.gold-bloom { animation: goldbloom calc(700ms * var(--motion)) var(--e-snap); }
@keyframes goldbloom { 0%{text-shadow:0 0 0 rgba(var(--gold),0)} 35%{text-shadow:0 0 22px rgba(var(--gold),.9)} 100%{text-shadow:0 0 0 rgba(var(--gold),0)} }
.conf-row { display: flex; gap: 8px; justify-content: center; }
.conf-row button { border: 1px solid var(--line); background: var(--paper-hi); color: var(--ink-2);
  border-radius: 16px; padding: 5px 14px; font-size: .8rem; cursor: pointer; }
.conf-row button:hover { border-color: var(--verm); color: var(--ink); }
.ex-main { font-family: var(--serif); font-style: italic; font-size: 1.05rem; line-height: 1.55; color: var(--ink); margin: 4px 0; }
.more-ex { text-align: left; border-top: 1px solid var(--line); margin-top: 10px; padding-top: 8px; }
.ex-more { font-family: var(--serif); font-size: .98rem; line-height: 1.5; margin: 0 0 9px; cursor: pointer; padding-left: 12px; border-left: 2px solid var(--line); }
.ex-more:hover { border-left-color: var(--verm); }
.ex-more .ex-en { display: block; font-family: var(--sans); font-size: .78rem; color: var(--ink-2); margin-top: 2px; }
.rev-btns { display: flex; gap: 10px; justify-content: center; margin-top: 14px; flex-wrap: wrap; }
.rev-btns button { border: 1px solid var(--line); background: var(--card); color: var(--ink); border-radius: 10px;
  padding: 12px 22px; font-size: .95rem; cursor: pointer; }
.rev-btns .primary { background: var(--verm); border-color: var(--verm); color: #f6efe1; }
.rev-btns .b-again { border-color: var(--verm); color: var(--verm); }
.rev-btns .b-good { border-color: var(--good); color: var(--good); }
#rev-card .opt { font-family: var(--serif); font-size: 1.15rem; padding: 9px 16px; }
.tilesrow { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin: 12px 0 4px; min-height: 44px; }
.ltile { min-width: 38px; height: 44px; padding: 0 10px; border: 1px solid var(--line); border-radius: 9px;
  background: var(--paper-hi); font-family: var(--serif); font-size: 1.25rem; font-weight: 600; color: var(--ink);
  cursor: pointer; transition: transform var(--d-xs) var(--e-std), border-color var(--d-s) var(--e-std); }
.ltile:hover { border-color: var(--verm); }
.ltile:disabled { opacity: .25; cursor: default; }
.built { font-family: var(--serif); font-size: 1.6rem; letter-spacing: .08em; min-height: 2.2rem;
  border-bottom: 2px solid var(--line); display: inline-block; min-width: 180px; padding: 0 10px; }
.built.ok { color: var(--good); border-color: var(--good); }
.built.bad { color: var(--verm); border-color: var(--verm); }
.session-ring { width: 56px; height: 56px; }
.session-ring circle { fill: none; stroke-width: 5; }
.session-ring .track { stroke: var(--hm0); }
.session-ring .prog { stroke: var(--verm); stroke-linecap: round; transform: rotate(-90deg); transform-origin: 50% 50%;
  transition: stroke-dashoffset var(--d-l) var(--e-std); }
.rootline { font-size: .8rem; color: var(--ink-2); margin-top: 8px; line-height: 1.5; }
.rootline .rt { color: var(--verm); font-weight: 600; }
.rootline .rw { cursor: pointer; border-bottom: 1px dotted var(--ink-3); }
.rootline .rw.knw { border-bottom-color: transparent; color: var(--ink); }
.mnem-box { width: 100%; border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  border-radius: 8px; padding: 7px 10px; font-family: inherit; font-size: .85rem; resize: vertical; min-height: 40px; }

/* words list */
.filters { display: flex; gap: 6px; margin: 14px 0; flex-wrap: wrap; align-items: center; }
.filters button { border: 1px solid var(--line); background: var(--card); color: var(--ink); border-radius: 8px;
  padding: 5px 12px; cursor: pointer; font-size: .85rem; }
.filters button.active { background: var(--verm); color: #f6efe1; border-color: var(--verm); }
.filters input { margin-left: auto; border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 8px; padding: 5px 10px; }
#wordlist { list-style: none; padding: 0; margin: 0; }
#wordlist li { display: flex; align-items: baseline; gap: 12px; padding: 9px 4px; border-bottom: 1px solid var(--line); }
#wordlist .wf { font-family: var(--serif); font-size: 1.1rem; min-width: 130px; cursor: pointer; }
#wordlist .wf:hover { color: var(--verm); }
#wordlist .wg { color: var(--ink-2); font-size: .88rem; flex: 1; }
#wordlist .cnt { color: var(--ink-3); font-size: .74rem; white-space: nowrap; }

/* grammar + journey */
.glist { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
@media (max-width: 640px) { .glist { grid-template-columns: 1fr; } }
.glesson-card { border: 1px solid var(--line); border-radius: 10px; padding: 13px 15px; background: var(--card); cursor: pointer;
  transition: border-color var(--d-s) var(--e-std); }
.glesson-card:hover { border-color: var(--verm); }
.glesson-card .gru { font-family: var(--serif); font-weight: 600; font-size: 1.05rem; }
.glesson-card .gen2 { font-size: .78rem; color: var(--ink-2); font-style: italic; }
#lesson-body { font-size: .96rem; line-height: 1.65; max-width: 640px; }
#lesson-body p { margin: 0 0 .9em; }
.gex { border-left: 2px solid var(--verm); background: var(--card); border-radius: 0 10px 10px 0;
  padding: 10px 14px; margin: 12px 0; }
.gex .gru-s { font-family: var(--serif); font-size: 1.05rem; line-height: 1.55; }
.gex .gru-s mark { background: rgba(var(--gold), .35); color: inherit; border-radius: 3px; padding: 0 2px; }
.gex .gen-s { font-size: .82rem; color: var(--ink-2); margin-top: 4px; }
.gex .gnote { font-size: .78rem; color: var(--verm); margin-top: 4px; }
.gchip { display: inline-block; border: 1px solid var(--line); border-radius: 12px; padding: 2px 10px;
  font-size: .74rem; color: var(--verm); cursor: pointer; margin-top: 6px; }
.gchip:hover { border-color: var(--verm); }
.tl { border-left: 2px solid var(--line); margin-left: 8px; padding-left: 22px; }
.tl .tl-day { position: relative; margin-bottom: 20px; }
.tl .tl-day::before { content: ''; position: absolute; left: -28px; top: 4px; width: 10px; height: 10px;
  border-radius: 50%; background: var(--verm); }
.tl .tl-date { font-size: var(--type-meta); letter-spacing: .12em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 4px; }
.tl .tl-ev { font-size: .92rem; margin: 3px 0; }

/* «вернуться назад» dissolve */
.dissolve .w.wasnew { animation: dissolve calc(600ms * var(--motion)) var(--e-std) both; }
@keyframes dissolve { 0%{background:var(--new-tint); filter:blur(0)} 60%{filter:blur(1px)} 100%{background:transparent; filter:blur(0)} }

/* overlays, toast, popup */
/* z 290: ABOVE the lesson stage (240) and toasts (250) — a ceremony pressed at
   the lesson bench must not open invisibly underneath the room — and below
   the particle layers (300+), whose bursts belong on top of the plate. */
.overlay { position: fixed; inset: 0; z-index: 290; background: rgba(20,14,8,.5); display: none;
  align-items: center; justify-content: center; padding: 20px; }
.overlay.show { display: flex; }
.overlay .panel { background: var(--paper-hi); border: 1px solid var(--line); border-radius: 16px;
  max-width: 500px; width: 100%; padding: 26px 28px; box-shadow: var(--shadow); max-height: 88vh; overflow-y: auto; }
.overlay .panel h3 { margin-top: 0; }
.toast { position: fixed; bottom: 76px; right: 18px; z-index: 250; background: var(--card); color: var(--ink);
  border: 1px solid var(--line); border-left: 3px solid var(--verm); border-radius: 10px; padding: 12px 16px;
  box-shadow: var(--shadow); max-width: 300px; cursor: pointer; font-size: .88rem;
  transform: translateY(16px); opacity: 0; pointer-events: none;
  transition: transform var(--d-m) var(--e-enter), opacity var(--d-m) var(--e-enter); }
.toast.show { transform: none; opacity: 1; pointer-events: auto; }
.toast .tt { font-weight: 600; color: var(--verm); font-size: .74rem; letter-spacing: .12em; text-transform: uppercase; }
#tip { position: absolute; z-index: 90; pointer-events: none; background: var(--ink); color: var(--paper);
  padding: 7px 12px; border-radius: 8px; font-size: .88rem; max-width: 320px; line-height: 1.45;
  opacity: 0; transform: translateY(4px); transition: opacity var(--d-s) var(--e-enter), transform var(--d-s) var(--e-enter); font-family: var(--sans); }
#tip.show { opacity: 1; transform: translateY(0); }
#tip .tw { font-weight: 700; }
#pop { position: absolute; z-index: 100; width: 340px; max-width: calc(100vw - 24px); background: var(--paper-hi);
  border: 1px solid var(--line); border-radius: 14px; box-shadow: var(--shadow); padding: 15px 17px;
  display: none; font-family: var(--sans);
  /* the card is a slip from THIS book: it wears the book's ink at its head,
     the same pigment as the drop cap and the sheet — a colourless white card
     was the one object in the room that belonged to no book at all */
  border-top: 3px solid var(--book-ink, var(--carbon)); }
#pop.show { display: block; animation: popin calc(220ms * var(--motion)) var(--e-enter); }
@keyframes popin { from { opacity: 0; transform: translateY(6px) scale(.98); } }
/* ON A PHONE THE CARD DOCKS. Anchored beside the word it covered the rest of
   the line, and a word without its line is the one thing this card must not
   produce. Here it sits on the bottom edge, the text stays lit above it.
   THE PRESS LAW REACHES THE PHONE TOO (70-shell.css:213-221). The docked form
   is ID-qualified, so it outranked the square cut every other #pop wears — and
   on a phone this is the most-touched surface in the reading route: rounded on
   every single lookup, on a sheet cut straight. A card is not a seal, a lamp
   or a drop of ink, so it is not round. */
#pop.docked { position: fixed; left: 8px; right: 8px; bottom: 8px; top: auto;
  width: auto; max-width: none; border-radius: 0;
  box-shadow: 0 -12px 44px rgba(0,0,0,.20), var(--shadow);
  max-height: 62vh; overflow-y: auto; }
#pop.docked.show { animation: popdock calc(240ms * var(--motion)) var(--e-enter); }
@keyframes popdock { from { opacity: 0; transform: translateY(14px); } }
#pop .pw { font-family: var(--serif); font-size: 1.5rem; font-weight: 600;
  color: var(--book-ink, var(--carbon)); }
#pop .plemma { font-size: .84rem; color: var(--ink-3); margin: 3px 0 0; }
#pop .pg { font-size: 1rem; line-height: 1.45; margin-top: 3px; }
#pop .pstage { font-size: .76rem; color: var(--ink-3); margin: 4px 0 8px; }
/* THE LINE, in English, on the card — not behind a button. Set in the reading
   face rather than the interface one, because it is the book talking and not
   the app. Rules above and below so it reads as a quotation from the page. */
#pop .pline { display: flex; align-items: flex-start; gap: 9px; margin-top: 11px;
  padding-top: 10px; border-top: 1px solid var(--line); font-family: var(--serif);
  font-size: .93rem; line-height: 1.5; color: var(--ink-2); }
#pop .pline-en { flex: 1; }
/* The two speakers lose their boxes — border, fill and letterpress shadow —
   but NOT their 44px hit area, which the stamps sheet gives every icon button
   and a thumb still needs. A bordered stamp beside a 1.5rem serif headword was
   the heaviest thing on a card whose whole subject is a word. */
#pop .pw .icon-btn, #pop .pline-say { flex: none; border: none; background: none;
  box-shadow: none; padding: 2px 4px; font-size: .82rem; opacity: .45;
  vertical-align: .18em; }
#pop .pw .icon-btn:hover, #pop .pline-say:hover { opacity: 1; box-shadow: none; }
#pop .pw .icon-btn:active, #pop .pline-say:active { box-shadow: none; }
#pop .prow { margin-top: 12px; align-items: center; }
#pop .p-more { margin-left: auto; border: none !important; background: none !important;
  box-shadow: none !important; color: var(--steel) !important;
  padding: 4px 0 !important; font-size: var(--t-00); }
/* «less ▴» turned reward yellow under the pointer — #f6c445 on the cream card,
   about 1.6:1 — and a phone keeps :hover on whatever was last touched, so on a
   phone it stayed that yellow the whole time the drawer was open. The
   reward's text ink carries the hover instead (about 5.9:1 on the galley),
   and only where a pointer can actually hover. */
#pop .p-more:hover { box-shadow: none !important; }
@media (hover: hover) { #pop .p-more:hover { color: var(--signal-ink) !important; } }
#pop .p-more:active { transform: none !important; box-shadow: none !important; }
#pop #p-drawer:not(:empty) { border-top: 1px solid var(--line); margin-top: 11px; padding-top: 9px; }
#pop .pchips { margin-top: 6px; }
/* the word whose card is open keeps its place in the line */
.w.w-open { background: rgba(var(--gold), .28); border-radius: 3px;
  box-shadow: 0 1px 0 var(--book-ink, var(--signal)); }
#pop .row { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
#pop .row button, #pop .row a { border: 1px solid var(--line); background: var(--paper); color: var(--ink);
  border-radius: 8px; padding: 5px 10px; font-size: .8rem; cursor: pointer; text-decoration: none; }
#pop .row button:hover { border-color: var(--verm); }
#pop .row button.on1 { background: rgba(var(--gold), .3); border-color: rgba(var(--gold), .8); }
#pop .row button.on2 { background: var(--verm); border-color: var(--verm); color: #f6efe1; }
#pop .forms { font-size: .78rem; color: var(--ink-3); margin-top: 8px; line-height: 1.6; }
#pop .forms b { color: var(--ink-2); font-weight: 600; }
#pop .close { position: absolute; top: 8px; right: 10px; border: none; background: none; color: var(--ink-3);
  font-size: 1.05rem; cursor: pointer; }
#pop .kbd-hint { font-size: var(--t-00); color: var(--ink-3); margin-top: 9px; }
/* «k known · l learning» is a promise about keys a phone does not have */
@media (hover: none) and (pointer: coarse) { #pop .kbd-hint { display: none; } }
#pop .sec { border-top: 1px solid var(--line); margin-top: 10px; padding-top: 8px; max-height: 170px; overflow-y: auto; font-size: .85rem; }
#pop .sec .pos { font-style: italic; color: var(--ink-2); }
#pop .sec ol { margin: 2px 0 6px; padding-left: 18px; }
kbd { background: var(--card); border: 1px solid var(--line); border-radius: 4px; padding: 0 5px; font-size: .85em; font-family: var(--sans); }
.snowflake { position: fixed; top: -4vh; z-index: 300; pointer-events: none; color: #dfe8f2;
  text-shadow: 0 0 4px rgba(255,255,255,.35); animation: fall linear forwards; }
[data-theme="paper"] .snowflake, [data-theme="sepia"] .snowflake { color: #9db2c8; text-shadow: none; }
@keyframes fall { to { transform: translateY(110vh) rotate(200deg); } }
body.motion-low .snowflake { animation-duration: 1ms !important; opacity: .0 !important; }
.set-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid var(--line); font-size: .92rem; }
.set-row:last-child { border-bottom: none; }
.set-row .lab { flex: 1; }
.set-row select, .set-row input[type=text] { border: 1px solid var(--line); background: var(--card); color: var(--ink);
  border-radius: 8px; padding: 5px 9px; font-family: inherit; }
#ov-colophon .panel { text-align: center; max-width: 430px; }
#ov-colophon .kon { font-family: var(--serif); font-size: 2.6rem; letter-spacing: .3em; color: var(--verm); margin: 6px 0; }
.greeting { font-family: var(--serif); font-size: 1.05rem; color: var(--ink-2); text-align: center; margin: -6px 0 22px; font-style: italic; }
@media (max-width: 640px) {
  .masthead h1, .masthead .as-h1 { font-size: clamp(34px, 11vw, 52px); }
  #text { font-size: var(--fs, 1.28rem); }
  /* the deep tail is the reading route's alone — see the .view ruling above */
  #v-read.view { padding-bottom: 40vh; }
}
/* ============================================================
   THE PLAY LAYER IS STRUCK. Seven saturated hues — green, gold,
   blue, purple, rose, teal, orange — were six more than this
   product owns, and the XP bar, quests, chests, combos and the
   level-up ceremony they painted are gone with them. The names
   are aliased onto the single-hue system, exactly as cobalt and
   gold were before them, so any surviving reference stays lawful.
   ============================================================ */
:root {
  --g-green: var(--accent); --g-blue: var(--accent); --g-purple: var(--accent);
  --g-green-d: var(--accent-deep);
  --g-gold: var(--signal); --g-ring: var(--rule);
}
/* the paper-burst particles survive; their palette is set by the caller */
.g-particle { position:fixed; width:9px; height:9px; border-radius:50%; z-index:320;
  pointer-events:none; will-change:transform,opacity; }

/* review feedback */
#rev-card.ok-flash { animation:okflash 620ms cubic-bezier(.2,0,0,1); }
@keyframes okflash { 0%{box-shadow:0 0 0 0 transparent} 30%{box-shadow:0 0 0 5px color-mix(in srgb, var(--signal) 35%, transparent)} 100%{box-shadow:0 0 0 0 transparent} }
#rev-card.no-flash { animation:noflash 620ms cubic-bezier(.2,0,0,1); }
@keyframes noflash { 0%{transform:translateX(0)} 22%{transform:translateX(-7px)} 44%{transform:translateX(6px)} 66%{transform:translateX(-3px)} 100%{transform:translateX(0)} }
body.motion-low #rev-card.no-flash { animation:none; }
.rev-btns .primary, .btn.primary { will-change:transform; }
.streak-chip .fl { transform-origin:50% 90%; }
.streak-chip.big .fl { font-size:1.15rem; }

/* session summary */
.sum-stat { display:flex; align-items:center; gap:12px; padding:11px 0; border-bottom:1px solid var(--line); }
.sum-stat:last-child { border-bottom:none; }
.sum-stat .s-ic { width:34px; height:34px; border-radius:10px; display:grid; place-items:center; flex:none; font-size:1rem; }
.sum-stat .s-v { margin-left:auto; font-family:var(--sans); font-weight:800; font-variant-numeric:tabular-nums; }

/* ============================================================
   THE CONTENTS — a table of instalments, not a game path.
   Number, title, a dotted leader, a word. Nothing sits on
   anything else.
   ============================================================ */
.path { position: relative; padding: 12px 0 40px; max-width: 42rem; margin: 0 auto; }
.path::before { display: none; }
.path-sect { display: block; margin: 2.4rem 0 .4rem; padding-bottom: .55rem;
  border-bottom: 1px solid var(--carbon); max-width: none; }
.path-sect .avatar { display: none; }
.path-sect .s-name { font-family: var(--serif); font-size: 1.45rem; font-weight: 600; line-height: 1.15; }
.path-sect .s-meta { font-family: var(--bill); font-size: .7rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--steel); margin-top: 5px; }
.path-row { display: block; margin: 0; }
.path-row.has-current { margin-top: 0; }
.node { position: relative; transform: none; }
.node-btn {
  width: 100%; height: auto; min-height: 0; border-radius: 0; cursor: pointer;
  display: grid; grid-template-columns: 3.4rem minmax(0, auto) 1fr auto;
  align-items: baseline; gap: .35rem .75rem;
  padding: .8rem 0; margin: 0;
  border: none; border-bottom: 1px solid var(--rule);
  background: transparent; box-shadow: none; color: var(--carbon);
  text-align: left; font-size: inherit;
}
.node-btn:hover { background: transparent; box-shadow: none; }
.node-btn:active { transform: none; box-shadow: none; }
.node-btn .n-no {
  position: static; font-family: var(--mono); font-weight: 400; font-style: normal;
  font-size: .78rem; letter-spacing: .03em; color: var(--steel);
}
.node-btn .n-title { font-family: var(--serif); font-size: 1.12rem; line-height: 1.3; }
.node-btn .n-lead { display: block; height: 0; border-bottom: 1px dotted var(--rule); align-self: center; min-width: 1.25rem; }
.node-btn .n-state {
  font-family: var(--bill); font-size: var(--t-00); letter-spacing: .16em;
  text-transform: uppercase; color: var(--steel); white-space: nowrap;
}
.node.current .node-btn { width: 100%; height: auto; font-size: inherit;
  border-color: transparent; border-bottom: 1px solid var(--carbon); background: transparent; color: var(--carbon); }
.node.current .n-title { font-size: 1.45rem; font-weight: 600; }
.node.current .n-no, .node.current .n-state { color: var(--carbon); }
.node.current .n-state { font-size: 1.15rem; letter-spacing: 0; text-transform: none; }
.node.done .node-btn { color: var(--steel); }
.node.done .n-title { font-style: italic; }
.node.locked .node-btn { color: var(--steel); box-shadow: none; }
.node .bubble, .node .halo, .node-mate, .node .ring-pulse, .node .n-label { display: none; }
/* ============================================================
   MOTION SEQUENCE SUPPORT — elements GSAP needs to own directly
   (it cannot animate a pseudo-element), plus the resting states
   each sequence lands on.
   ============================================================ */
/* wax seal: GSAP owns the transform during the stamp, and the deboss remains */
.seal-hold.stamping .seal-body { animation: none !important; transition: none; }
.seal-hold.stamped .seal-body {
  transform: rotate(11deg); animation: none !important;
  box-shadow: inset 0 2px 5px rgba(255,255,255,.28), inset 0 -3px 6px rgba(0,0,0,.38),
              inset 0 0 12px rgba(0,0,0,.22); }
.seal-hold .wax-rim { position: absolute; inset: -6px; border-radius: 50%; pointer-events: none;
  opacity: 0; transform: scale(.55); filter: blur(1px);
  background: radial-gradient(circle, var(--wax) 42%,
    color-mix(in srgb, var(--wax) 30%, transparent) 68%, transparent 72%); }
/* the wet edge of the ink seep on the word card */
.pop-bleed { position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  opacity: 0; mix-blend-mode: multiply; }
[data-theme="dark"] .pop-bleed { mix-blend-mode: screen; }
/* the sealed node's check draws itself; the wipe retracts the old colour */
.node-btn .tick { width: 34px; height: 34px; overflow: visible; pointer-events: none; }
.node-btn .tick path { stroke: currentColor; }
.node-btn .wipe { position: absolute; inset: 0; border-radius: 50%; overflow: hidden; pointer-events: none; }
.node-btn .wipe > i { position: absolute; inset: 0; transform-origin: 50% 0%;
  background: radial-gradient(circle at 34% 30%, color-mix(in srgb, var(--wc) 78%, #fff), var(--wc) 62%); }
.node .ring-pulse { position: absolute; inset: -9px; border-radius: 50%;
  border: 2px solid var(--signal); opacity: 0; pointer-events: none; }
/* the drop cap becomes a real element so it can be inked in */
#text p.dc-on .dc { float: left; color: var(--verm); font-weight: 700; font-size: 3.1em;
  line-height: .84; padding: .04em .12em 0 0; transform-origin: 0 85%; }
/* the home rule is drawn, not bordered */
#v-home .rule.rule-draw { border-top: none; height: 2px; }
#v-home .rule-draw .rule-svg { display: block; width: 100%; height: 2px; overflow: visible; }
#v-home .rule-draw .rule-line { stroke: var(--line); stroke-width: 1; vector-effect: non-scaling-stroke; }

/* ---- scene epigraph & first read banner ----
   The slip at the scene's door is the RESIDENT'S: its figure stands beside
   its own lines at every width — below 1000px this is the only place the
   book's companion exists at all, so the slip is the introduction. The left
   rule takes the book's own ink; the figure is mounted live and stood still. */
.scene-epigraph {
  display: flex; align-items: flex-start; gap: 14px;
  background: var(--galley); border: 1px solid var(--rule);
  border-left: 3px solid var(--book-ink, var(--signal));
  padding: 12px 16px; margin: 14px 0 22px; border-radius: 4px; box-shadow: var(--lift-sm);
  transition: opacity 280ms ease, transform 280ms ease; cursor: pointer;
}
.scene-epigraph.fading { opacity: 0; transform: translateY(-8px); pointer-events: none; }
/* THE SLIP ARRIVES WITH THE OVERTURE, NOT BESIDE IT. The old .guide card
   carried its own entrance and playSceneOpen leaves #guide alone for exactly
   that reason — but .scene-epigraph never inherited one, so the slip (and
   the resident standing on it) popped in fully-formed while the title was
   still settling: there, not-there, there. One settle, delayed to fall in
   sequence after the subtitle, `both` so nothing shows before its cue. */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .scene-epigraph {
    animation: epigin calc(560ms * var(--motion, 1)) var(--e-std) both;
    animation-delay: calc(300ms * var(--motion, 1));
  }
}
@keyframes epigin { from { opacity: 0; transform: translateY(9px); } }
.scene-epigraph .ep-fleuron { font-size: 1.25rem; color: var(--signal-ink); line-height: 1; flex: none; margin-top: 2px; }
.scene-epigraph .ep-fig { flex: none; width: 58px; height: 58px; align-self: center;
  cursor: pointer; filter: drop-shadow(0 3px 6px rgba(20,30,42,.16)); }
.scene-epigraph .ep-fig svg { display: block; overflow: visible; }
@media (max-width: 560px) { .scene-epigraph .ep-fig { width: 48px; height: 48px; } }
.scene-epigraph .ep-body { flex: 1; }
.scene-epigraph .ep-text { font-family: var(--serif); font-style: italic; font-size: .96rem; line-height: 1.5; color: var(--carbon); margin: 0 0 4px; }
.scene-epigraph .ep-meta { font-family: var(--bill); font-size: .75rem; text-transform: uppercase; letter-spacing: .12em; color: var(--steel); }
.scene-epigraph .ep-btn {
  flex: none; font-family: var(--bill); font-size: .78rem; text-transform: uppercase;
  letter-spacing: .1em; padding: 4px 10px; background: var(--sheet);
  border: 1px solid var(--rule); color: var(--carbon); cursor: pointer; border-radius: 3px; align-self: center;
}
.scene-epigraph .ep-btn:hover { background: var(--carbon); color: var(--sheet); }

.first-read-banner {
  background: var(--galley); border: 2px solid var(--signal); padding: 11px 16px;
  margin: 10px 0 14px; text-align: center; border-radius: 4px; box-shadow: var(--lift);
  animation: viewin 400ms var(--e-enter) both;
}
.first-read-banner h2 { font-size: 1.06rem; margin: 0 0 3px; }
.first-read-banner p  { font-size: .82rem; }
/* THE GATE IS HEIGHT, NOT WIDTH.
   All of this was written behind max-width: 560px, and the thing it is
   fighting — furniture standing between a stranger and the first Russian
   sentence — is measured in pixels down the page. So a 1024x768 laptop window
   held FEWER words of Russian in its first screen than a 414x896 phone: above
   the width breakpoint the epigraph came back and the margins went to full.
   Either condition now applies the economies. */
@media (max-width: 560px), (max-height: 800px) {
  .first-read-banner { padding: 9px 12px; margin: 8px 0 12px; }
  .first-read-banner h2 { font-size: .98rem; }
  .first-read-banner p  { font-size: .78rem; }
  /* The scene head is the product's one claim to being a book rather than an
     app, so the Cyrillic title and the ornament stay. The reading advice and
     the epigraph are not the ceremony and they are 100px of a 844px screen. */
  .scene-head { margin: 10px 0 14px; }
  .scene-head h2 { font-size: 1.5rem; margin: 6px 0 2px; }
  #read-wrap.first-read .scene-head .sub { display: none; }
  /* AND THE COUNSEL. wisdomFor() writes two or three italic lines about the
     words you meet in the sentences you love — good writing, and on a first
     read it stands between somebody who has never seen this product and the
     only thing that will persuade them. It is there on every later visit. */
  #read-wrap.first-read .scene-head .margin-note { display: none; }
}
.first-read-banner .fr-tag {
  display: inline-block; font-family: var(--bill); font-size: .76rem; letter-spacing: .18em;
  text-transform: uppercase; color: var(--signal-ink); font-weight: 700; margin-bottom: 6px;
}
.first-read-banner h2 { font-family: var(--serif); font-size: 1.35rem; font-weight: 600; color: var(--carbon); margin: 0 0 8px; line-height: 1.3; }
.first-read-banner p { font-family: var(--sans); font-size: .9rem; color: var(--steel); margin: 0; }
/* the first goal, in view: three dots of ink fill as the lines are read */
.first-read-banner .fr-meter { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 13px; }
.first-read-banner .fr-meter i { width: 11px; height: 11px; border-radius: 50%; flex: none;
  border: 1.5px solid var(--signal-ink); background: transparent;
  transition: background .4s var(--e-std), transform .4s var(--e-std); }
.first-read-banner .fr-meter i.lit { background: var(--signal-ink); transform: scale(1.18); }
.first-read-banner .fr-cap { font-family: var(--serif); font-style: italic; font-size: .8rem; color: var(--steel); margin-left: 5px; }
/* the stamped mark on the ceremony's second act */
.cer-stamped { font-size: 2.2rem; color: var(--signal-ink); text-align: center; margin: 4px 0 8px;
  animation: stamp-land calc(500ms * var(--motion)) var(--e-enter); }
@keyframes stamp-land { 0% { opacity: 0; transform: scale(2.2) rotate(-8deg); } 60% { transform: scale(.94); } 100% { opacity: 1; transform: scale(1); } }
/* THE MARK IS NOT THE MOTION. This glow ran 1.8s forever on the first word of
   the running prose — hardcoded, so no tier could reach it: full speed at calm,
   at low, and under prefers-reduced-motion, inside the one route that stands
   its motion down entirely. The invitation is the --signal underline, which
   survives every tier; the glow is now the top tier's ornament only, and it
   breathes on --motion like everything else. Its peach was a raw hex belonging
   to no room — it is the signal, mixed. */
.w.first-word-pulse {
  position: relative; font-weight: 600; color: var(--signal-ink) !important;
  text-decoration: underline; text-decoration-color: var(--signal); text-underline-offset: 4px;
}
body.motion-full .w.first-word-pulse {
  animation: firstpulse calc(1800ms * var(--motion)) ease-in-out infinite alternate;
}
@keyframes firstpulse {
  0% { text-shadow: 0 0 0 transparent; }
  100% { text-shadow: 0 0 10px color-mix(in srgb, var(--signal) 70%, transparent); }
}
.w.primed {
  text-decoration: underline; text-decoration-color: var(--signal);
  text-underline-offset: 3px; text-decoration-thickness: 1.5px;
}

/* ---- the guide: a character steps in, speaks, and withdraws ---- */
.guide { display:flex; align-items:flex-end; gap:14px; margin:0 0 26px; cursor:pointer;
  animation:guidein 620ms var(--e-enter) both; }
@keyframes guidein { from{opacity:0; transform:translateY(16px)} }
.guide .g-who { width:76px; height:76px; flex:none; filter:drop-shadow(0 5px 10px rgba(20,30,42,.22)); }
.guide .g-who svg { display:block; overflow:visible; }
.guide .g-bubble { position:relative; flex:1; background:var(--paper-0); border:1.5px solid var(--line-2);
  border-radius:14px; padding:14px 18px; font-family:var(--serif); font-size:1.04rem; line-height:1.5;
  color:var(--ink-1); box-shadow:0 4px 0 rgba(20,30,42,.05); min-height:56px; }
.guide .g-bubble::before { content:''; position:absolute; left:-10px; bottom:16px;
  border:10px solid transparent; border-right-color:var(--line-2); }
.guide .g-bubble::after { content:''; position:absolute; left:-8px; bottom:16px;
  border:10px solid transparent; border-right-color:var(--paper-0); }
.guide .g-next { position:absolute; right:12px; bottom:8px; font-family:var(--sans); font-size:var(--t-00);
  letter-spacing:.14em; text-transform:uppercase; color:var(--ink-3p); }
.guide .g-next::after { content:' ▸'; }
@media all {
  body.motion-full .guide .g-next { animation:nextpulse 1.8s ease-in-out infinite; }
  @keyframes nextpulse { 0%,100%{opacity:.5} 50%{opacity:1} }
}
.caret { display:inline-block; width:2px; height:1em; background:var(--accent); vertical-align:-2px;
  margin-left:2px; animation:caret 900ms steps(2) infinite; }
@keyframes caret { 0%,100%{opacity:1} 50%{opacity:0} }
/* THE MARGIN COMPANION IS RETIRED (owner's ruling, 2026-08-22 night): while
   prose is on screen the page is clean — nothing stands beside it, not even
   a still friend. The resident lives at the thresholds: the epigraph slip
   at the scene's door (.scene-epigraph .ep-fig) and the road-door card at
   its end. placeMate no longer creates .margin-mate; show() still sweeps
   any stale one from an old session. */

/* ---- a character lives in the word card and reacts to what you decide ---- */
.pop-mate { position:absolute; top:-16px; right:-8px; width:52px; height:52px; cursor:pointer;
  filter:drop-shadow(0 3px 6px rgba(20,30,42,.22)); z-index:2; }
.pop-mate svg { display:block; overflow:visible; }
.pop-say { position:absolute; top:-30px; right:44px; white-space:nowrap; background:var(--ink-1);
  color:var(--paper-0); font-family:var(--serif); font-style:italic; font-size:.76rem;
  padding:5px 10px; border-radius:9px; opacity:0; pointer-events:none; z-index:3;
  transition:opacity var(--d-m) var(--e-std), transform var(--d-m) var(--e-std); transform:translateY(4px); }
.pop-say.show { opacity:1; transform:translateY(0); }

/* Клякса lives on the path, beside the node you are on */
.klyaksa { width:74px; height:74px; cursor:pointer; user-select:none; -webkit-user-select:none;
  filter:drop-shadow(0 4px 8px rgba(20,30,42,.22)); }
.klyaksa svg { display:block; overflow:visible; }
.node-mate { position:absolute; left:100%; top:50%; transform:translateY(-50%); margin-left:6px; }
.node-mate.flip { left:auto; right:100%; margin-left:0; margin-right:6px; }
.k-say { position:absolute; left:50%; bottom:100%; transform:translate(-50%,-6px); white-space:nowrap;
  background:var(--ink-1); color:var(--paper-0); font-family:var(--serif); font-style:italic;
  font-size:.78rem; padding:5px 11px; border-radius:10px; opacity:0; pointer-events:none;
  transition:opacity var(--d-m) var(--e-std), transform var(--d-m) var(--e-std); }
.k-say.show { opacity:1; transform:translate(-50%,-11px); }
/* the practice header stands at the very top of the screen: a caption above
   the figure is clipped by the masthead, one below it lands on the card's dark
   crown. So the quill speaks SIDEWAYS, into the open paper to its left —
   settling a hair toward the figure, as the charter's ink would. */
#v-review .k-say { bottom:auto; left:auto; top:50%; right:calc(100% + 8px);
  transform:translate(6px,-50%); z-index:6; }
#v-review .k-say.show { transform:translate(0,-50%); }
/* No connecting lines: with a serpentine offset they never align honestly,
   and the wave itself already reads as a road. */
.node-pop { animation:nodepop 620ms var(--e-snap); }
@keyframes nodepop { 0%{transform:scale(1)} 30%{transform:scale(1.22) rotate(-6deg)} 60%{transform:scale(.96)} 100%{transform:scale(1)} }
/* a rest node between books — a neutral rule, never gold */
.node.reward .node-btn { --nc:var(--rule); }

/* ============================================================
   MICRO-INTERACTIONS — the hundred small moments.
   Rule: transform/opacity only; every duration rides --motion.
   ============================================================ */
/* --- nav: the active tab draws its own underline --- */
.nav-tabs button { position:relative; }
.nav-tabs button::after { content:''; position:absolute; left:9px; right:9px; bottom:2px; height:2px;
  background:var(--accent); border-radius:2px; transform:scaleX(0); transform-origin:50% 50%;
  transition:transform var(--d-m) var(--e-enter); }
.nav-tabs button.active::after { transform:scaleX(1); }

/* --- cards lift and settle --- */
.card { transition:transform var(--d-m) var(--e-std), box-shadow var(--d-m) var(--e-std); }
.issue, .book, .glesson-card, .find-card { will-change:transform; }
.issue:active, .book:active, .glesson-card:active { transform:scale(.985); }
/* a book spine eases out of the shelf like a real one */
.book { transform-origin:0 50%; }
.book:hover { transform:translateY(-3px) rotate(-.5deg); }
.book .b-title { transition:color var(--d-s) var(--e-std); }
.book:hover .b-title { color:var(--accent); }

/* --- icon buttons rotate a touch when pressed --- */
.icon-btn { transition:border-color var(--d-s) var(--e-std), background var(--d-s) var(--e-std),
  transform var(--d-xs) var(--e-snap); }
.icon-btn:active { transform:rotate(-6deg) scale(.94); }
#btn-settings:hover { transform:rotate(35deg); }

/* --- theme dots bloom --- */
.theme-dots button { transition:transform var(--d-s) var(--e-snap), border-color var(--d-s) var(--e-std),
  box-shadow var(--d-s) var(--e-std); }
.theme-dots button:hover { transform:scale(1.22); }
.theme-dots button.on { transform:scale(1.1); }

/* --- the chip reads density, quietly; the flame keeps the room ink and
       never grows hotter — heat tiers were streak language --- */
.streak-chip { transition:border-color var(--d-m) var(--e-std); }
.streak-chip:hover { border-color:var(--line-2); }

/* --- quiz options: press, then a colour sweep on the right one --- */
.quiz-opt { transition:border-color var(--d-s) var(--e-std), background var(--d-m) var(--e-std),
  transform var(--d-xs) var(--e-std); position:relative; overflow:hidden; }
.quiz-opt:active { transform:scale(.99); }
.quiz-opt.right::before { content:''; position:absolute; inset:0; background:var(--ok); opacity:.16;
  transform-origin:0 50%; animation:sweep 500ms var(--e-enter); }
@keyframes sweep { from{transform:scaleX(0)} to{transform:scaleX(1)} }

/* --- the typed answer field reacts --- */
.type-input { transition:border-color var(--d-s) var(--e-std), transform var(--d-xs) var(--e-std); }
.type-input:focus { border-color:var(--accent); }
.type-input.good { border-color:var(--ok); }
.type-input.bad { border-color:var(--err); animation:noflash 500ms var(--e-std); }

/* --- letter tiles pop in, and press like keys --- */
.ltile { animation:tilein 300ms var(--e-enter) both; }
.tilesrow .ltile:nth-child(1){animation-delay:0ms}.tilesrow .ltile:nth-child(2){animation-delay:25ms}
.tilesrow .ltile:nth-child(3){animation-delay:50ms}.tilesrow .ltile:nth-child(4){animation-delay:75ms}
.tilesrow .ltile:nth-child(5){animation-delay:100ms}.tilesrow .ltile:nth-child(6){animation-delay:125ms}
.tilesrow .ltile:nth-child(7){animation-delay:150ms}.tilesrow .ltile:nth-child(8){animation-delay:175ms}
.tilesrow .ltile:nth-child(9){animation-delay:200ms}.tilesrow .ltile:nth-child(n+10){animation-delay:225ms}
@keyframes tilein { from{opacity:0; transform:translateY(8px) scale(.85)} }
.ltile:active { transform:translateY(2px) scale(.94); }

/* --- reading: a read sentence leaves the page alone --- */
.sent { position:relative; }
/* A read sentence used to carry a 3px margin tick as an absolutely-positioned
   ::before. But .sent is an INLINE element, so its containing block is the
   bounding box of its first and last line fragments — which for any sentence
   that does not start a line puts the tick in the middle of the paragraph,
   sometimes inside the preceding word. It was drawing bars into the prose.

   That was replaced by a faint fill, and the fill was worse in a way that was
   invisible until the page became warm and the chrome became cool: every read
   sentence turned into a hard-edged rectangle, so a page of Chekhov rendered
   as a mosaic of grey blocks with the prose inside them. Softening it to 4%
   did not help, because the defect was the geometry and not the strength — a
   rectangle at 4% is still a rectangle.

   So it paints nothing. How far you have read is already stated exactly, in
   words, at the foot of the page ("28 sentences left · ≈3 min"), and a second
   copy of that fact is not worth the appearance of the thing you came to read.
   The sentence stays interactive; only the paint is gone. */
.sent.done { }

/* the read-on control that replaces the dead "instalment read" label */
.read-on { font-family:var(--sans); font-size:var(--t-0); letter-spacing:.06em;
  border:1px solid var(--minium); background:var(--gesso); color:var(--minium-t);
  padding:5px 13px; cursor:pointer; white-space:nowrap;
  clip-path:polygon(0 4px,4px 0,calc(100% - 4px) 0,100% 4px,100% 100%,0 100%);
  box-shadow:0 3px 0 var(--foot); }
.read-on:active { transform:translateY(3px); box-shadow:0 0 0 var(--foot); }

/* --- popup rows stagger in --- */
#pop .row button { animation:rowin 260ms var(--e-enter) both; }
#pop .row:nth-of-type(1) button:nth-child(1){animation-delay:20ms}
#pop .row:nth-of-type(1) button:nth-child(2){animation-delay:45ms}
#pop .row:nth-of-type(1) button:nth-child(3){animation-delay:70ms}
#pop .row:nth-of-type(1) button:nth-child(4){animation-delay:95ms}
@keyframes rowin { from{opacity:0; transform:translateY(5px)} }

/* --- toast arrives with a nudge --- */
.toast.show { animation:toastin 420ms var(--e-snap); }
@keyframes toastin { 0%{transform:translateY(20px) scale(.94)} 60%{transform:translateY(-3px) scale(1.02)} 100%{transform:none} }

/* --- meters shimmer once when they grow --- */
.meter > i.retr { position:relative; overflow:hidden; }
.meter.grew > i.retr::after { content:''; position:absolute; inset:0;
  background:linear-gradient(100deg, transparent 25%, rgba(255,255,255,.7) 50%, transparent 75%);
  transform:translateX(-100%); animation:metershine 800ms var(--e-std); }
@keyframes metershine { 0%{opacity:1; transform:translateX(-100%)} 100%{opacity:0; transform:translateX(180%)} }

/* --- the seal breathes while it waits to be pressed --- */
.seal-hold { transition:transform var(--d-m) var(--e-std); }
.seal-hold:hover { transform:scale(1.04); }
@media all {
  body.motion-full .seal-hold:not(.pressing) .seal-body { animation:sealbreath 3.4s ease-in-out infinite; }
  @keyframes sealbreath { 0%,100%{transform:scale(1)} 50%{transform:scale(1.035)} }
}

/* --- view arrival: content settles rather than snapping ---
   THE ONE ANIMATION EVERY READER SEES, and the one that never read the dial.
   420ms plus up to 110ms of stagger, hardcoded, on every element of every
   route: so "calm" — which halves the whole app — was not calm on the only
   motion a reader meets at every door. The low tier stood it down below; the
   middle tier had nothing to hold. It rides --motion now, like the rest. */
.view.show > * { animation:viewin calc(420ms * var(--motion)) var(--e-enter) both; }
.view.show > *:nth-child(1){animation-delay:0ms}.view.show > *:nth-child(2){animation-delay:calc(40ms * var(--motion))}
.view.show > *:nth-child(3){animation-delay:calc(80ms * var(--motion))}.view.show > *:nth-child(n+4){animation-delay:calc(110ms * var(--motion))}
@keyframes viewin { from{opacity:0; transform:translateY(7px)} }
body.motion-low .view.show > * { animation:none; }

/* --- word popup close spins away --- */
#pop .close { transition:transform var(--d-s) var(--e-snap), color var(--d-s) var(--e-std); }
#pop .close:hover { transform:rotate(90deg); color:var(--accent); }

/* --- shelf spines: a colour per author, not per level --- */
.book[data-a="chekhov"] { --spine: var(--g-green); }
.book[data-a="gogol"]   { --spine: var(--accent); }
.book[data-a="turgenev"]{ --spine: var(--g-blue); }
.book[data-a="dostoevsky"]{ --spine: var(--g-purple); }
.book { background:linear-gradient(90deg, var(--spine, var(--accent)) 0 6px, var(--card) 6px); }
.book .lvl.l1 { border-color:var(--g-green); color:var(--g-green); }

/* --- progress numbers settle when they change: one deceleration, no bounce --- */
.tile .v.ticked { animation:ticksettle 420ms var(--e-std) both; }
@keyframes ticksettle { from{transform:scale(1.06)} to{transform:scale(1)} }

/* ============ LtL wordmark: LANGUAGE / through / LITERATURE ============ */
.wordmark { text-align: center; user-select: none; }
.wordmark .wm-l { font-family: var(--serif); letter-spacing: .34em; font-size: .92rem; color: var(--ink-1); text-transform: uppercase; }
.wordmark .wm-t { font-family: var(--serif); font-style: italic; font-size: 1.05rem; color: var(--signal);
  margin: 1px 0; letter-spacing: .04em; }
.wordmark.small { display: inline-flex; align-items: baseline; gap: .45em; }
.wordmark.small .wm-l { font-size: var(--t-00); letter-spacing: .22em; }
.wordmark.small .wm-t { font-size: .8rem; }
.room-line { font-size: var(--t-00); letter-spacing: .3em; color: var(--ink-3p); text-transform: uppercase; margin-top: 6px; }
/* ============ the living flame (silent companion; no face, ever) ============ */
#companion { position: fixed; right: 14px; bottom: 84px; z-index: 25; width: 34px; pointer-events: none;
  opacity: .9; transition: opacity var(--d-l) var(--e-std), transform 200ms var(--e-std); }
#companion .candlestick { width: 10px; height: 26px; margin: 0 auto; background: linear-gradient(180deg, #e8dcc0, #cbb78f);
  border-radius: 3px 3px 2px 2px; position: relative; }
[data-theme="dark"] #companion .candlestick { background: linear-gradient(180deg, #d8c9a6, #a08c64); }
#companion .bead { position: absolute; left: -2px; width: 5px; height: 7px; border-radius: 40% 40% 50% 50%;
  background: #efe4c8; opacity: 0; }
#companion .flame { width: 12px; height: 18px; margin: 0 auto -2px; border-radius: 50% 50% 46% 54% / 62% 62% 38% 38%;
  background: radial-gradient(50% 65% at 50% 72%, #ffdf9e 0%, #f0a94e 55%, rgba(216,103,75,.55) 85%, transparent 100%);
  filter: drop-shadow(0 0 7px rgba(255,180,80,.55)); transform-origin: 50% 92%;
  transition: transform 200ms var(--e-std), opacity var(--d-l) var(--e-std), filter var(--d-l) var(--e-std); }
@media all {
  body.motion-full #companion .flame { animation: flame-sway 4s ease-in-out infinite alternate; }
  @keyframes flame-sway { 0% { transform: skewX(-2deg) scaleY(1); } 100% { transform: skewX(2.4deg) scaleY(1.04); } }
}
#companion[data-state="attending"] .flame { filter: drop-shadow(0 0 9px rgba(255,180,80,.7)); }
#companion[data-state="listening"] .flame { transform: scaleY(1.12); filter: drop-shadow(0 0 12px rgba(255,190,90,.85)); }
#companion[data-state="pleased"] .flame { animation: flame-flare calc(350ms * var(--motion)) var(--e-snap); }
@keyframes flame-flare { 0%{transform:scale(1)} 45%{transform:scale(1.35) translateY(-2px)} 100%{transform:scale(1)} }
#companion[data-state="guttering"] .flame { animation: flame-gutter calc(300ms * var(--motion)) var(--e-std); }
@keyframes flame-gutter { 0%,100%{opacity:1; transform:scaleY(1)} 40%{opacity:.45; transform:scaleY(.6) skewX(6deg)} 70%{opacity:.8; transform:scaleY(.85) skewX(-3deg)} }
#companion[data-state="ember"] .flame { transform: scaleY(.35) scaleX(.7); opacity: .55; filter: drop-shadow(0 0 3px rgba(255,150,60,.4)); }
#companion[data-state="asleep"] { opacity: .25; }
body.motion-low #companion { display: none; }
@media (max-width: 700px) { #companion { display: none; } }
/* ============ ink bloom (the signature gesture: answers seep in) ============ */
#tip.show { animation: inkbloom calc(220ms * var(--motion)) var(--e-enter); }
#pop.show { animation: inkbloom calc(220ms * var(--motion)) var(--e-enter); }
/* repeat taps: the card is furniture now, not an arrival — no ceremony */
#pop.show.quick { animation: none; }
@keyframes inkbloom { 0% { opacity: 0; clip-path: circle(6% at var(--bx, 50%) var(--by, 0%)); filter: blur(1.5px); }
  100% { opacity: 1; clip-path: circle(140% at var(--bx, 50%) var(--by, 0%)); filter: blur(0); } }
.tr-reveal { animation: seep calc(300ms * var(--motion)) var(--e-enter); transform-origin: 0 0; }
@keyframes seep { 0% { opacity: 0; transform: scaleY(.6); filter: blur(2px); } 100% { opacity: 1; transform: scaleY(1); filter: blur(0); } }
/* word graduation: the underline burns away as an ember */
.w.graduate { animation: graduate calc(900ms * var(--motion)) var(--e-std); }
@keyframes graduate {
  0%   { background: rgba(var(--gold), .4); box-shadow: 0 2px 0 var(--accent); }
  45%  { background: rgba(216,103,75,.35); box-shadow: 0 2px 0 rgba(216,103,75,.9); color: var(--accent); }
  100% { background: transparent; box-shadow: none; }
}
/* seal deboss persists; stamp gets the hand-press 1° wobble */
.seal-stamp { animation: sealstamp calc(900ms * var(--motion)) var(--e-std); }
.deboss { box-shadow: inset 0 2px 5px rgba(255,255,255,.28), inset 0 -3px 6px rgba(0,0,0,.38), inset 0 0 12px rgba(0,0,0,.22); }
/* session close: ribbon + snuff */
#close-ritual { position: fixed; inset: 0; z-index: 260; pointer-events: none; display: none; }
#close-ritual.show { display: block; }
#close-ritual .ribbon { position: absolute; top: -20%; left: 50%; width: 26px; height: 0;
  background: linear-gradient(90deg, var(--accent-deep), var(--accent) 50%, var(--accent-deep));
  transform: translateX(-50%); animation: ribbondrop calc(900ms * var(--motion)) var(--e-enter) forwards; }
#close-ritual .ribbon::after { content: ''; position: absolute; bottom: -12px; left: 0; border: 13px solid var(--accent);
  border-bottom-color: transparent; }
@keyframes ribbondrop { to { height: 68%; } }
.smoke { position: fixed; width: 8px; height: 8px; border-radius: 50%; background: rgba(160,150,130,.5);
  filter: blur(4px); pointer-events: none; z-index: 261; }
/* ============ reader's ticket & ex-libris ============ */
.ticket { border: 1.5px solid var(--line-2); border-radius: 10px; background: var(--paper-0);
  padding: 16px 18px; position: relative; overflow: hidden; font-family: var(--serif); }
.ticket::before { content: ''; position: absolute; inset: 4px; border: 1px solid var(--line); border-radius: 7px; pointer-events: none; }
.ticket .tk-head { text-align: center; letter-spacing: .3em; font-size: var(--t-00); color: var(--ink-2p); text-transform: uppercase; }
.ticket .tk-name { text-align: center; font-size: 1.3rem; font-weight: 600; margin: 8px 0 2px; }
.ticket .tk-sub { text-align: center; font-size: .74rem; color: var(--ink-2p); font-style: italic; }
.ticket .tk-seal { position: absolute; right: 12px; bottom: 10px; width: 38px; height: 38px; transform: rotate(9deg); font-size: .8rem; }
.exlibris { width: 92px; height: 92px; margin: 0 auto; border: 1.5px solid var(--ink-2p); border-radius: 6px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; font-family: var(--serif);
  background: var(--paper-0); cursor: pointer; transition: border-color var(--d-s) var(--e-std), transform var(--d-s) var(--e-std); }
.exlibris:hover { border-color: var(--accent); transform: translateY(-2px); }
.exlibris.sel { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-tint); }
.exlibris .xl-orn { font-size: 1.3rem; color: var(--accent); }
.exlibris .xl-mono { font-size: 1.25rem; font-weight: 700; letter-spacing: .06em; }
.exlibris .xl-cap { font-size: var(--t-00); letter-spacing: .22em; color: var(--ink-3p); text-transform: uppercase; margin-top: 3px; }
/* the enrolment heading: its own treatment, in the room's voice. It was a
   bare <h3>, which took the masthead's full-bleed reverse bar and dropped it
   across the wordmark. */
.cer-title { font-family: var(--serif); font-size: 1.3rem; line-height: 1.3; text-align: center;
  color: var(--ink); margin: 2px 0 10px; text-transform: none; letter-spacing: 0;
  background: none; padding: 0; }
/* the card needs room to breathe: the ornament, the monogram once it exists,
   and the caption were competing inside 92px */
.exlibris { min-height: 96px; height: auto; padding: 10px 6px; gap: 2px; }
.exlibris .xl-mono:empty { display: none; }
/* coach mark for opening-into-the-text. z 265: above the lesson stage (240)
   and toasts (250) — at 120 a coach fired mid-lesson was mounted but
   invisible — and below the overlay (290). */
.coach { position: fixed; bottom: 92px; left: 50%; transform: translateX(-50%); z-index: 265;
  background: var(--ink-1); color: var(--paper-0); border-radius: 12px; padding: 10px 18px; font-size: .95rem;
  font-family: var(--serif); font-style: italic; box-shadow: var(--shadow); pointer-events: none;
  animation: coachin calc(700ms * var(--motion)) var(--e-enter); }
@keyframes coachin { 0% { opacity: 0; transform: translate(-50%, 10px); } 100% { opacity: 1; transform: translate(-50%, 0); } }
/* found object between the pages */
.found-object { max-width: 240px; margin: 18px auto; text-align: center; font-family: var(--serif);
  padding: 14px; border: 1px dashed var(--line-2); border-radius: 8px; background: var(--paper-0);
  transform: rotate(-1.4deg); animation: coachin calc(700ms * var(--motion)) var(--e-enter); }
.found-object .fo-what { font-size: 1.6rem; }
.found-object .fo-note { font-size: .78rem; color: var(--ink-2p); font-style: italic; margin-top: 6px; line-height: 1.5; }
/* patina: foxing on well-read issue cards */
.issue.foxed::after { content: ''; position: absolute; inset: 0; border-radius: 12px; pointer-events: none;
  background: radial-gradient(26px 20px at 100% 0%, rgba(150,107,39,.12), transparent 70%),
              radial-gradient(20px 16px at 0% 100%, rgba(150,107,39,.09), transparent 70%); }
/* marginal literary-calendar note */
.almanac { font-size: .76rem; color: var(--ink-2p); font-style: italic; font-family: var(--serif);
  text-align: center; margin: 4px 0 0; }
.almanac .al-orn { color: var(--signal); font-style: normal; }
/* stagger entrances (lists arriving like set type) */
.stagger-in > * { animation: setline calc(400ms * var(--motion)) var(--e-enter) both; }
.stagger-in > *:nth-child(1) { animation-delay: 0ms; } .stagger-in > *:nth-child(2) { animation-delay: 30ms; }
.stagger-in > *:nth-child(3) { animation-delay: 60ms; } .stagger-in > *:nth-child(4) { animation-delay: 90ms; }
.stagger-in > *:nth-child(5) { animation-delay: 120ms; } .stagger-in > *:nth-child(6) { animation-delay: 150ms; }
@keyframes setline { 0% { opacity: 0; transform: translateY(6px); } 100% { opacity: 1; transform: none; } }
/* colophon / manifesto page */
.manifesto { max-width: 560px; margin: 0 auto; font-family: var(--serif); line-height: 1.7; font-size: .98rem; }
.manifesto h3 { text-align: center; letter-spacing: .2em; }
.manifesto .rule-orn { text-align: center; color: var(--signal); margin: 14px 0; }

/* ============ the companion layer ============ */
/* STRUCTURAL CONTAINMENT GUARD (2026-08-22, cascade concordat clause e).
   The exercise mate's medallion is an absolutely-positioned ::before with
   inset: 0, drawn by the arcade sheet. Any .gx-mate that is not itself a
   positioned element hands that disc to the nearest positioned ancestor —
   once, that was the stage, and the medallion painted a screen-filling
   gold disc that also ate every click. This floor makes the mate's own box
   the disc's containing block wherever the mate appears, whatever the skin
   above does or fails to do: higher-specificity rules may RE-position it
   (the arcade pins it fixed), but no missing rule can ever leave it static.
   The bug class is unwritable. Asserted by tests/cascade.test.js. */
.gx-mate { position: relative; }

/* One friend, one voice, in the same place on every screen. Presence is
   mostly silence — the strip is hidden entirely when there is nothing
   specific to say, which is most of the time. */
.friend-strip { display:flex; align-items:center; gap:13px; max-width:640px; margin:0 auto 18px;
  animation:frIn 720ms cubic-bezier(.2,.9,.3,1) both; }
@keyframes frIn { from{opacity:0; transform:translateY(9px)} to{opacity:1; transform:none} }
.friend-strip .fr-who { width:52px; height:52px; flex:none; filter:drop-shadow(0 4px 9px rgba(20,30,42,.2)); }
.friend-strip .fr-who svg { display:block; overflow:visible; }
.friend-strip .fr-bubble { position:relative; flex:1; background:var(--paper-0); border:1px solid var(--line-2);
  border-radius:15px 15px 15px 5px; padding:11px 15px; font-family:var(--serif); font-size:1rem;
  line-height:1.5; color:var(--ink); box-shadow:0 3px 12px rgba(20,30,42,.06); }
.friend-strip .fr-bubble::before { content:''; position:absolute; left:-8px; bottom:12px;
  border:6px solid transparent; border-right-color:var(--line-2); }
.friend-strip .fr-bubble::after { content:''; position:absolute; left:-6px; bottom:12px;
  border:6px solid transparent; border-right-color:var(--paper-0); }

/* The reader's own sentence, in their own words, above the room's. */
.aim-line { max-width:640px; margin:0 auto 16px; text-align:center; font-family:var(--serif);
  font-style:italic; font-size:1.06rem; color:var(--ink-2); }
.aim-line b { font-style:normal; color:var(--ink); }
.aim-line .aim-edit { display:block; margin:4px auto 0; font-family:var(--sans); font-style:normal;
  font-size:var(--t-00); letter-spacing:.09em; text-transform:uppercase; color:var(--faint);
  background:none; border:none; cursor:pointer; }
.aim-line .aim-edit:hover { color:var(--accent); }

/* The one question it ever asks, and the confession that earns it.
   Set like a printed questionnaire, not a chat: the confession in italic,
   the question in the page face, the choices a ruled list the pen ticks. */
.ask-card { max-width:520px; margin:0 auto 18px; padding:18px 22px 14px; }
.ask-card .confess { font-family:var(--serif); font-style:italic; color:var(--ink-2); margin:0 0 10px; }
.ask-card .q { font-family:var(--serif); font-size:1.12rem; font-weight:600; margin:0 0 12px; }
.ask-card .opts { display:flex; flex-direction:column; gap:8px; }
.ask-card .opts button { text-align:left; justify-content:flex-start;
  font-family:var(--serif); font-size:1rem; font-weight:500; }
.ask-card .opts input[type="text"] { font-family:var(--serif); }
.ask-card .later { margin-top:6px; }

/* ============ the tour: one spotlight, one bubble ============ */
/* z 265: above the lesson stage (240) and toasts (250), below the overlay (290) */
#tour { position:fixed; inset:0; z-index:265; }
#tour[hidden] { display:none; }
#tour-mask { position:absolute; inset:0; background:rgba(12,18,26,.52);
  backdrop-filter:blur(1.5px); animation:tmIn 380ms ease both; }
@keyframes tmIn { from{opacity:0} to{opacity:1} }
#tour-ring { position:absolute; border-radius:16px; box-shadow:0 0 0 4px var(--g-gold), 0 0 0 9999px rgba(12,18,26,.52);
  transition:top .55s cubic-bezier(.3,1.2,.4,1), left .55s cubic-bezier(.3,1.2,.4,1),
             width .55s cubic-bezier(.3,1.2,.4,1), height .55s cubic-bezier(.3,1.2,.4,1);
  pointer-events:none; opacity:0; }
#tour-ring.on { opacity:1; }
body.motion-low #tour-ring { transition:none; }
#tour-panel { position:absolute; left:50%; bottom:26px; transform:translateX(-50%);
  width:min(560px, calc(100vw - 32px)); display:flex; align-items:flex-end; gap:13px; }
/* the bubble gives way to a ring at the foot of the screen (tourStep) */
#tour-panel.at-top { bottom:auto; top:calc(26px + env(safe-area-inset-top, 0px)); }
#tour-panel .t-who { width:60px; height:60px; flex:none; filter:drop-shadow(0 6px 14px rgba(0,0,0,.4)); }
#tour-panel .t-who svg { display:block; overflow:visible; }
#tour-panel .t-bubble { flex:1; position:relative; background:var(--paper-0); border:1.5px solid var(--line-2);
  border-radius:16px 16px 16px 5px; padding:15px 17px 11px; font-family:var(--serif); font-size:1.02rem;
  line-height:1.55; color:var(--ink); box-shadow:0 14px 40px rgba(0,0,0,.34); }
#tour-panel .t-row { display:flex; align-items:center; gap:10px; margin-top:11px; }
#tour-panel .t-dots { flex:1; display:flex; gap:5px; }
#tour-panel .t-dots i { width:5px; height:5px; border-radius:50%; background:var(--line-2); display:block; }
#tour-panel .t-dots i.on { background:var(--accent); }
#tour-panel .t-skip, #tour-panel .t-next { font-family:var(--sans); font-size:.7rem; letter-spacing:.08em;
  text-transform:uppercase; border:none; background:none; cursor:pointer; padding:4px 2px; }
#tour-panel .t-skip { color:var(--faint); }
#tour-panel .t-next { color:var(--accent); font-weight:600; }
@media (max-width:520px) { #tour-panel .t-who { display:none; } }

/* the section guide button — a tour is always re-openable, never forced twice */
.guide-again { display:inline-flex; align-items:center; gap:6px; font-family:var(--sans); font-size:.7rem;
  letter-spacing:.08em; text-transform:uppercase; color:var(--faint); background:none;
  border:1px dashed var(--line); border-radius:20px; padding:4px 12px; cursor:pointer; margin-bottom:14px; }
.guide-again:hover { color:var(--accent); border-color:var(--accent); }

/* ============ what I remember about you ============ */
.memlist { list-style:none; padding:0; margin:18px 0 0; max-width:640px; }
.memlist li { display:flex; gap:12px; align-items:flex-start; padding:12px 2px;
  border-bottom:1px solid var(--line); font-family:var(--serif); font-size:1rem; line-height:1.55; }
.memlist li.gone { opacity:.3; text-decoration:line-through; }
.memlist .mem-x { flex:none; font-family:var(--sans); font-size:var(--t-00); letter-spacing:.08em;
  text-transform:uppercase; color:var(--faint); background:none; border:none; cursor:pointer; padding:3px 0; }
.memlist .mem-x:hover { color:var(--accent); }

.rev-attr { text-align:center; font-family:var(--serif); font-size:.98rem; color:var(--ink-2);
  min-height:1.4em; margin:12px auto 0; max-width:460px;
  transition:opacity 700ms var(--e-std); }
.rev-attr.in { animation:attrIn 460ms cubic-bezier(.2,.9,.3,1) both; }
/* said once, then the ink dries — a quip that outstays the card it was about
   reads as a stray line stranded at the foot of the page */
.rev-attr.dry { opacity:0; }
@keyframes attrIn { from{opacity:0; transform:translateY(5px)} to{opacity:1; transform:none} }
.pfirst { font-family:var(--sans); font-size:var(--t-00); letter-spacing:.03em; color:var(--faint);
  margin:2px 0 4px; }

/* ---- RevMotion: the review card's own physics (sequences.js) ---- */
/* A wrong answer is not punished. The correct form is re-inked letter by
   letter and a nib rules a line under it — the pedagogy and the brand agree. */
.ink-word { position:relative; display:inline-block; }
.ink-ch { display:inline-block; will-change:transform,opacity,filter; }
.ink-rule { position:absolute; left:0; right:0; bottom:-.34em; width:100%; height:.5em;
  overflow:visible; pointer-events:none; }
.ink-rule path { stroke-dasharray:200; }
/* the correct-answer sweep: a band of light crossing the card once, clipped to it */
.rev-sweep { position:absolute; inset:0; overflow:hidden; border-radius:inherit;
  pointer-events:none; z-index:2; }
.rev-sweep > i { position:absolute; top:-20%; bottom:-20%; width:38%; display:block;
  background:linear-gradient(100deg, transparent, rgba(var(--gold), .34) 45%, transparent);
  transform:translateX(-140%) skewX(-14deg); will-change:transform; }
#rev-card { position:relative; }
body.motion-low .rev-sweep { display:none; }


/* ---- THE FOG ---- */
/* Sharpness = memory. --fog is 0 for a word you hold and 1 for one you have
   never met; --fog-scale is the page-wide legibility floor. Blur and opacity
   only, so it composites on the GPU and never reflows the text. */
:root { --fog-scale: 1; --fog-max: 2.1px; }
.w { --fog: 0;
  filter: blur(calc(var(--fog) * var(--fog-scale) * var(--fog-max)));
  opacity: calc(1 - var(--fog) * var(--fog-scale) * 0.42);
  transition: filter 420ms var(--e-std), opacity 420ms var(--e-std); }
/* the squint: everything sharp for as long as you hold */
body.squint .w { filter: none !important; opacity: 1 !important;
  transition: filter 180ms var(--e-std), opacity 180ms var(--e-std); }
/* clearing a word is the reward, so it gets its own settle */
.w.bloom { transition: filter 620ms var(--e-snap), opacity 620ms var(--e-snap); }
/* respect the reader who has turned motion down: state still shows, softly */
body.motion-low .w { transition: none; }
/* fog is a game layer, not a legibility tax — calm mode reads plainly */
/* With the fog off nothing may touch the glyphs — no blur, no dimming, no
   transition that could catch the text mid-render. The literature reads plain. */
body.fog-off .w { filter: none !important; opacity: 1 !important; }
body.fog-off .squint-hint { display: none; }
.squint-hint { position: fixed; left: 50%; bottom: 92px; transform: translateX(-50%);
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .09em; text-transform: uppercase;
  color: var(--faint); background: var(--paper-0); border: 1px solid var(--line);
  border-radius: 20px; padding: 5px 13px; z-index: 30; pointer-events: none;
  opacity: 0; transition: opacity 400ms var(--e-std); }
.squint-hint.on { opacity: 1; }


/* ---- the frontier: the only number in the app that is a fact about Russian ---- */
.frontier-card { max-width:640px; margin:0 auto 16px; text-align:center; }
.frontier-card .fr-n { font-family:var(--serif); font-size:2.6rem; font-weight:600; line-height:1;
  color:var(--ink); font-variant-numeric:tabular-nums; }
.frontier-card .fr-n .fr-d { font-size:1rem; color:var(--faint); font-weight:400; }
.frontier-card .fr-lab { font-family:var(--sans); font-size:.7rem; letter-spacing:.13em;
  text-transform:uppercase; color:var(--ink-2); margin-top:6px; }
.frontier-card .fr-sub { font-family:var(--serif); font-style:italic; color:var(--ink-2);
  font-size:.92rem; margin-top:10px; }
.keys { display:flex; flex-wrap:wrap; gap:8px; justify-content:center; margin-top:12px; }
.keys button { font-family:var(--serif); font-size:1rem; border:1px solid var(--line);
  background:var(--card); color:var(--ink); border-radius:11px; padding:7px 13px; cursor:pointer;
  display:flex; align-items:baseline; gap:8px; transition:border-color .18s, transform .18s; }
.keys button:hover { border-color:var(--signal); transform:translateY(-2px); }
.keys button b { font-weight:600; }
.keys button i { font-family:var(--sans); font-style:normal; font-size:var(--t-00); letter-spacing:.06em;
  text-transform:uppercase; color:var(--faint); }

/* ==========================================================================
   THE BOOTH: three components carry the whole world.
   ========================================================================== */

/* --- 1. THE APERTURE ---------------------------------------------------- */
/* The reading column is a hole cut in the booth with a lit exercise sheet
   behind it. It is the only surface in the app that is paper, and the only
   one that is brighter than its surroundings. */
#v-read .reader, #v-read #text, .aperture {
  background-color: var(--sheet);
  /* the 5mm клетка lattice, printed not drawn — two hairlines, one layer */
  background-image:
    linear-gradient(to right, var(--rule) 0 1px, transparent 1px),
    linear-gradient(to bottom, var(--rule) 0 1px, transparent 1px);
  background-size: 19px 19px;
  background-blend-mode: normal;
}
#v-read #text { position: relative; padding: 26px 26px 40px; }
/* the lattice must sit under the type, not compete with it */
#v-read #text::before {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(var(--sheet), var(--sheet));
  opacity: .74; z-index: 0;
}
#v-read #text > * { position: relative; z-index: 1; }
/* the proscenium: a hard edge where the booth stops and the sheet begins */
#v-read .reader {
  border: 1px solid var(--size);
  box-shadow: inset 0 0 0 3px var(--gesso), 0 0 34px -6px rgba(0,0,0,.14);
}
/* the calcium spot falling on the page */
#v-read .reader::after {
  content: ''; position: absolute; inset: 0; pointer-events: none; z-index: 2;
  background: radial-gradient(78% 46% at 50% 16%,
    color-mix(in srgb, var(--limelight) 22%, transparent), transparent 72%);
  mix-blend-mode: multiply; opacity: .5;
}
[data-theme="dark"] #v-read .reader::after { mix-blend-mode: screen; opacity: .16; }

/* --- 2. THE FOOTING ----------------------------------------------------- */
/* Every element declares what it is by what it stands on. A figure is a
   painted cut-out standing by a trapezoid tongue in a slot cut in the stage
   floor. Chamfer on the TOP corners only — the way a flat's corners are
   trimmed so the canvas does not catch on the fly lines. */
.card, .btn, .panel, .book, .tile, #rev-card, #pop, .toast,
.glesson-card, .ask-card {
  border-radius: 0;
  clip-path: polygon(0 5px, 5px 0, calc(100% - 5px) 0, 100% 5px,
                     100% 100%, 0 100%);
}
.card, .book, .glesson-card, .tile, #rev-card, .ask-card {
  background: var(--gesso); border: 1px solid var(--size);
  box-shadow: 0 3px 0 var(--foot);
  transition: box-shadow 160ms var(--e-std), border-color 160ms var(--e-std);
}
/* the slot the tab stands in — on the wrapper, outside any clip */
.card::after, .book::after, .glesson-card::after {
  content: ''; position: absolute; left: 50%; bottom: -5px;
  width: clamp(24px, 22%, 54px); height: 2px; transform: translateX(-50%);
  background: color-mix(in srgb, var(--ink) 22%, transparent);
  border-radius: 1px; pointer-events: none;
}
.card, .book, .glesson-card { position: relative; }
/* nothing lifts on hover; the outline comes up and the slot darkens */
.card:hover, .book:hover, .glesson-card:hover { border-color: color-mix(in srgb, var(--ink) 30%, transparent); }
.book:hover::after, .glesson-card:hover::after { background: color-mix(in srgb, var(--ink) 34%, transparent); }

/* a button is a figure standing: pressing it drops it into its slot */
.btn, .rev-btns button, .quiz-opt, .filters button, #pop .row button {
  border-radius: 0;
  clip-path: polygon(0 4px, 4px 0, calc(100% - 4px) 0, 100% 4px, 100% 100%, 0 100%);
  box-shadow: 0 3px 0 var(--foot);
  transition: transform 90ms cubic-bezier(.2,0,0,1), box-shadow 90ms cubic-bezier(.2,0,0,1),
              background 160ms var(--e-std), color 160ms var(--e-std), border-color 160ms var(--e-std);
}
.btn:active, .rev-btns button:active, .quiz-opt:active, .filters button:active {
  transform: translateY(3px); box-shadow: 0 0 0 var(--foot);
}
.btn.primary, .rev-btns .primary { background: var(--ink); border-color: var(--ink); color: var(--gesso); }
/* exactly one lit button per screen: the one true next action */
.btn.primary { box-shadow: 0 3px 0 var(--foot), 0 0 0 1px var(--minium),
  0 0 22px -4px color-mix(in srgb, var(--limelight) 70%, transparent); }

/* a chip is a ticket stub — the only round thing in the booth, because a lamp is round */
.gchip, .streak-chip, .guide-again, .book .lvl {
  border-radius: 22px; font-family: var(--sans); letter-spacing: .14em;
  -webkit-mask-image: radial-gradient(circle 7px at 0 50%, transparent 98%, #000 100%);
          mask-image: radial-gradient(circle 7px at 0 50%, transparent 98%, #000 100%);
}

/* every number in this world is set by a printer on a bill */
.tile .v, .fr-n, .s-v, .cnt, .tl-date,
.meter-legend span, .pstage, .fr-lab {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
}

/* --- 3. THE ПОМЕТА ------------------------------------------------------ */
/* The mark a Russian schoolchild rules under a word to parse a sentence.
   ONE mechanism — background-image on the .w span — carries every state, so
   the marks share a baseline. text-decoration is banned here: its style,
   thickness and offset controls do not share a baseline across variants and
   `wavy` amplitude is engine-defined.

   The reader's own hand is ballpoint blue. The teacher's is red lead. */
.w {
  --mark: transparent; --mark-h: 2px; --r: 1;
  background-repeat: no-repeat;
  background-position: 0 100%;
  background-size: 100% var(--mark-h);
  background-image: linear-gradient(var(--mark), var(--mark));
  transition: background-size 380ms cubic-bezier(.05,.7,.1,1),
              filter 420ms var(--e-std), opacity 420ms var(--e-std);
}
/* THE CLEAN PAGE LAW. Measured on «Шинель» instalment 2: 821 of 825 word
   tokens carried a printed rule — new words a grey hairline each, known words
   a standing 2px stroke, learning words a third weight — and a page of
   Gogol read as a page of conveyancing. A mark only means something on a
   page that is otherwise print. So: ink belongs to the words that carry the
   READER'S story — the ones being learned, the ones slipping — and to
   nothing else. A claimed word's reward is that it returns to plain print. */
/* held cold: plain print — the word is yours, the page is clean */
.w.held { --mark: transparent; }
/* Learning: the mark is the memory gauge, in three drawers rather than a
   continuous width. See paintLemma — the class swap is 9x cheaper than the
   inline custom property it replaces, on the operation this product performs
   more than any other. Pencil pressure, not the press's rule: the reader's
   own hand is allowed to be lighter than the type it annotates. */
.w.l    { --mark: color-mix(in srgb, var(--carbon) 62%, transparent); --mark-h: 2px; background-size: 0 var(--mark-h); }
.w.l.r0 { background-size: 34% var(--mark-h); }
.w.l.r1 { background-size: 68% var(--mark-h); }
.w.l.r2 { background-size: 100% var(--mark-h); }
/* never met: nothing at all, so the eye is drawn by the fog, not by paint */
.w.new { --mark: transparent; }
/* The underline hint on words never met.
   Ink at a fixed density rather than --rule, because --rule under a word on
   the sheet measures Lc 25.7 in the light room and only −15.1 in the dark one,
   which is the divider floor — the hairline vanished at night.
   `hint-new` (the default) now marks new words only until the reader has
   deliberately MARKED a word — `inv-on`, evaluated once at each scene's door
   in openScene, never withdrawn mid-page (a tap to peek at a gloss is not a
   mark; see the openScene comment). After that the invitation lives at the
   hand (hover) and the page is print. `hint-all` keeps the standing hairline
   under every unmet word, everywhere, for the reader who chooses it. */
body.hint-all .w.new,
body.hint-new #read-wrap.inv-on .w.new { --mark: color-mix(in srgb, var(--carbon) 34%, transparent); --mark-h: 1px; }
/* met but set aside */
.w.skip { background-image: repeating-linear-gradient(90deg, var(--ink-3) 0 3px, transparent 3px 6px); --mark-h: 1px; }
/* reading only — recognised, never drilled: dot-dash */
.w.ronly { background-image: repeating-linear-gradient(90deg, var(--ink-3) 0 5px, transparent 5px 7px, var(--ink-3) 7px 8px, transparent 8px 10px); --mark-h: 1px; }
/* slipping: the teacher's red, and the only wavy line in the product */
.w.leech { --mark-h: 5px; background-size: 8px 5px; background-repeat: repeat-x;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5'%3E%3Cpath d='M0,3.5 q2,-3 4,0 t4,0' fill='none' stroke='%23cb4726' stroke-width='1.4'/%3E%3C/svg%3E"); }
/* the word under examination stands up: a button's tab, at one-ninth scale */
.w.standing { background-image:
  linear-gradient(var(--paste), var(--paste)),
  linear-gradient(var(--paste), var(--paste));
  background-repeat: no-repeat, no-repeat;
  background-position: 0 100%, 50% 100%;
  background-size: 100% 2px, 5px 4px; }
.w:hover { --mark: var(--minium); --mark-h: 2px; background-size: 100% 2px; color: var(--minium-t); }

/* the rail: six slots in Russian school order, the grammar HUD */
.rail { display: inline-flex; gap: 3px; align-items: flex-end; height: 12px; vertical-align: middle; }
.rail i { display: block; width: 5px; height: 5px; background: var(--rule); border-radius: 0; }
.rail i.met { background: var(--paste); }
.rail i.now { background: var(--ink); height: 8px; }
.rail-lab { font-family: var(--sans); font-size: var(--t-00); letter-spacing: .18em;
  color: var(--ink-3); text-transform: uppercase; }


/* ---- THE BILL: the playbill for tonight ---- */
.frontier-card { background:var(--gesso); border:1px solid var(--size); box-shadow:0 3px 0 var(--foot); padding:22px 24px 20px; }
.bill-head { font-family:var(--bill); font-size:var(--t-00); letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink-3); border-bottom:1px solid var(--size); padding-bottom:8px; margin-bottom:14px; }
.frontier-card .fr-n { font-family:var(--bill); font-size:var(--t-7); font-weight:700; }
.frontier-card .fr-lab { font-family:var(--sans); letter-spacing:.12em; }
.bill { list-style:none; padding:0; margin:16px auto 4px; max-width:420px; text-align:left; }
/* a line on the cast list: no box, no fill — a dotted leader and a printed figure */
.bill li { display:flex; align-items:baseline; gap:8px; padding:5px 0; font-family:var(--bill);
  font-size:var(--t-1); letter-spacing:.06em; text-transform:uppercase; color:var(--ink); }
.bill li i { flex:1; height:1em; background-image:repeating-linear-gradient(90deg,var(--size) 0 1px,transparent 1px 5px);
  background-repeat:repeat-x; background-position:0 82%; background-size:auto 1px; }
.bill li .b-n { font-family:var(--mono); font-variant-numeric:tabular-nums; color:var(--ink-2); }
.bill li.struck { color:var(--ink-3); }
.bill li.struck .b-ti { text-decoration:line-through; text-decoration-thickness:1px; }
.bill li .b-x { font-family:var(--sans); font-size:var(--t-00); letter-spacing:.14em; color:var(--zelyonka); }


/* ---- the grammar card ---- */
.pat-opts { display:flex; flex-wrap:wrap; gap:9px; justify-content:center; margin-top:16px; }
.pat-opts button { flex:0 0 auto; width:auto; min-width:78px; display:block;
  font-family:var(--serif); font-size:var(--t-3); padding:9px 18px;
  border:1px solid var(--size); background:var(--gesso); color:var(--ink); cursor:pointer;
  clip-path:polygon(0 4px,4px 0,calc(100% - 4px) 0,100% 4px,100% 100%,0 100%);
  box-shadow:0 3px 0 var(--foot); }
.pat-opts button:hover:not(:disabled) { border-color:var(--minium); color:var(--minium-t); }
.pat-opts button:disabled { cursor:default; }
.pat-opts button.right { background:var(--zelyonka); border-color:var(--zelyonka); color:var(--gesso); }
.pat-opts button.wrong { border-color:var(--minium); color:var(--minium-t); text-decoration:line-through; }
#rev-card .rail i { width:6px; height:6px; }
.pat-why { margin-top:16px; padding-top:12px; border-top:1px solid var(--size);
  font-family:var(--serif); font-size:var(--t-1); color:var(--ink-2); line-height:1.6; }
#rev-card .rail { margin:6px auto 0; }
#rev-card .rail-lab { margin-top:10px; }

/* ---- the shelf you do not own yet ---- */
.unopened { margin-top:20px; padding-top:16px; border-top:1px solid var(--size); }
.un-head { font-family:var(--bill); font-size:var(--t-00); letter-spacing:.22em; text-transform:uppercase;
  color:var(--ink-3); margin-bottom:10px; }
.unopened .bill li .b-of { font-family:var(--mono); font-size:var(--t-00); color:var(--ink-3); margin-left:6px; }
.un-note { font-family:var(--serif); font-style:italic; font-size:var(--t-1); color:var(--ink-2);
  max-width:440px; margin:12px auto 14px; line-height:1.55; }

/* The letters offer, laid into the finished-book plate when the counter is
   declined. Same plate, same paper — it is the second half of one sentence,
   not a new panel arriving. */
/* The one word the house opens by itself. Ink seeping into paper — the same
   settle as everything else here, no pulse, no bounce, nothing that says
   "look at me" in a voice the rest of the room never uses. */
.w-shown { background: color-mix(in srgb, var(--signal) 16%, transparent);
  box-shadow: 0 1px 0 var(--signal);
  transition: background 900ms cubic-bezier(.2,.7,.3,1), box-shadow 900ms cubic-bezier(.2,.7,.3,1); }
@media (prefers-reduced-motion: reduce) { .w-shown { transition: none; } }
/* AFTER THE DEMONSTRATION, THE INVITATION. The house opened one word and shut
   it again; what stayed behind was nothing, and almost nobody went on to try
   the gesture themselves. So the shown word keeps its red mark, and one line
   in the margin hands the gesture over — both leave for good at the reader's
   own first touch. */
.w.w-invite { --mark: var(--signal); --mark-h: 2px; background-size: 100% 2px; }
/* THE PEN'S RING: the librarian circles ONE word, once, in one slow 4-second
   seep — ink being drawn, not an element appearing. Nothing bounces; the ring
   settles at rest slightly dry. Under reduced motion it simply stands. */
.w.w-ring { position: relative; }
.w.w-ring::after {
  content: ''; position: absolute; inset: -0.28em -0.5em;
  border: 2px solid var(--signal-ink); border-radius: 50%;
  transform: rotate(-2.5deg); opacity: .78; pointer-events: none;
}
body:not(.motion-low) .w.w-ring::after {
  opacity: 0;
  clip-path: polygon(0 0, 0 0, 0 100%, 0 100%);
  animation: ink-ring 4s cubic-bezier(0.16, 1, 0.3, 1) .2s forwards;
}
@keyframes ink-ring {
  0%   { opacity: 0;   clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); }
  25%  { opacity: .85; }
  100% { opacity: .78; clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }
}
.gesture-invite { display: block; margin: 8px 0 2px;
  font-family: var(--serif); font-style: italic;
  font-size: .88rem; line-height: 1.5; color: var(--signal-ink); }
/* the manicule — the printer's own pointing hand, not a sparkle */
.gesture-invite::before { content: '☞ '; font-style: normal; }
/* THE LIBRARIAN'S PENCIL: before the first-ever touch, the opening lines'
   words carry a faint dotted rule — "these answer" said without a word.
   It dries at the first tap and never returns. */
#read-wrap.first-read #text p:nth-of-type(-n+2) .w {
  border-bottom: 1px dotted color-mix(in srgb, var(--signal-ink) 38%, transparent);
}
/* THE BOOK'S INK on the page it is printed on. Falls back to the room's own
   signal everywhere, so a book with no assigned colour looks exactly as it did
   and nothing depends on the assignment existing. */
#v-read .scene-head .rule,
#v-read .scene-head hr { border-top-color: var(--book-ink, var(--signal)); }
#v-read #scene-orn, #v-read .scene-head .orn { color: var(--book-ink, var(--signal)); }
/* the kicker over the title prints in the book's ink too — the first thing
   the page says is said in this book's own pigment */
#v-read .scene-head .vy { color: color-mix(in srgb, var(--book-ink, var(--ink-2)) 78%, var(--ink-2)); }
#v-read .divider { color: var(--book-ink, var(--verm)); }
#v-read .sent.done { text-decoration-color: color-mix(in srgb, var(--book-ink, var(--signal)) 55%, transparent); }
#readbar .rb-progress i, #readbar .prog i { background: var(--book-ink, var(--signal)); }
/* the sentences-read fill on the bar rides the book's ink as well (it can:
   openScene declares --book-ink on the body for the fixed furniture) */
#readbar .fill { background: var(--book-ink, var(--verm)); height: 3px; }
/* THE DARK ROOM GUARD. The six book inks are dark pigments mixed for paper;
   at night they sink into the ground. Ink-coloured TEXT lifts toward the
   room's own light ink; hairline ornament may stay dim, a drop cap may not. */
[data-theme="dark"] #v-read .scene-head .vy,
[data-theme="dark"] #pop .pw { color: color-mix(in srgb, var(--book-ink, var(--carbon)) 32%, var(--carbon)); }
[data-theme="dark"] #text p.dropcap::first-letter,
[data-theme="dark"] #text .dc { color: color-mix(in srgb, var(--book-ink, var(--verm)) 38%, var(--carbon)); }
[data-theme="dark"] #v-read .divider { color: color-mix(in srgb, var(--book-ink, var(--verm)) 40%, var(--carbon)); }
/* THE LAMP ROOM NEEDED THE SAME GUARD, IN THE OTHER DIRECTION — and did not
   have one, because the night is the obvious case and the lamp is not.

   The scene head does not sit on the sheet. The prose does (#v-read #text
   takes --sheet), but the head stands above it, on the ROOM — and the lamp
   room's ground is #cabe9f under the reading route, far darker than the cool
   near-white the pigments were ground against. On it they close up rather
   than sink:

       okhra ....... 2.53:1   («Смерть чиновника», «Спать хочется», «Белыя ночи»)
       zelen ....... 4.11:1
       sepia ....... 4.47:1

   Okhra is the failure — three of the fifteen books, on the line that names
   the instalment. A light room takes the mirror of the night guard: the
   pigment walks toward the room's own DARK ink rather than its light one.
   At 50% every masstone clears the floor (4.85 okhra, 7.17 grafit) and each
   is still recognisably itself — half a pigment is still that pigment, which
   is the whole reason §11 mixes rather than replaces. */
[data-theme="sepia"] #v-read .scene-head .vy,
[data-theme="sepia"] #v-read .divider,
[data-theme="sepia"] #v-read #scene-orn,
[data-theme="sepia"] #v-read .scene-head .orn {
  color: color-mix(in oklab, var(--book-ink, var(--carbon)) 50%, var(--carbon));
}
/* The directory of rooms, in the settings panel. */
.room-list { font-family:var(--serif); font-size:var(--t-1); }
.room-list a { color:var(--signal-ink); }
.room-list b { font-weight:600; }
/* The password rule, said before it is broken rather than after. */
.k-rule { font-family:var(--serif); font-style:italic; font-size:var(--t-1);
  color:var(--ink-2); margin:calc(var(--s2) * -1) 0 var(--s3); line-height:1.45; }
.letters-row { display:flex; gap:var(--s2); flex-wrap:wrap; margin:var(--s4) 0 var(--s2); }
.letters-row input { flex:1 1 15rem; min-width:0; font:inherit; font-size:var(--t-1);
  padding:10px 12px; color:var(--carbon); background:var(--sheet);
  border:1px solid var(--edge); border-radius:0; }
.letters-row input:focus-visible { outline:2px solid var(--signal); outline-offset:1px; }
.letters-say:empty { display:none; }
.letters-plate .btnrow { margin-top:var(--s2); }

/* ---- the ladder ---- */
.lad-line { font-family:var(--bill); font-size:var(--t-5); font-weight:700; margin:4px 0 10px; }
.lad-line b { font-size:var(--t-6); }
.lad-note { font-family:var(--serif); font-size:var(--t-1); color:var(--ink-2); line-height:1.6;
  max-width:460px; margin:12px auto; }
.rungs { list-style:none; padding:0; margin:14px auto 6px; max-width:400px; text-align:left; }
.rungs li { display:flex; align-items:baseline; gap:8px; padding:4px 0; }
.rungs li .r-l { font-family:var(--serif); font-size:var(--t-2); }
.rungs li i { flex:1; height:1em; background-image:repeating-linear-gradient(90deg,var(--size) 0 1px,transparent 1px 5px);
  background-repeat:repeat-x; background-position:0 78%; background-size:auto 1px; }
.rungs li .r-o { font-family:var(--mono); font-size:var(--t-00); color:var(--ink-3); }

/* ---- the key: signing in ---- */
/* Not a bare symbol. A key nobody can find is a door nobody opens — and this
   is the only route by which a purchase reaches a second machine. */
.nav-key { display:inline-flex; align-items:center; gap:6px; }
.nav-key #nav-key-label { font-size:.86em; }
.nav-key.in { color:var(--zelyonka); }
.nav-key.in::after { content:'●'; font-size:.5em; vertical-align:middle; }
#key-panel { max-width: 460px; }
#key-panel h3 { margin: 0 0 4px; }
#key-panel .k-lede { font-family: var(--serif); color: var(--ink-2); font-size: var(--t-1);
  line-height: 1.6; margin: 0 0 16px; }
#key-panel input[type=email],
#key-panel input[type=password],
#key-panel input[type=text] { width: 100%; font-family: var(--serif); font-size: var(--t-2);
  padding: 11px 14px; border: 1px solid var(--size); background: var(--sheet); color: var(--ink);
  clip-path: polygon(0 4px,4px 0,calc(100% - 4px) 0,100% 4px,100% 100%,0 100%); }
#key-panel input[type=email], #key-panel input[type=password], #key-panel input[type=text] { margin-bottom: 9px; }
/* THE RING THE READER CAN ACTUALLY SEE. This said outline:none and then
   "replaced" it with a 1px --minium edge — --minium is --signal #f6c445, and
   on the focused field's own ground, --sheet, that measures 1.60:1 in daylight
   and 1.56:1 under the lamp: a rumour of a ring on the one form in the house
   where losing the caret costs real work. The outline stays off, so the ring
   is drawn INSIDE the field instead — the same trade 26-games.css:1247-1250
   makes, an outline traded for a box-shadow. In the two warm rooms the gain is
   geometric: 2px, inset, where there was a 1px edge. The ring's ink is the
   same gold there, because --mark is var(--signal) (10-components.css:93) and
   the lamp block at :308 does not redefine it; only [data-theme="dark"] moves
   it, to --signal-ink #ffe28a (10-components.css:132) — the flip
   10-components.css:88-92 minted the token for. (The chamfer at
   :1974 never reaches the built page — 66-rooms.css:638 cuts these same three
   fields square.) The three types are named so the ring outranks
   66-rooms.css:644, which repaints this border in --signal on focus. */
#key-panel input:focus { outline: none; }
#key-panel input[type=email]:focus-visible,
#key-panel input[type=password]:focus-visible,
#key-panel input[type=text]:focus-visible {
  border-color: var(--minium-t);
  box-shadow: inset 0 0 0 2px var(--mark);
}

/* two ways in, side by side, so neither looks like the awkward one */
.k-tabs { display: flex; gap: 0; margin: 0 0 14px; border-bottom: 1px solid var(--size); }
.k-tabs button { flex: 1; background: none; border: none; cursor: pointer; padding: 8px 4px;
  font-family: var(--sans); font-size: var(--t-0); letter-spacing: .06em; color: var(--ink-3);
  border-bottom: 2px solid transparent; margin-bottom: -1px; }
.k-tabs button.on { color: var(--ink); border-bottom-color: var(--minium); }
.k-alt { display: flex; flex-direction: column; gap: 4px; margin-top: 14px;
  padding-top: 12px; border-top: 1px solid var(--size); }
.k-linkish { background: none; border: none; padding: 3px 0; cursor: pointer; text-align: left;
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .04em; color: var(--ink-3); }
.k-linkish:hover { color: var(--minium-t); }
#key-panel .k-sent { font-family: var(--serif); font-size: var(--t-1); line-height: 1.65;
  color: var(--ink-2); }
#key-panel .k-sent b { color: var(--ink); font-style: normal; }
#key-panel .k-err { color: var(--minium-t); font-family: var(--sans); font-size: var(--t-0);
  margin-top: 8px; }
#key-panel .k-rows { list-style: none; padding: 0; margin: 14px 0 0; }
#key-panel .k-rows li { display: flex; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--size); font-family: var(--sans);
  font-size: var(--t-0); }
#key-panel .k-rows li span:last-child { font-family: var(--mono); color: var(--ink-2); }
#key-panel .k-danger { font-family: var(--sans); font-size: var(--t-00); letter-spacing: .08em;
  text-transform: uppercase; color: var(--faint); background: none; border: none;
  cursor: pointer; padding: 6px 0; }
#key-panel .k-danger:hover { color: var(--minium-t); }


/* ---- the two plans ---- */
.plans { display:grid; grid-template-columns:1fr 1fr; gap:10px; margin:16px 0 4px; }
.plans .plan { display:flex; flex-direction:column; align-items:flex-start; gap:2px;
  text-align:left; padding:14px 15px; cursor:pointer; background:var(--gesso);
  border:1px solid var(--size); box-shadow:0 3px 0 var(--foot);
  clip-path:polygon(0 5px,5px 0,calc(100% - 5px) 0,100% 5px,100% 100%,0 100%);
  transition:border-color .16s var(--e-std); }
.plans .plan:hover { border-color:var(--minium); }
.plans .plan:active { transform:translateY(3px); box-shadow:0 0 0 var(--foot); }
.plans .plan.best { border-color:var(--minium);
  box-shadow:0 3px 0 var(--foot), 0 0 22px -6px color-mix(in srgb, var(--limelight) 70%, transparent); }
.plans .p-amt { font-family:var(--bill); font-size:var(--t-5); font-weight:700; line-height:1; }
.plans .p-per { font-family:var(--sans); font-size:var(--t-00); letter-spacing:.1em;
  text-transform:uppercase; color:var(--ink-3); }
.plans .p-note { font-family:var(--serif); font-size:var(--t-0); color:var(--ink-2);
  line-height:1.45; margin-top:6px; }
@media (max-width:520px) { .plans { grid-template-columns:1fr; } }

/* ==========================================================================
   КАБИНЕТ — the structure. A 19th-century title page is a set of structural
   moves too: centred type, tracked serif capitals, the double rule, the
   ornament on the axis. Everything below is one of those four. The diagonal
   wedge and the wood-type poster face are struck — that was the composing
   room of 1923, one revolution too late for this library.
   ========================================================================== */
.masthead { position: relative; isolation: isolate;
  text-align: center; padding: 38px 0 30px; margin-bottom: 26px; }
/* the title page closes with a double rule, thin over thick, full measure */
.masthead::after {
  content: ''; position: absolute; left: 8%; right: 8%; bottom: 0; height: 5px;
  border-top: 1px solid var(--rule-room);
  border-bottom: 2px solid var(--carbon);
}
.masthead .wordmark { text-align: center; }
.masthead .room-line { text-align: center; }

/* The room's name is set the way the instalment heads already are: tracked
   serif capitals at a size a hand press could actually lock up. */
.masthead h1, .masthead .as-h1 {
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(30px, 5.4vw, 52px); line-height: 1.12;
  letter-spacing: .14em; text-transform: uppercase; text-indent: .14em;
  margin: 16px 0 8px; text-wrap: balance; color: var(--carbon);
}
.masthead .wordmark, .masthead .room-line { color: var(--carbon); }
/* the hinge of the lockup prints SIGNAL — the hex it has always rendered.
   Gold leaf is ceremony foil now (concordat clause c): a standing brand
   surface may not wear the seal's material. */
.masthead .wordmark .wm-t { color: var(--signal); }
.masthead .rule { margin-left: auto; margin-right: auto; }

/* --- THE SHEET ON THE DESK. A card is a piece of paper laid on oak: a
   hairline edge in faded ink, a soft fall of shadow from the lamp. --- */
.card, .book, .glesson-card, .tile, #rev-card, .ask-card, .frontier-card {
  clip-path: none; border-radius: 0;
  border: 1px solid color-mix(in srgb, var(--carbon) 42%, var(--rule));
  box-shadow: var(--shadow);
  background: var(--panel);
}
.card::after, .book::after, .glesson-card::after { display: none; }
.frontier-card, #ladder-card { background: var(--galley); }
/* the bar: every panel is headed by a rule of solid ink, full bleed */
.bill-head, .un-head, .smallcaps {
  font-family: var(--bill); font-weight: 600; text-transform: uppercase;
  letter-spacing: .16em;
}
.frontier-card .bill-head, #ladder-card .bill-head {
  background: var(--carbon); color: var(--galley);
  margin: -22px -24px 16px; padding: 7px 24px; border-bottom: none;
}

/* --- TYPE AS OBJECT. The number is not a label, it is the largest thing on
   the plane, and it is set in the counting face. --- */
.frontier-card .fr-n, .lad-line {
  font-family: var(--mono); font-weight: 400;
  letter-spacing: -.02em; line-height: .9;
}
.frontier-card .fr-n { font-size: clamp(40px, 9vw, 72px); }
.frontier-card .fr-n .fr-d { font-family: var(--sans); font-size: var(--t-1);
  letter-spacing: 0; color: var(--steel); }
.fr-lab { font-family: var(--bill); letter-spacing: .2em; text-transform: uppercase;
  color: var(--signal-ink); }

/* --- BUTTONS ARE LETTERPRESS. A plate that lifts a hair under the hand and
   presses INTO the paper when struck — travel down, shadow gone, ink deeper. --- */
.btn, .rev-btns button, .quiz-opt, .filters button, .plans .plan {
  clip-path: none; border-radius: 0;
  border: 1px solid color-mix(in srgb, var(--carbon) 55%, var(--rule));
  background: var(--galley); color: var(--carbon);
  box-shadow: 0 1px 2px rgba(52,32,15,.14), 0 4px 12px -6px rgba(52,32,15,.35);
  font-family: var(--sans); font-weight: 600; letter-spacing: .04em;
  transition: background .12s linear, color .12s linear,
              box-shadow .12s var(--e-std, ease), transform .12s var(--e-std, ease);
}
.btn:hover, .rev-btns button:hover, .quiz-opt:hover {
  background: var(--sheet); border-color: var(--carbon);
}
.btn.primary, .rev-btns .primary {
  background: var(--signal); border-color: var(--signal-ink); color: var(--on-signal);
}
.btn.primary:hover { background: var(--signal-ink); border-color: var(--signal-ink); }
.btn:active, .rev-btns button:active {
  transform: translateY(1px); box-shadow: none;
  background: color-mix(in srgb, var(--carbon) 8%, var(--galley));
}
.btn.primary:active { background: var(--signal-ink); color: var(--on-signal); }
.plans .plan.best { border-color: var(--signal); border-width: 2px; }
.plans .p-amt { font-family: var(--mono); }

/* --- THE NAVIGATION IS A RUNNING HEAD — tracked serif capitals, the way a
   chapter names itself at the top of a page. --- */
.nav-tabs button {
  font-family: var(--serif); text-transform: uppercase; letter-spacing: .14em;
  font-size: var(--t-0); border-radius: 0;
}
.nav-tabs button.active { color: var(--signal-ink); }
.nav-tabs button.active::after { background: var(--signal); height: 2px; }

/* --- THE APERTURE. The page is the one warm, orthogonal, quiet thing in a
   hard room — figure against ground, and the whole reason for the room. --- */
#v-read .reader {
  background: var(--sheet); background-image: none;
  border: none; box-shadow: none;
  border-left: 6px solid var(--signal);
}
#v-read .reader::after { display: none; }        /* no light effects on the page */
#v-read #text { background: var(--sheet); background-image: none; padding: 30px 34px 44px; }
#v-read #text::before { display: none; }

/* ===== 10-components.css ===== */
/* ============================================================================
   10-components.css — THE COMPONENT LAYER, CUT FOR НАБОР

   The palette and the three faces changed underneath a component layer that
   had been written for warm paper, soft corners and three saturated hues. The
   result was not a bad palette on a good interface — it was a good palette on
   an interface still shaped like the old one: 14px radii, blurred shadows, the
   reading sheet used as a button ground, and three sizes doing the work of
   nine. Recolouring cannot fix a shape.

   This sheet loads after head.html, so it overrides rather than edits. It is
   the only file this feature touches.

   THE FIVE MOVES, in order of how much they change:

   1. THE SHEET STOPS BEING A BUTTON. head.html aliases --paper-hi to --sheet,
      and .btn, .tile, .quiz-opt, .ltile, .find-card, #pop and every overlay
      panel reached for it. That put the one warm surface in the product on
      roughly forty small chrome elements per screen, which is exactly how a
      figure-and-ground system stops working: if everything is the page,
      nothing is. Every one of them moves to --galley. --sheet is left to the
      reading plane and to the study card, which is a reading plane too.

   2. RADIUS GOES TO ZERO AND SHADOWS LOSE THEIR BLUR. A blurred shadow implies
      a light source; this product has planes and no light. Elevation is an
      offset with zero blur in the light rooms, and in the dark room it is
      luminance, because a cast shadow on #0d0e0d is a promise the screen
      cannot keep.

   3. EVERY CARD GETS AN EDGE. --rule on --galley measures Lc 15.2, the divider
      floor — which is why the old cards read as fog banks rather than stock.
      In the light rooms the card edge is --carbon (Lc 90.6 on galley, 62.1 on
      room). In the dark room --carbon would be a near-white hairline around
      every one of the Desk's twelve cards, so there the edge is --rule
      (Lc −17.7) and the elevation is carried by galley's luminance over room,
      which is what head.html's dark mode was built to do.

   4. ONE SATURATED HUE, RATIONED. The play layer's --g-green/-gold/-blue/
      -purple/-rose/-teal/-orange are aliased onto ink and сурик here, the
      same trick head.html used to retire cobalt. After this, сурик covers
      area in three places only — the primary action, the node you are standing
      on, and a seal you have earned — and otherwise appears as a MARK: the
      focus ring, the meter fill, the checked compartment of a checkbox, the
      rule down the side of a quotation, the stud on the timeline. Selected
      states that used to be red — the word filters, .icon-btn.on, the
      popover's "known" toggle — are carbon fills now. Red that is everywhere
      is not a currency.

   5. NINE SIZES, ONE 4px MODULE. Sizes come from --t-00..--t-7 and nothing
      else; spacing from --s1..--s9, which are 4px multiples and nothing else.

   And one correction that is not a move but a law: the header and the readbar
   were --room. The room is display type only — Lc 32.9 for steel is what makes
   that enforceable rather than a preference — and both of those bars carry
   eight sans labels and a count. They are galleys now.

   ACCESSIBILITY, MEASURED (APCA Lc, light / dark / sepia):
     panel-head label on the ink bar   −91.0 /  95.2 / −90.2
     primary button label on сурик    −75.0 / −75.0 / −75.3  (the body floor)
     body on galley                     90.6 / −97.0 /  89.7
     steel label on galley              75.6 / −77.5 /  76.0
     signal-ink label on galley         73.5 / −70.0 /  75.5
     input text on sheet               101.1 / −94.4 /  98.4
   The polarity flip is why --on-signal exists and why it is used and never
   spelled out: carbon on light сурик is Lc 29.1 and sheet on dark сурик is
   Lc 19.5. Both are illegal and the pair is not symmetrical.

   No animation is declared here — another builder owns motion. Hover, active,
   focus-visible and disabled are STATES, not transitions; where head.html has
   already declared a transition for a property, these states inherit it.
   ========================================================================== */

:root {
  /* --- the 4px module. Nine steps, because nine is what nine sizes need. --- */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 20px;
  --s6: 24px; --s7: 32px; --s8: 40px; --s9: 48px;

  /* --- the printed edge. Not a new colour: a name for which token does the
     ruling on which ground, so the dark room can answer differently without a
     single hex appearing inside a media query. --- */
  --edge:    color-mix(in srgb, var(--carbon) 55%, var(--rule));  /* card, button and table rules */
  --edge-hi: var(--carbon);   /* the floating things: overlays, popovers, toasts */
  /* elevation under the lamp: soft, falls downward, three heights */
  --lift:    0 2px 3px rgba(52,32,15,.14), 0 8px 20px -10px rgba(52,32,15,.45);
  --lift-lg: 0 4px 8px rgba(52,32,15,.15), 0 22px 48px -18px rgba(52,32,15,.5);
  --lift-sm: 0 1px 2px rgba(52,32,15,.14), 0 5px 14px -8px rgba(52,32,15,.4);

  /* --- сурик when it is a MARK rather than a fill behind a label. At 1–2px
     the light-room red still reads on every ground, but in the dark room
     --signal on galley measures Lc −21.1 and on room −23.3, which is a rumour
     of a focus ring. head.html already found this for the underline under a
     word; --mark is the same finding given a name. --- */
  --mark: var(--signal);

  /* the scrim behind a modal, built from the room's own darkest token so it
     never becomes a white wash when the polarity flips */
  --scrim: color-mix(in srgb, var(--carbon) 58%, transparent);

  /* the panel head: a solid bar of ink with the label reversed out of it */
  --bar:    var(--carbon);
  --on-bar: var(--galley);

  /* card geometry, shared so a full-bleed head can do the arithmetic */
  --pad-y: 20px;
  --pad-x: 24px;

  /* ---- RETIRING THE PLAY LAYER ----------------------------------------
     head.html declares a seven-hue "delight cast" — green, gold, blue,
     purple, rose, teal, orange — and the path nodes, XP bar, quest bars,
     goal ring, level badge and tour ring all reach for it. That is six
     saturated hues more than this product owns. Rather than hunt every call
     site, they are aliased onto ink and сурик here, exactly as head.html
     aliased cobalt and gold onto the single-hue system. Success is ink
     filling in; there is no green in this product. ------------------------ */
  --g-green: var(--carbon);   --g-green-d: var(--steel);
  --g-gold:  var(--signal);   --g-gold-d:  var(--signal-ink);
  --g-blue:  var(--steel);    --g-purple:  var(--carbon);
  --g-rose:  var(--signal-ink); --g-teal:  var(--steel);
  --g-orange: var(--signal);  --g-ring:    var(--rule);
}

[data-theme="dark"] {
  /* A near-white 1px box around each of twelve Desk cards is a wireframe, not
     a print shop. --rule is the hairline this room was designed with. */
  --edge:    var(--rule);
  --edge-hi: var(--steel);
  /* the study after dark: the same lamp, deeper falls. Hard 0-blur offsets
     were the press room's planes; the candlelit room casts soft shadow too. */
  --lift:    0 2px 4px rgba(0,0,0,.5), 0 10px 24px -10px rgba(0,0,0,.75);
  --lift-lg: 0 4px 10px rgba(0,0,0,.55), 0 26px 52px -18px rgba(0,0,0,.85);
  --lift-sm: 0 1px 2px rgba(0,0,0,.5), 0 6px 16px -8px rgba(0,0,0,.65);
  --mark:    var(--signal-ink);
  --scrim:   color-mix(in srgb, var(--case) 78%, transparent);
}

/* ============================================================================
   1. GROUND RULES
   ========================================================================== */

body { font-size: var(--t-1); line-height: 1.55; }

h1, h2, h3, h4 {
  font-family: var(--bill); font-weight: 700;
  text-transform: uppercase; letter-spacing: .045em;
  line-height: 1.05; text-wrap: balance;
}
/* .as-h1 is a heading demoted to h2 so the shell carries ONE h1 (the room's
   plate) — the story title and «YOUR FIRST PAGE» keep the first rank's
   size and set wherever the h1 rules below and in head.html reach. */
h1, .as-h1 { font-size: var(--t-7); margin: 0 0 var(--s2); }
h2 { font-size: var(--t-5); margin: 0 0 var(--s3); }
h3 { font-size: var(--t-2); margin: 0 0 var(--s3); }

/* The one register that is not the display face: a tracked sans label. Used
   for every "what is this" line that is not a heading. */
.smallcaps, .scene-head .vy, .tl .tl-date,
.book .b-author, .issue .no, .find-card .fc-cat, .frontier-card .fr-lab {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase; color: var(--steel);
}
.muted { color: var(--steel); }
.faint { color: var(--ink-3p); }

/* Anything that quotes Russian prose is set in the page face, never the UI
   face. Anything that counts is set in the UI face, because Geologica is the
   only one of the three carrying tnum. */
.plan-line, .aim-line, .ask-card .confess, .ask-card .q, .found-object,
.almanac, .greeting, .rev-attr, .issue .en, .book .b-year { font-family: var(--serif); }

.view { padding: var(--s6) var(--s5) 14vh; }
/* THE TAILPIECE. A printed page does not simply run out of paper — it closes
   with an ornament on the axis. Ends the third of a screen of bare wall that
   used to trail under every room. */
.view.show::after {
  content: '❦';
  display: block; text-align: center;
  margin-top: 72px;
  color: var(--rule-room); font-size: 1.15rem;
  opacity: .7;
}

/* THE MASTHEAD IS DELIBERATELY NOT HERE. head.html now composes it as a
   full-bleed left-aligned band on --case with a diagonal cut and its own
   rule — a whole composition, not a defaulted heading. An earlier draft of
   this file centred .masthead .rule and re-coloured .room-line, which was
   right against the masthead that existed then and is wrong against this one.
   The only masthead rule left here is the h1 size in the scale above, which
   that composition overrides with its own clamp. */

/* ============================================================================
   2. THE HEADER — a masthead rule, not a floating bar
   ========================================================================== */

/* THE HEADER IS A GALLEY, NOT THE ROOM. It was `color-mix(room 94%,
   transparent)`, which is two faults in one declaration: the reading sheet
   ghosted through it on scroll, and it put UI text on --room. The room is
   display type only — that is not a preference, it is what Lc 32.9 for steel
   on room forces — and a nav rail of eight sans labels is UI text. On galley
   the same labels measure 75.6 for steel, 90.6 for carbon and 73.5 for the
   active tab's signal-ink; on room they were 47.1, 62.1 and 45.0. */
header {
  background: var(--galley);
  border-bottom: 2px solid var(--carbon);
}
.bar { padding: 0 var(--s4); gap: var(--s2); min-height: 52px; }

.nav-tabs { gap: 0; align-items: stretch; }
.nav-tabs button {
  font-family: var(--sans); font-size: var(--t-0); font-weight: 500;
  letter-spacing: .02em; color: var(--steel);
  padding: 0 var(--s3); min-height: 44px; border-radius: 0; flex: none;
}
.nav-tabs button:hover { color: var(--carbon); }
.nav-tabs button.active { color: var(--carbon); font-weight: 600; }
/* the active tab is ruled in ink, flush and square — a drawer pulled out */
.nav-tabs button::after {
  left: var(--s2); right: var(--s2); bottom: 6px; height: 2px;
  background: var(--carbon); border-radius: 0;
}

/* The three rooms as three squares of stock rather than three dots. 34x44 on
   the desk, 44x44 once the pointer is a thumb. */
.theme-dots { gap: 0; border: 1px solid var(--carbon); }
.theme-dots button {
  width: 34px; height: 44px; border-radius: 0; padding: 0;
  border: none; border-right: 1px solid var(--carbon); box-shadow: none;
}
.theme-dots button:last-child { border-right: none; }
.theme-dots button.on { box-shadow: inset 0 0 0 3px var(--carbon); transform: none; }
.theme-dots button:hover { transform: none; }

/* ============================================================================
   3. CARDS AND PANELS — sheets of stock
   ========================================================================== */

.card,
.overlay .panel,
.scene-end,
.ticket,
.prep-banner,
.find-card,
.issue,
.glesson-card,
.gex,
.tr-reveal,
.ask-card {
  border-radius: 0;
  background: var(--galley);
  border: 1px solid var(--edge);
  box-shadow: none;
}
.card {
  padding: var(--pad-y) var(--pad-x);
  margin-bottom: var(--s4);
}

/* THE PANEL HEAD. A solid bar of ink, full bleed, label reversed out in the
   display face. Lc −91.0 light, 95.2 dark, −90.2 sepia — past the body floor
   in all three rooms, which is what lets a heading this small be this small.

   The !important is not decoration. app.js writes `style="margin:0 0 2px"` and
   `style="font-size:1.4rem"` onto several of these h3s inline, and app.js
   belongs to another builder right now. An inline margin would leave the bar
   floating 20px inside the card it is supposed to cap. */
.card > h3,
.card > .bill-head,
.overlay .panel > h3,
.scene-end > h3 {
  margin: calc(var(--pad-y) * -1) calc(var(--pad-x) * -1) var(--s4) !important;
  padding: 5px var(--s4) 6px !important;
  background: var(--bar); color: var(--on-bar);
  font-family: var(--bill); font-size: var(--t-1) !important; font-weight: 700;
  text-transform: uppercase; letter-spacing: .12em; line-height: 1.2;
  text-align: left !important; text-wrap: nowrap;
}
/* a head that is not the first thing in the card caps a section, not the card.
   THE GUARD MUST COVER EVERY SURFACE THE BAR DOES (2026-08-23,
   owner-photographed). The rule above pulls a cap bar out to the card's edges
   with a negative top margin, which is right for a head that opens its card
   and wrong for one that does not — and the exception was written for `.card`
   only while the bar was granted to four selectors. The rank's papers put a
   «class 10 of 14» line above their title, so in `#key-panel` the h3 was
   hauled up by a whole pad and printed straight over it: two texts, one
   space, the German rank name «SCHNEIDERGESELLE» sitting on top of the line
   that named its class. Any panel that opens with a standfirst before its
   title had the same fault waiting. */
.card > h3:not(:first-child),
.card > .bill-head:not(:first-child),
.overlay .panel > h3:not(:first-child),
.scene-end > h3:not(:first-child) {
  margin-top: var(--s5) !important;
}
/* nested heads keep the display face but not the bar — a card has one cap */
.card h3, .overlay .panel h3 {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .1em;
}

/* the overlay panel is a sheet lifted clear of the room */
.overlay { background: var(--scrim); padding: var(--s5); }
/* dvh, not vh: on a phone the URL bar eats vh, and 88vh of panel left the
   bottom of every ceremony under the browser chrome */
.overlay .panel { max-height: min(88vh, calc(100dvh - 28px)); }
/* THE DOORS NEVER LEAVE THE SCREEN. The ceremony's panel scrolls — six
   residents, a name, an address — and its action row used to scroll away
   with it, so "continue" lived below the fold at exactly the moment a new
   reader was being asked to commit. The row now rides the bottom of the
   panel's viewport until the content above it has been read past. */
#intro-panel > .btnrow {
  position: sticky; bottom: 0; z-index: 2;
  margin-left: -28px; margin-right: -28px; margin-bottom: -14px;
  padding: 12px 28px 14px;
  background: var(--galley);
  border-top: 1px solid var(--rule);
}
.overlay .panel {
  border-color: var(--edge-hi);
  box-shadow: var(--lift-lg);
  padding: var(--pad-y) var(--pad-x);
  max-height: 86vh;
}

/* the resumption card keeps its ink spine, squared off */
.resume-card { border-left: 5px solid var(--signal); }
.prep-banner { border-left: 5px solid var(--signal); padding: var(--s3) var(--s4); font-size: var(--t-1); }

/* ============================================================================
   4. BUTTONS — flat stamps
   ========================================================================== */

.btn,
.icon-btn,
.filters button,
.conf-row button,
.rev-btns button,
.quiz-opt,
.ltile,
.keys button,
#pop .row button,
#pop .row a,
.ask-card .opts button,
.guide-again,
.plan-opt,
.read-on,
.exlibris {
  font-family: var(--sans); font-size: var(--t-1); font-weight: 500;
  letter-spacing: .01em; line-height: 1.2;
  color: var(--carbon);
  background: var(--galley);
  border: 1px solid var(--edge);
  border-radius: 0;
  box-shadow: var(--lift-sm);
  cursor: pointer;
}
.btn, .rev-btns button, .conf-row button, .filters button,
.keys button, .ask-card .opts button, .plan-opt {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--s2);
  min-height: 44px; padding: var(--s2) var(--s4);
}
.icon-btn { min-height: 44px; min-width: 44px; padding: 0 var(--s3); font-size: var(--t-1); }

/* THE ASK CARD IS A QUESTIONNAIRE, NOT A KEYPAD. Its choices are quotations —
   titles of books, in the page face, ranged left the way a list is set. A
   centred bold sans label would make three stamps out of three sentences. */
.ask-card .opts button {
  justify-content: flex-start; text-align: left;
  font-family: var(--serif); font-weight: 500;
}

/* A stamp is pressed: it travels onto its own shadow and the shadow goes.
   head.html already declares a transform transition for these, so this is a
   resting state and its opposite, not a new animation. */
.btn:hover, .icon-btn:hover, .filters button:hover, .conf-row button:hover,
.rev-btns button:hover, .quiz-opt:hover, .ltile:hover, .keys button:hover,
#pop .row button:hover, .ask-card .opts button:hover, .guide-again:hover {
  background: var(--galley);        /* an earlier patch reached for --sheet on
                                       hover, which puts the reading plane back
                                       on every button in the product */
  border-color: var(--signal-ink); box-shadow: var(--lift); transform: none;
}
/* Pressed, the plate goes INTO the paper: one pixel of travel, the shadow
   gone, the ink a shade deeper. The violent full-invert is struck — nothing
   in this room snaps to black under the hand. */
.btn:active, .icon-btn:active, .filters button:active, .rev-btns button:active,
.quiz-opt:active, .ltile:active, .keys button:active, #pop .row button:active {
  transform: translateY(1px); box-shadow: none;
  background: color-mix(in srgb, var(--carbon) 9%, var(--galley));
  color: var(--carbon); border-color: var(--carbon);
}

/* THE PRIMARY ACTION — the only large signal fill in the product. Its label
   takes --on-signal, which is a dark ink in every room, because the fill is
   the same yellow in every room. It was --carbon for a while, and --carbon
   flips: at night this button printed cream on gold at 1.53:1. */
.btn.primary, .rev-btns .primary, #pat-next, .plan.best {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--on-signal);
  font-weight: 600;
}
/* сурик does not turn into ink under the pointer. An earlier patch swapped the
   primary button to carbon on hover with a --room label — Lc 62.1, and a
   colour change that large on hover reads as a different button. */
.btn.primary:hover, .rev-btns .primary:hover,
.btn.primary:active, .rev-btns .primary:active {
  background: var(--signal); border-color: var(--signal); color: var(--on-signal);
}
.btn.primary:hover, .rev-btns .primary:hover { box-shadow: var(--lift); }

/* Selected is INK, not red. A filter chip, a toggled tool and a marked word
   are all "this one" — they are not all "something changed". */
.filters button.active,
.icon-btn.on,
#pop .row button.on2,
.nav-key.on {
  background: var(--carbon); border-color: var(--carbon); color: var(--galley);
}
#pop .row button.on1 { background: var(--case); border-color: var(--edge); }

.btn:disabled, .icon-btn:disabled, .ltile:disabled, .quiz-opt:disabled,
.rev-btns button:disabled, button[disabled] {
  color: var(--ink-3p); border-color: var(--rule);
  background: var(--galley); box-shadow: none;
  cursor: default; opacity: 1;                 /* opacity dims the edge too */
}

/* A KEYBOARD RING THAT IS NOT THE BROWSER'S. сурик as a mark, offset so the
   ring never touches the stamp's own edge. On the primary button the ring
   would be red on red, so it flips to ink. */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--mark);
  outline-offset: 2px;
}
.btn.primary:focus-visible, .rev-btns .primary:focus-visible {
  outline-color: var(--carbon);
}

/* text-only controls: the link register */
.k-linkish, .aim-line .aim-edit, .memlist .mem-x, .ask-card .later,
#tour-panel .t-skip, #tour-panel .t-next, .tr-reveal .tr-tools button {
  font-family: var(--sans); font-size: var(--t-0); letter-spacing: .1em;
  text-transform: uppercase; background: none; border: none; box-shadow: none;
  color: var(--signal-ink); cursor: pointer; padding: var(--s2) 0;
  min-height: 44px;
}
.k-linkish:hover, .memlist .mem-x:hover, .aim-line .aim-edit:hover { color: var(--carbon); }
#tour-panel .t-skip, .ask-card .later { color: var(--steel); }
.guide-again { border-style: solid; border-radius: 0; font-size: var(--t-00);
  letter-spacing: .14em; padding: 0 var(--s4); min-height: 44px; }

/* ============================================================================
   5. FORMS — the last place a default browser control survives
   ========================================================================== */

input[type="text"], input[type="search"], input[type="email"],
input[type="password"], input[type="number"], textarea, select,
.mnem-box, .filters input, .set-row select, .set-row input[type="text"] {
  appearance: none; -webkit-appearance: none;
  font-family: var(--sans); font-size: var(--t-1); line-height: 1.3;
  color: var(--carbon);
  background-color: var(--sheet);   /* an input is the one place you write, so
                                       it is the one piece of chrome that is
                                       allowed to be stock. Lc 101.1 / −94.4 */
  border: 1px solid var(--edge);
  border-radius: 0;
  box-shadow: inset 2px 2px 0 color-mix(in srgb, var(--carbon) 8%, transparent);
  min-height: 44px;
  padding: var(--s2) var(--s3);
  max-width: 100%;
}
textarea, .mnem-box { padding: var(--s3); line-height: 1.5; resize: vertical; }
::placeholder { color: var(--ink-3p); opacity: 1; }
input:hover, select:hover, textarea:hover { border-color: var(--signal-ink); }

/* The select's own arrow is drawn by the platform and there is no version of
   it that belongs in this room. Two hard-edged gradient triangles instead —
   zero bytes, no image, and it inherits the ink. */
select, .set-row select {
  padding-right: var(--s7);
  background-image:
    linear-gradient(45deg,  transparent 50%, var(--carbon) 50%),
    linear-gradient(135deg, var(--carbon) 50%, transparent 50%);
  background-size: 5px 5px, 5px 5px;
  background-position: right 17px center, right 12px center;
  background-repeat: no-repeat;
}

/* A CHECKBOX AS A COMPARTMENT. The control is 44x44 so a thumb can hit it, but
   what is painted is a 22px square compartment; checked is a сурик sort
   dropped into it. Built from background layers rather than a border, because
   a border follows the 44px hit box and the hit box must stay invisible. */
input[type="checkbox"], input[type="radio"] {
  appearance: none; -webkit-appearance: none;
  width: 44px; height: 44px; margin: 0 -11px; flex: none;
  border: none; background: transparent; box-shadow: none; cursor: pointer;
  background-repeat: no-repeat;
  background-position: center, center;
  background-size: 20px 20px, 22px 22px;
  background-image:
    linear-gradient(var(--sheet), var(--sheet)),
    linear-gradient(var(--edge),  var(--edge));
}
input[type="checkbox"]:checked, input[type="radio"]:checked {
  background-position: center, center, center;
  background-size: 12px 12px, 20px 20px, 22px 22px;
  background-image:
    linear-gradient(var(--mark),  var(--mark)),
    linear-gradient(var(--sheet), var(--sheet)),
    linear-gradient(var(--edge),  var(--edge));
}

/* the reading-rate slider: a hairline track and one slug of type for a thumb */
input[type="range"] {
  appearance: none; -webkit-appearance: none;
  height: 44px; background: transparent; accent-color: var(--signal);
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 2px; background: var(--carbon); border: none; border-radius: 0;
}
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; width: 10px; height: 22px; margin-top: -10px;
  background: var(--signal); border: 1px solid var(--carbon); border-radius: 0;
}
input[type="range"]::-moz-range-track { height: 2px; background: var(--carbon); }
input[type="range"]::-moz-range-thumb {
  width: 10px; height: 22px; background: var(--signal);
  border: 1px solid var(--carbon); border-radius: 0;
}
#rate { width: 84px; }

/* the typed-answer field: still a rule to write on, now square and tnum-free */
.type-input {
  background: transparent; border: none; box-shadow: none;
  border-bottom: 2px solid var(--signal); border-radius: 0;
  font-family: var(--serif); font-size: var(--t-5); text-align: center;
}
.type-input.good { border-bottom-color: var(--carbon); }
.type-input.bad  { border-bottom-color: var(--signal-ink); }
.type-input:focus { border-bottom-color: var(--carbon); }

/* a settings row is a ruled line in a ledger */
.set-row {
  gap: var(--s4); padding: var(--s2) 0; min-height: 52px;
  border-bottom: 1px solid var(--rule); font-size: var(--t-1);
}
.set-row .lab { line-height: 1.4; }
/* A <select> is as wide as its longest option, and "I read on my own —
   translation on tap" is 355px of it, which folded the label beside it into
   four lines. Capped at half the row; the control clips, the sentence does not. */
.set-row select, .set-row input[type="text"] { min-width: 140px; max-width: 50%; }
/* THE ROOM DIRECTORY IS THE ONE CONTROL IN HERE THAT IS NOT A CONTROL: two
   room names and a clause, laid beside a two-line label. It had no width rule
   of its own, so it took the row and squeezed «Reading rooms (each room keeps
   its own record…)» down to its min-content — one word per line, twelve lines
   deep, in both rooms. It is capped exactly like the selects it sits under,
   and the label is allowed to shrink past its longest word. */
.set-row .lab { flex: 1 1 auto; min-width: 0; }
/* ONE ROOM PER LINE, not a middot-joined run. Inline, the run wrapped mid-list
   in the 50% column and left the separator hanging at the end of a line —
   «The Russian Room / — you are here · / The German Room», which reads as
   three things rather than two. A directory is a list; it is set as one. */
.set-row .room-list { flex: 0 1 auto; max-width: 50%; text-align: right; line-height: 1.4;
  display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
/* a room's name is a name: «The Russian Room» must not break across two lines */
.set-row .room-list a, .set-row .room-list b { white-space: nowrap; }
/* the room being stood in: its name, and underneath it the fact, small */
.set-row .room-list .here { display: flex; flex-direction: column; align-items: flex-end; }
.set-row .room-list .here .faint { font-size: var(--t-2); line-height: 1.2; }

/* ============================================================================
   6. DATA — anything with a digit in a column gets tnum
   ========================================================================== */

.tile .v, .sum-stat .s-v, #wordlist .cnt, #readbar .left-note, .tick-n,
.frontier-card .fr-n, .bill .b-n, .bill .b-of, .rungs .r-o, .lad-line,
.meter-legend, .dow,
table td, table th, .p-amt, .p-per {
  font-family: var(--sans);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}

/* THE BIG FIGURE. It was Spectral at 2.6rem — a literary number. It is a count
   of objects the reader owns, so it is set in the face that has tnum, at the
   top of the scale, tight, with its unit demoted to metadata beside it. */
.frontier-card .fr-n {
  font-size: var(--t-7); font-weight: 700; line-height: .9;
  letter-spacing: -.02em; color: var(--carbon);
  display: flex; align-items: baseline; justify-content: center; gap: var(--s2);
  margin-top: var(--s3);
}
.frontier-card .fr-n .fr-d {
  font-size: var(--t-2); font-weight: 400; color: var(--steel);
  letter-spacing: 0;
}
.frontier-card .fr-lab { margin-top: var(--s3); }
.frontier-card .fr-sub {
  font-family: var(--serif); font-size: var(--t-1); font-style: italic;
  color: var(--steel); margin-top: var(--s3);
}
.lad-line { font-size: var(--t-4); font-weight: 700; margin-top: var(--s3); }
.lad-line b { font-size: var(--t-6); line-height: 1; }
.lad-note { font-size: var(--t-0); line-height: 1.55; color: var(--steel); margin: var(--s3) 0 0; }

/* THE BILL, and the ladder's rungs. app.js emits
   `<span class=b-ti>…</span><i></i><span class=b-n>42</span>` — the empty <i>
   is a leader waiting for a stylesheet that never came, which is why the Desk
   was rendering bulleted lines reading "Лошадиная фамилия42". */
.bill, .rungs {
  list-style: none; margin: var(--s4) 0 0; padding: 0;
}
.bill li, .rungs li {
  display: flex; align-items: baseline; gap: var(--s2);
  padding: var(--s2) 0; margin: 0;
  border-bottom: 1px solid var(--rule);
}
.bill li:last-child, .rungs li:last-child { border-bottom: none; }
.bill li > i, .rungs li > i {
  flex: 1 1 auto; min-width: var(--s5); align-self: baseline;
  border-bottom: 1px dotted var(--rule); transform: translateY(-.28em);
}
.bill .b-ti, .rungs .r-l { font-family: var(--serif); font-size: var(--t-1); }
.bill .b-n {
  font-size: var(--t-2); font-weight: 700; color: var(--carbon);
  min-width: 3.5ch; text-align: right;
}
.bill .b-of, .rungs .r-o { font-size: var(--t-0); color: var(--steel); }
/* each line of the bill in its book's own ink: the count carries the pigment,
   the dotted leader takes a breath of it — same provenance mark as the shelf */
.bill li .b-n { color: var(--ink, var(--carbon)); }
.bill li > i { border-bottom-color: color-mix(in srgb, var(--ink, var(--rule)) 40%, var(--rule)); }
.bill .b-x {
  font-family: var(--bill); font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--on-signal);
  background: var(--signal); padding: 1px var(--s2); margin-left: var(--s2);
}
.bill li.struck .b-ti { color: var(--steel); text-decoration: line-through; }
.bill-head, .un-head, .meter-legend {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .12em;
}
.un-head { font-size: var(--t-1); color: var(--carbon); margin-bottom: var(--s2); }
.unopened { margin-top: var(--s5); border-top: 2px solid var(--carbon); padding-top: var(--s4); }
.un-note { font-size: var(--t-0); line-height: 1.55; color: var(--steel); margin-top: var(--s3); }

/* THE TILE GRID — a galley: compartments divided by printer's rule, not by
   twelve separate rounded boxes floating on a card.

   Built from each tile's own LEFT/TOP rule pulled back by -1px so adjacent
   tiles share one hairline. Two rejected alternatives, both measured in the
   browser rather than reasoned about:

   - 1px grid gaps over a carbon container. Five tiles in a two-column grid
     leave a sixth cell with no tile in it, and that cell rendered as a solid
     block of carbon on the Desk.
   - right/bottom rules with -1px right/bottom margins. Grid items paint in
     document order, so tile N+1's background lands on top of tile N's right
     border and the rule disappears: measured, tile 0 ended at x=877.42 and
     tile 1 began at x=876.42. Left/top rules put the border on the item that
     paints LAST, which is the one that wins. The first row and column overlap
     the container's own border, and that is harmless because both are --edge. */
.tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(112px, 1fr));
  gap: 0; background: var(--galley); border: 1px solid var(--edge);
  margin-top: var(--s2);
}
.tile {
  background: var(--galley); border: none; border-radius: 0;
  border-left: 1px solid var(--edge); border-top: 1px solid var(--edge);
  margin: -1px 0 0 -1px;
  padding: var(--s3);
}
.tile .v { font-size: var(--t-5); font-weight: 700; line-height: 1; letter-spacing: -.01em; }
.tile .l {
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .1em;
  text-transform: uppercase; color: var(--steel); margin-top: var(--s2);
}

/* METERS. Track is the empty compartment; the fill is what has been cast into
   it. In the dark room --signal on a track measures Lc 0.0 against --rule, so
   the fill is --mark, which is the same finding head.html made for the 2px
   underline under a word. */
/* No border on a track, and the track stays --hm0.

   Two measurements decided both halves of that. app.js writes
   `style="height:4px"` onto the meter on every one of the fourteen shelf
   cards, so a 1px rule top and bottom leaves 2px of track and the coverage bar
   renders as a solid black rule. And --case as the track fails the thing a
   track exists for: сурик on --case is Lc 15.1 in the light room and --case
   on --galley is Lc 0.0 in the dark one, so either the fill or the track
   disappears depending on the room. --hm0 is head.html's own per-room answer
   for exactly this bar (--rule light and sepia, --rule-room dark) and it gives
   the fill Lc 42.2 / −51.4 / 43.5 to sit on. */
.meter, .warmbar {
  border-radius: 0; background: var(--hm0);
  border: none; box-shadow: none; overflow: hidden;
}
.meter { height: 8px; }
.meter > i, .warmbar > i { border-radius: 0; }
.meter > i.retr { background: var(--mark); }
.meter > i.storage { background: var(--steel); opacity: 1; }
.warmbar { height: 6px; }
.warmbar > i, .warmbar.cold > i { background: var(--mark); }
.meter .tick { background: var(--galley); top: 0; height: 100%; }
.meter .tick.t98 { background: var(--carbon); width: 2px; }
.meter-legend {
  font-size: var(--t-00); color: var(--steel); margin-top: var(--s2);
  letter-spacing: .1em;
}
.session-ring .track { stroke: var(--case); }
.session-ring .prog { stroke: var(--mark); stroke-linecap: butt; }

/* the reading calendar: a drawer of compartments sharing their hairlines. Same
   reason as the tiles — thirty days do not fill five rows of seven, and the
   leftover cells must be galley, not a hole cut in the card. */
/* The calendar takes the gap treatment rather than the tiles' border trick,
   because here the rule between compartments IS the card surface: 1px of
   --galley showing through. That also solves the leftover cells for free —
   thirty days do not fill five rows of seven, and what shows in the gap and
   what shows in an empty cell are the same colour by construction. */
.ledger { gap: 1px; background: var(--galley); border: 1px solid var(--edge); margin-top: var(--s2); }
.ledger + .ledger { border-top: none; margin-top: 0; }
/* A day with no reading in it is an EMPTY COMPARTMENT, not a pale tint of the
   heat ramp. --hm0 is --rule-room in the dark room, one step off the --rule
   next to it, so thirty unread days rendered as one olive block. --case reads
   as a recess in every room. The four heat steps still win on specificity. */
.ledger .dcell { background: var(--case); border: none; margin: 0; border-radius: 0; }
.ledger .dcell.today { outline: 2px solid var(--mark); outline-offset: -2px; z-index: 1; }
.dow {
  font-family: var(--sans); font-size: var(--t-00); color: var(--steel);
  background: var(--galley); padding: 3px 0; letter-spacing: .04em;
  border: none; margin: 0;
}

/* generic tables, for anything a lesson brings with it */
table { width: 100%; border-collapse: collapse; font-size: var(--t-1); }
th, td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--rule); text-align: left; }
th {
  font-family: var(--bill); font-size: var(--t-0); text-transform: uppercase;
  letter-spacing: .12em; color: var(--on-bar); background: var(--bar);
  border-bottom: none;
}
td:not(:first-child), th:not(:first-child) { text-align: right; }

/* ============================================================================
   7. LISTS AND ROWS
   ========================================================================== */

#wordlist li {
  gap: var(--s4); padding: var(--s3) 0;
  border-bottom: 1px solid var(--rule); align-items: baseline;
}
#wordlist .wf { font-family: var(--serif); font-size: var(--t-2); min-width: 8.5em; }
#wordlist .wf:hover { color: var(--signal-ink); }
#wordlist .wg { min-width:0; font-size: var(--type-body); line-height:1.6; overflow-wrap:anywhere; color: var(--steel); }
#wordlist .cnt { font-size: var(--type-meta); color: var(--steel); }
.filters { gap: var(--s2); margin: var(--s4) 0 var(--s5); }
.filters input { margin-left: auto; min-width: 180px; }

.set-row:last-child, .sum-stat:last-child, .memlist li:last-child { border-bottom: none; }
.sum-stat { padding: var(--s3) 0; border-bottom: 1px solid var(--rule); gap: var(--s3); }
.sum-stat .s-ic {
  border-radius: 0; width: 36px; height: 36px;
  background: var(--case) !important;      /* the summary writes its tint inline;
                                              the booth keeps its own compartment */
  border: 1px solid var(--edge); color: var(--carbon);
}
.sum-stat .s-v { font-size: var(--t-2); font-weight: 700; color: var(--carbon); }
.sum-stat { font-size: var(--t-1); }

.memlist li { padding: var(--s3) 0; border-bottom: 1px solid var(--rule); font-size: var(--t-1); gap: var(--s4); }

/* seals are cast sorts — only impressions actually taken are ever set;
   the case holds no empty compartments and counts nothing still owed */
.seals { gap: var(--s2); }
.seal, .ach {
  border-radius: 0; background: var(--case);
  border: 1px solid var(--edge); box-shadow: none;
  color: var(--steel); font-family: var(--bill); font-weight: 700;
}
.ach { width: 48px; height: 48px; font-size: var(--t-3); }
.ach.got { background: var(--signal); color: var(--on-signal); border-color: var(--signal); }

/* the journey timeline: a ruled margin with square studs */
.tl { border-left: 2px solid var(--carbon); margin-left: var(--s2); padding-left: var(--s6); }
.tl .tl-day { margin-bottom: var(--s6); }
.tl .tl-day::before {
  border-radius: 0; background: var(--signal);
  width: 9px; height: 9px; left: -30px; top: 6px;
}
.tl .tl-ev { font-size: var(--type-body); line-height:1.6; overflow-wrap:anywhere; margin: var(--s1) 0; }

/* THE PATENT OF RANK — the journey's masthead is a civil-service document:
   the class numeral engraved at the left, the pre-reform Russian name in
   display serif, the honorific, and the climb to the next rung as a rule
   that is exactly as long as the reading is. */
#journey-patent .jp-doc {
  display: flex; gap: 18px; align-items: center;
  background: var(--sheet); border: 1px solid var(--carbon);
  box-shadow: var(--lift-sm);
  padding: 18px 20px; margin: 10px 0 4px;
}
#journey-patent .jp-cls {
  flex: none; width: 74px; height: 74px; border-radius: 50%;
  display: grid; place-items: center;
  font-family: var(--bill); font-size: 2rem; font-weight: 700;
  color: var(--gold-leaf);
  background: radial-gradient(circle at 38% 32%,
    color-mix(in srgb, var(--gold-leaf) 16%, var(--sheet)), var(--sheet) 70%);
  border: 2px solid color-mix(in srgb, var(--gold-leaf) 55%, var(--carbon));
  box-shadow: inset 0 0 0 5px var(--sheet),
              inset 0 0 0 6px color-mix(in srgb, var(--gold-leaf) 45%, transparent);
}
#journey-patent .jp-ru {
  font-family: var(--serif); font-size: 1.35rem; font-weight: 700;
  margin-top: 2px;
}
#journey-patent .jp-en { font-size: var(--type-body); color: var(--steel); }
#journey-patent .jp-mid { min-width:0; max-width:65ch; overflow-wrap:anywhere; }
#journey-patent .jp-mid .smallcaps { font-size:var(--type-meta); line-height:1.5; }
#journey-patent .jp-note {
  font-family: var(--serif); font-style: italic; font-size: var(--type-body);
  color: var(--steel); margin-top: 3px;
}
#journey-patent .jp-rise {
  height: 3px; background: var(--rule); margin-top: 10px; position: relative;
  overflow: hidden;
}
#journey-patent .jp-rise i {
  position: absolute; inset: 0 auto 0 0; width: calc(var(--p, 0) * 1%);
  background: var(--signal); transition: width 700ms var(--e-std, ease);
}
#journey-patent .jp-next {
  font-size: var(--type-meta); color: var(--steel); margin-top: 5px;
  font-variant-numeric: tabular-nums;
}

/* THE CABINET OF SEALS — earned impressions pressed in wax; the unearned
   BLIND-STAMPED into the paper: embossed, uninked, naming their price —
   which is what makes them worth wanting. Nothing here is a badge grid:
   every impression sits a hair off true, as a hand leaves them. */
.jseals {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%,240px), 1fr));
  gap: 10px;
}
.jseal {
  display: grid; grid-template-columns: 44px minmax(0,1fr);
  grid-template-areas: "wax t" "wax d";
  column-gap: 11px; align-items: center;
  background: var(--galley); border: 1px solid var(--rule);
  padding: 10px 11px; min-height: 60px;
}
/* the hand's scatter: neighbours never sit identically */
.jseal:nth-child(3n)   { transform: rotate(.4deg); }
.jseal:nth-child(3n+1) { transform: rotate(-.3deg); }
.jseal-wax {
  grid-area: wax; width: 42px; height: 42px; border-radius: 50%;
  display: grid; place-items: center; font-size: 1rem;
  color: color-mix(in srgb, var(--steel) 72%, transparent);
  border: 1px solid var(--rule);
  background: var(--galley);
  /* a blind stamp: relief without ink */
  box-shadow: inset 0 1.5px 3px color-mix(in srgb, var(--carbon) 16%, transparent),
              inset 0 -1px 1px rgba(255,255,255,.7);
}
.jseal-wax .ic { width: 1.35em; height: 1.35em; }
.jseal-t { grid-area: t; font-family: var(--bill); font-weight: 700; font-size: var(--type-body); letter-spacing: .05em; text-transform: uppercase; overflow-wrap:anywhere; }
.jseal-d { grid-area: d; font-size: var(--type-meta); color: var(--steel); line-height: 1.5; overflow-wrap:anywhere; }
.jseal.got { border-color: var(--carbon); background: var(--sheet); box-shadow: var(--lift-sm); }
.jseal.got .jseal-wax {
  border: none; color: var(--on-signal);
  background: radial-gradient(circle at 36% 30%,
    color-mix(in srgb, var(--signal) 82%, white), var(--signal) 62%,
    color-mix(in srgb, var(--signal) 72%, black));
  box-shadow: 0 1px 3px rgba(0,0,0,.35), inset 0 0 0 3px rgba(255,255,255,.14),
              inset 0 -2px 4px rgba(0,0,0,.22);
  rotate: -4deg;
}
.jseal.got:nth-child(even) .jseal-wax { rotate: 3deg; }
.jseal.got:nth-child(5n) .jseal-wax { rotate: -7deg; }

/* THE PLATES. When an impression has its cut badge (ui/seals.js), the wax
   circle steps aside — the badge brings its own lit base. Unearned plates
   hang as blind stamps: form without ink, which is what makes them wanted. */
.jseal-wax:has(.seal-art) {
  width: 52px; height: 52px;
  border: none; border-radius: 0;
  background: none; box-shadow: none;
}
/* the earned-coin paint must ALSO stand down when a plate is present —
   :has() carries only its argument's specificity, and the .got rule was
   quietly repainting a gold square underneath every badge */
.jseal.got .jseal-wax:has(.seal-art) {
  background: none;
  box-shadow: none;
  border: none;
  width: 52px; height: 52px;
}
.seal-art { display: grid; place-items: center; width: 100%; height: 100%; }
.seal-art svg { width: 100%; height: 100%; display: block; }
.jseal:not(.got) .seal-art { filter: grayscale(1) opacity(.38) contrast(.9); }
.jseal.got .seal-art { filter: drop-shadow(0 2px 3px rgba(20, 24, 40, .3)); }
.jseal:has(.seal-art) { grid-template-columns: 52px 1fr; }

/* THE CHAMELEON'S DIAGNOSIS — one quiet plate on the reveal, once per word */
.rev-cham {
  margin: 8px 0 2px;
  padding: 8px 12px;
  font-family: var(--serif);
  font-style: italic;
  font-size: .88rem;
  color: var(--carbon);
  border-left: 3px solid var(--signal, #a5342a);
  background: color-mix(in srgb, var(--signal, #a5342a) 6%, transparent);
}

/* A WHOLE MINUTE OF BLISS — the sitting closes on a line the reader owns */
.rev-bliss {
  margin: 16px auto 4px;
  max-width: 34em;
  padding: 14px 16px 10px;
  border-block: 2px solid var(--carbon);
  text-align: center;
}
.rev-bliss .rb-ru {
  font-family: var(--serif);
  font-size: 1.35rem;
  line-height: 1.45;
  margin: 0 0 4px;
}
.rev-bliss .rb-en {
  font-family: var(--serif);
  font-style: italic;
  font-size: .9rem;
  opacity: .75;
  margin: 0 0 6px;
}
.rev-bliss .rb-who {
  font-family: var(--bill);
  font-weight: 600;
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .6;
  margin: 0;
}

/* WHERE THE KNIFE STOPS — tomorrow's first line, uncut */
.rev-uncut {
  margin: 10px auto 0;
  max-width: 34em;
  text-align: center;
}
.rev-uncut .ru-line {
  font-family: var(--serif);
  font-size: 1.05rem;
  margin: 4px 0 0;
}
.rev-uncut .uncut-veil {
  filter: blur(3.5px);
  opacity: .55;
  user-select: none;
}

/* THE MINTING — the room a plate is struck in. The badge is assembled
   layer by layer (each little thing of the die arriving by hand, on a
   decelerating curve), then the press lands with the ONE sanctioned
   overshoot, a sheen is drawn once across the gold, and the finished
   plate settles into the house's four-second breath. */
.seal-panel {
  max-width: 380px;
  text-align: center;
  padding: 26px 24px 20px;
}
.seal-panel .sc-stage {
  position: relative;
  width: 148px; height: 148px;
  margin: 0 auto 8px;
  display: grid; place-items: center;
}
/* the rays wake behind the die — smolder-slow, never a spin you can feel */
.seal-panel .sc-rays {
  position: absolute; inset: -26px;
  background: conic-gradient(from 0deg,
    rgba(246, 196, 69, .22) 0deg 9deg, transparent 9deg 30deg,
    rgba(246, 196, 69, .22) 30deg 39deg, transparent 39deg 60deg,
    rgba(246, 196, 69, .22) 60deg 69deg, transparent 69deg 90deg,
    rgba(246, 196, 69, .22) 90deg 99deg, transparent 99deg 120deg,
    rgba(246, 196, 69, .22) 120deg 129deg, transparent 129deg 150deg,
    rgba(246, 196, 69, .22) 150deg 159deg, transparent 159deg 180deg,
    rgba(246, 196, 69, .22) 180deg 189deg, transparent 189deg 210deg,
    rgba(246, 196, 69, .22) 210deg 219deg, transparent 219deg 240deg,
    rgba(246, 196, 69, .22) 240deg 249deg, transparent 249deg 270deg,
    rgba(246, 196, 69, .22) 270deg 279deg, transparent 279deg 300deg,
    rgba(246, 196, 69, .22) 300deg 309deg, transparent 309deg 330deg,
    rgba(246, 196, 69, .22) 330deg 339deg, transparent 339deg 360deg);
  border-radius: 50%;
  -webkit-mask-image: radial-gradient(circle, transparent 38%, #000 46%, transparent 72%);
  mask-image: radial-gradient(circle, transparent 38%, #000 46%, transparent 72%);
  opacity: 0;
}
body:not(.motion-low) .seal-panel .sc-rays {
  animation: sc-rays-in 1.2s ease-out .3s forwards, sc-rays-turn 60s linear infinite;
}
@keyframes sc-rays-in { to { opacity: 1; } }
@keyframes sc-rays-turn { to { transform: rotate(360deg); } }
.seal-panel .sc-plate {
  position: relative;
  width: 128px; height: 128px;
  border-radius: 50%;
  overflow: hidden;
}
.seal-panel .sc-plate svg { width: 100%; height: 100%; display: block; }
/* each layer of the die lays itself down — decelerate, seep, settle */
.seal-panel .sc-plate svg > :not(defs) { transform-box: fill-box; transform-origin: center; }
body:not(.motion-low) .sc-plate.minting svg > :not(defs) {
  opacity: 0;
  animation: sc-part .5s cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0s) forwards;
}
@keyframes sc-part {
  from { opacity: 0; transform: translateY(6px) scale(.92); }
  to   { opacity: 1; transform: none; }
}
/* the press lands: the one sanctioned overshoot, plate-wide */
body:not(.motion-low) .sc-plate.struck {
  animation: sc-press .62s cubic-bezier(.34, 1.4, .5, 1) both;
}
@keyframes sc-press {
  0%   { transform: scale(1.14); }
  55%  { transform: scale(.965); }
  100% { transform: scale(1); }
}
/* one sheen, drawn across the gold by the strike — then never again */
.sc-plate.struck::after {
  content: '';
  position: absolute; inset: 0;
  background: linear-gradient(115deg, transparent 32%, rgba(255, 253, 245, .55) 46%, rgba(255, 253, 245, .1) 54%, transparent 66%);
  transform: translateX(-130%);
  pointer-events: none;
}
body:not(.motion-low) .sc-plate.struck::after {
  animation: sc-sheen .8s cubic-bezier(0.16, 1, 0.3, 1) .55s forwards;
}
@keyframes sc-sheen { to { transform: translateX(130%); } }
/* and then the plate breathes with the house — the brand's one tempo */
body:not(.motion-low) .sc-plate.struck svg {
  animation: sc-breath 4s ease-in-out 1.6s infinite;
}
@keyframes sc-breath {
  0%, 100% { transform: scale(1); }
  50%      { transform: scale(1.012); }
}

/* THE SIGNATURE LIFE — after the strike, ONE part of each die wakes:
   the flame flickers, the sound-arcs ripple out, a glint crosses the
   glass, the dangle sways once and rests. Ceremony-only (the cabinet
   grid stays still), phase-locked to the breath, nothing bounces. */
body:not(.motion-low) .sc-plate.struck [data-a="flicker"] {
  animation: a-flicker 4s ease-in-out 1.8s infinite;
}
@keyframes a-flicker {
  0%, 100% { transform: scaleY(1) rotate(0deg); opacity: 1; }
  30%      { transform: scaleY(1.06) rotate(-1.6deg); opacity: .93; }
  62%      { transform: scaleY(.965) rotate(1.2deg); }
}
body:not(.motion-low) .sc-plate.struck [data-a="ripple"] {
  animation: a-ripple 4s ease-out 1.8s infinite;
}
@keyframes a-ripple {
  0%, 55%, 100% { opacity: 1; }
  22%           { opacity: .35; }
}
body:not(.motion-low) .sc-plate.struck [data-a="glint"] {
  animation: a-glint 4s ease-in-out 2s infinite;
}
@keyframes a-glint {
  0%, 64%, 100% { opacity: var(--glint-rest, .6); transform: none; }
  76%           { opacity: 1; transform: translate(1.5px, -1px); }
}
body:not(.motion-low) .sc-plate.struck [data-a="sway"] {
  animation: a-sway 4s cubic-bezier(0.16, 1, 0.3, 1) 1.8s infinite;
}
@keyframes a-sway {
  0%, 100% { transform: rotate(0deg); }
  38%      { transform: rotate(2.4deg); }
  72%      { transform: rotate(-1.4deg); }
}
body:not(.motion-low) .sc-plate.struck [data-a="steam"] {
  animation: a-steam 4s ease-in-out 1.8s infinite;
}
@keyframes a-steam {
  0%, 100% { transform: translateY(0); opacity: .8; }
  50%      { transform: translateY(-2.2px); opacity: 1; }
}
.seal-panel .sc-name {
  font-family: var(--bill);
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: 1rem;
}
.seal-panel .sc-deed {
  font-family: var(--serif);
  font-style: italic;
  font-size: .9rem;
  opacity: .75;
  margin-top: 2px;
}
.seal-panel .sc-reveal {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px solid var(--rule);
  font-family: var(--serif);
  font-size: .92rem;
  line-height: 1.5;
}
body:not(.motion-low) .seal-panel .sc-reveal {
  opacity: 0;
  animation: sc-seep .9s ease-out .9s forwards;
}
@keyframes sc-seep { to { opacity: 1; } }

/* THE RISE TO COMFORTABLE READING — what you have, not what you lack: carbon
   filling toward the 95% tick where prose goes quiet. */
.cov-rise {
  position: relative; height: 6px; background: var(--rule);
  margin: 6px 0 2px; overflow: hidden;
}
.cov-rise i {
  position: absolute; inset: 0 auto 0 0; width: calc(var(--p, 0) * 1%);
  background: var(--carbon);
}
.cov-rise b {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  left: calc(var(--at, 95) * 1%); background: var(--signal);
}

/* ============================================================================
   8. THE SHELF, THE INSTALMENTS, THE CURIOSITIES
   ========================================================================== */

/* 188px, not 212px: the shelf lives in the narrow column of .grid2, which is
   440px inside its card padding. Two 212px tracks plus a 12px gap is 436 —
   and it still collapsed to one column, because auto-fill counts the gap
   before the tracks. 188 leaves the margin that arithmetic needs. */
.shelf { gap: var(--s3); grid-template-columns: repeat(auto-fill, minmax(188px, 1fr)); }
.book {
  --spine: var(--carbon);
  border-radius: 0; border: 1px solid var(--edge);
  padding: var(--s4) var(--s4) var(--s4) var(--s6);
  box-shadow: var(--lift-sm);
  background: linear-gradient(90deg, var(--spine) 0 8px, var(--galley) 8px);
  min-height: 168px;
}
/* An author is not a colour. The spine is ink; it turns сурик under the
   pointer, which is the only thing on the shelf that has changed. */
.book[data-a] { --spine: var(--carbon); }
.book:hover { --spine: var(--signal); transform: none; box-shadow: var(--lift); }
.book:hover .b-title { color: var(--carbon); }
.book:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--edge); }
.book .b-title { font-family: var(--serif); font-size: var(--t-3); font-weight: 600; margin: var(--s1) 0 0; }
.book .b-year { font-size: var(--type-meta); color: var(--steel); }
.book .b-desc { font-size: var(--type-body); color: var(--steel); margin: var(--s2) 0 auto; line-height: 1.6; overflow-wrap:anywhere; }
.book .lvl, .gchip, .tag {
  border-radius: 0; border: 1px solid var(--edge); background: var(--galley);
  font-family: var(--bill); font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--carbon); padding: 2px var(--s2);
}
.book .lvl.l1 { border-color: var(--edge); color: var(--carbon); }
.book .ribbon { background: var(--signal); top: 0; width: 10px; height: 26px; }
.book .b-seal { bottom: var(--s2); right: var(--s2); width: 28px; height: 28px;
  transform: none; font-size: var(--t-0); display: grid; place-items: center; }

.issues { gap: var(--s3); }
.issue { padding: var(--s4); box-shadow: var(--lift-sm); }
.issue:hover { border-color: var(--signal-ink); transform: none; box-shadow: var(--lift); }
.issue:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--edge); }
.issue .ti { font-family: var(--serif); font-size: var(--t-2); margin: var(--s1) 0 0; }
.issue .en { font-size: var(--t-0); color: var(--steel); }
.issue .meta { font-size: var(--t-00); color: var(--steel); margin-top: var(--s3);
  letter-spacing: .08em; text-transform: uppercase; }
.issue.foxed::after { display: none; }        /* a radial stain has no place on
                                                 a plane with no light source */

.finds-list { gap: var(--s3); }
.find-card { padding: var(--s3) var(--s4); }
.find-card .fc-cat { color: var(--signal-ink); }
.find-card .fc-t { font-family: var(--serif); font-size: var(--t-2); margin: var(--s1) 0; }
.find-card .fc-b { font-size: var(--t-0); color: var(--steel); line-height: 1.55; }

.glist { gap: var(--s3); }
.glesson-card { padding: var(--s3) var(--s4); box-shadow: var(--lift-sm); min-height: 64px; }
.glesson-card:hover { border-color: var(--signal-ink); box-shadow: var(--lift); }
.glesson-card:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--edge); }
.glesson-card .gru { font-family: var(--serif); font-size: var(--t-2); }
.glesson-card .gen2 { font-size: var(--t-0); color: var(--steel); }

/* a worked example is a quotation block ruled in сурик */
.gex { border-left: 5px solid var(--signal); padding: var(--s3) var(--s4); margin: var(--s4) 0; }
.gex .gru-s { font-family: var(--serif); font-size: var(--t-2); line-height: 1.6; }
.gex .gen-s { font-size: var(--t-0); color: var(--steel); margin-top: var(--s2); }
/* red is never a sentence: the grammatical note is ink, and only its lead-in
   is сурик. --signal on galley in the dark room is Lc −21.1 as TEXT. */
.gex .gnote { font-size: var(--t-0); color: var(--steel); margin-top: var(--s2); }
.gex .gnote::before { content: '❦ '; color: var(--signal-ink); }
#lesson-body { font-size: var(--t-2); line-height: 1.65; max-width: 34em; }
#lesson-body p { margin: 0 0 var(--s4); }
.gchip { display: inline-block; margin-top: var(--s2); padding: var(--s1) var(--s3); }

/* ============================================================================
   9. THE STUDY CARD
   ========================================================================== */

/* The one other warm plane in the product, and it earns it: everything on it
   is a sentence of Russian literature being read. Everything AROUND it is
   still the room. */
#rev-card {
  background: var(--sheet); border: 1px solid var(--edge); border-radius: 0;
  box-shadow: var(--lift); padding: var(--s7) var(--s6); gap: var(--s3);
}
#rev-card .cloze { font-family: var(--serif); font-size: var(--t-3); line-height: 1.65; }
#rev-card .cloze .blank { color: var(--signal-ink); border-bottom: 2px solid var(--signal); }
#rev-card .opt { font-family: var(--serif); font-size: var(--t-2); }
#rev-card .cloze mark, .ex-main mark, .ex-more mark, .gex .gru-s mark {
  background: color-mix(in srgb, var(--signal) 18%, transparent);
  border-radius: 0; padding: 0 3px; color: inherit;
}
.produce-gloss { font-size: var(--t-3); }
.reveal-word { font-family: var(--serif); font-size: var(--t-6); font-weight: 600; }
.reveal-word.ok { color: var(--carbon); }
.reveal-word.bad { color: var(--signal-ink); }
.ex-main { font-size: var(--t-2); }
.ex-more { font-size: var(--t-1); border-left: 2px solid var(--rule); padding-left: var(--s3); }
.ex-more:hover { border-left-color: var(--signal); }
.ex-more .ex-en { font-size: var(--t-0); color: var(--steel); }
.more-ex { border-top: 1px solid var(--rule); margin-top: var(--s3); padding-top: var(--s3); }
/* The verdict row rides the bottom of the screen while a long reveal card
   (examples, roots, chips) scrolls past behind it — the hand never has to
   travel to say what it knew. Same recipe as the masthead: the room's own
   paper, barely thinned, so the card reads through it as through one sheet. */
.rev-btns {
  gap: var(--s2); margin-top: var(--s4);
  position: sticky; bottom: 0; z-index: 5;
  padding: var(--s3) 0 calc(var(--s3) + env(safe-area-inset-bottom, 0px));
  /* OPAQUE, because 92% paper and a 3px blur is not enough to stop a serif
     sentence reading through it: mid-scroll the recap's own headings showed
     up INSIDE the verdict row, sliced in half, which reads as a rendering
     fault rather than as a tray. A tray is a solid thing with an edge. */
  background: var(--paper);
  box-shadow: 0 -1px 0 var(--rule);
}
.rev-btns .b-again { border-color: var(--signal-ink); color: var(--signal-ink); }
.rev-btns .b-good  { border-color: var(--carbon); color: var(--carbon); }
.conf-row button { min-height: 44px; font-size: var(--t-0); color: var(--steel); }
.quiz-opt {
  display: block; width: 100%; text-align: left;
  min-height: 44px; padding: var(--s3) var(--s4); margin: var(--s2) 0;
  font-size: var(--t-1);
  /* the practice room obeys the course tiles' law: an answer never breaks
     its word mid-word (the owner's proof-tile find) */
  overflow-wrap: normal; word-break: keep-all; hyphens: none;
}
.quiz-opt.right { border-color: var(--carbon); background: var(--carbon); color: var(--galley); }
.quiz-opt.right::before { display: none; }
.quiz-opt.wrong { border-color: var(--signal-ink); color: var(--signal-ink); opacity: 1; background: var(--galley); }
.pat-opts { display: flex; flex-wrap: wrap; gap: var(--s2); justify-content: center; margin-top: var(--s4); }
.pat-opts .quiz-opt { display: inline-flex; width: auto; margin: 0; font-family: var(--serif); font-size: var(--t-2); }
.pat-why { font-size: var(--t-0); color: var(--steel); margin-top: var(--s4); line-height: 1.6; }
.rail-lab {
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .5em;
  color: var(--steel); text-align: center; margin-top: var(--s3);
}
.rail { display: flex; gap: 1px; justify-content: center; margin-top: var(--s1); }
.rail i { display: block; width: 22px; height: 5px; background: var(--case); }
.rail i.met { background: var(--steel); }
.rail i.now { background: var(--signal); }
.tilesrow { gap: var(--s2); margin: var(--s3) 0 var(--s1); }
.ltile {
  min-width: 44px; height: 44px; padding: 0 var(--s3);
  font-family: var(--serif); font-size: var(--t-3); font-weight: 600;
}
.built {
  font-family: var(--serif); font-size: var(--t-5); letter-spacing: .06em;
  border-bottom: 2px solid var(--carbon); min-width: 200px; padding: 0 var(--s3);
}
.built.ok  { color: var(--carbon); border-color: var(--carbon); }
.built.bad { color: var(--signal-ink); border-color: var(--signal-ink); }
.rootline { font-size: var(--t-0); color: var(--steel); line-height: 1.6; }
.rootline .rt { color: var(--signal-ink); font-weight: 600; }
.rev-attr { font-size: var(--t-1); color: var(--steel); }

/* ============================================================================
   10. POPOVERS, TOASTS, TOOLTIPS
   ========================================================================== */

#pop {
  background: var(--galley); border: 1px solid var(--edge-hi);
  border-radius: 0; box-shadow: var(--lift); padding: var(--s4);
  width: 344px;
}
#pop .pw { font-family: var(--serif); font-size: var(--t-5); font-weight: 600; line-height: 1.1; }
#pop .plemma { font-family: var(--sans); font-size: var(--t-0); color: var(--steel); margin: var(--s1) 0 var(--s2); }
#pop .pg { font-size: var(--t-1); line-height: 1.5; }
#pop .pstage {
  font-family: var(--bill); font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--steel); margin: var(--s2) 0 var(--s3);
}
#pop .row { gap: var(--s2); margin-top: var(--s3); }
#pop .row button, #pop .row a {
  min-height: 40px; padding: var(--s2) var(--s3); font-size: var(--t-0);
  box-shadow: var(--lift-sm); text-decoration: none;
}
#pop .forms { font-size: var(--t-0); color: var(--steel); margin-top: var(--s3); line-height: 1.7; }
#pop .forms b { color: var(--carbon); }
#pop .sec { border-top: 1px solid var(--rule); margin-top: var(--s3); padding-top: var(--s3); font-size: var(--t-1); }
#pop .kbd-hint { font-size: var(--t-00); color: var(--ink-3p); margin-top: var(--s3); letter-spacing: .06em; }
#pop .close {
  top: 0; right: 0; width: 40px; height: 40px; color: var(--steel);
  font-size: var(--t-3); line-height: 1; background: none; border: none; box-shadow: none;
}
#pop .close:hover { color: var(--signal-ink); }

.toast {
  background: var(--galley); color: var(--carbon);
  border: 1px solid var(--edge-hi); border-left: 5px solid var(--signal);
  border-radius: 0; box-shadow: var(--lift); padding: var(--s3) var(--s4);
  font-size: var(--t-1); max-width: 320px; bottom: 84px; right: var(--s4);
}
.toast .tt {
  font-family: var(--bill); font-size: var(--t-0); font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--signal-ink);
  margin-bottom: var(--s1); display: block;
}

#tip {
  background: var(--carbon); color: var(--galley);   /* Lc −91.0 / 95.2 / −90.2 */
  border-radius: 0; padding: var(--s2) var(--s3);
  font-size: var(--t-0); line-height: 1.5; max-width: 320px;
}
kbd {
  background: var(--case); border: 1px solid var(--edge); border-radius: 0;
  padding: 1px var(--s1); font-family: var(--sans); font-size: var(--t-00);
  color: var(--carbon);
}
.coach, .margin-mate .m-say, .pop-say, .k-say {
  background: var(--carbon); color: var(--galley);
  border-radius: 0; box-shadow: none; font-family: var(--serif); font-style: italic;
}
.coach { font-size: var(--t-1); padding: var(--s2) var(--s4); }

/* speech from the room's characters: a caption card, hard-edged, with a
   square tail rather than a chat bubble's rounded one */
.guide .g-bubble, .friend-strip .fr-bubble, #tour-panel .t-bubble {
  border-radius: 0; background: var(--galley); border: 1px solid var(--edge);
  box-shadow: var(--lift-sm); color: var(--carbon);
}
.guide .g-bubble, .friend-strip .fr-bubble, #tour-panel .t-bubble {
  font-family: var(--serif); font-size: var(--t-2); line-height: 1.55;
  padding: var(--s3) var(--s4);
}
.guide .g-bubble::before, .friend-strip .fr-bubble::before { border-right-color: var(--edge); }
.guide .g-bubble::after, .friend-strip .fr-bubble::after { border-right-color: var(--galley); }
.guide .g-next { font-family: var(--sans); font-size: var(--t-00); letter-spacing: .14em; color: var(--steel); }
#tour-panel .t-dots i { border-radius: 0; width: 6px; height: 6px; background: var(--rule); }
#tour-panel .t-dots i.on { background: var(--signal); }
#tour-ring { border-radius: 0; box-shadow: 0 0 0 3px var(--signal), 0 0 0 9999px var(--scrim); }
#tour-mask { background: var(--scrim); backdrop-filter: none; }

/* ============================================================================
   11. THE PATH — nodes are sorts, not game buttons
   ========================================================================== */

/* Every node was a radial-gradient disc with an inner highlight ring, two
   inset shadows and a coloured drop shadow, in whichever of the seven play
   hues its author had been assigned. That is four things this product does not
   do — gradient, blur, glow, second hue — stacked on one element. A node is a
   piece of type: a square face, an offset shadow, and it presses. */
/* THE DOOR IS A BOOKPLATE. Signing in is being issued a reader's ticket at
   the old library, not filling in a web form — the panel says so first. */
#key-panel .k-plate {
  text-align: center; margin: 2px 0 14px; padding-bottom: 12px;
  border-bottom: 1px solid var(--rule);
}
#key-panel .k-plate .k-orn { display: block; color: var(--carbon); font-size: 19px; line-height: 1; }
#key-panel .k-plate .k-ru {
  display: block; font-family: var(--bill); letter-spacing: .2em;
  font-size: var(--t-0); color: var(--carbon); margin-top: 6px;
}
#key-panel .k-plate .k-en {
  display: block; font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--steel); margin-top: 2px;
}

/* THE PODCAST'S LINE UNDER THE CONTENTS. A marginal note in the house's
   voice, not a promotion: same measure as the contents it follows, the
   steel ink every other aside takes, and a hairline above it so it reads
   as a note appended to the book rather than a row of the book. */
.club-line { max-width: 42rem; margin: var(--s6) auto var(--s4);
  padding-top: var(--s3); border-top: 1px solid var(--rule, var(--ink-1p));
  font-family: var(--serif); font-style: italic;
  font-size: var(--t-1); line-height: 1.5; color: var(--steel); }
.club-line a { color: inherit; text-underline-offset: 3px; }
.club-line a:hover { color: var(--bookink, var(--pine)); }

/* THE CONTENTS. Type on a rule — number, title, a leader, a word.
   No stations, no spine, no badge sitting on a square. */
.path { position: relative; max-width: 42rem; margin: 0 auto; }
.path::before { display: none; }
.path > * { position: relative; }
.path-sect { display: block; margin: var(--s8) 0 var(--s3); padding-bottom: var(--s3);
  border-bottom: 1px solid var(--carbon); max-width: none; }
.path-sect .avatar { display: none; }
.path-sect .s-name { font-family: var(--serif); font-size: var(--t-4); }
.path-sect .s-meta { font-family: var(--bill); font-size: var(--t-00);
  letter-spacing: .14em; text-transform: uppercase; color: var(--steel); }
.path-row, .path-row.has-current { margin: 0; }
.node { transform: none; }
.node-btn {
  width: 100%; height: auto; min-height: 0; border-radius: 0;
  display: grid; grid-template-columns: 3.4rem minmax(0, auto) 1fr auto;
  align-items: baseline; gap: .35rem .75rem;
  padding: .8rem 0; background: transparent; color: var(--carbon);
  border: none; border-bottom: 1px solid var(--rule); box-shadow: none; font-size: inherit;
}
.node-btn::after { display: none; }
.node-btn:hover { transform: none; box-shadow: none; color: var(--carbon); }
.node-btn:active { transform: none; box-shadow: none; }
.node-btn .n-no {
  position: static; left: auto; bottom: auto;
  font-family: var(--mono); font-style: normal; font-weight: 400;
  font-size: .78rem; color: var(--steel);
}
.node.current .node-btn .n-no, .node.done .node-btn .n-no { color: inherit; }
.node.done .node-btn    { --nc: transparent; color: var(--steel); background: transparent; }
.node.current .node-btn { --nc: transparent; color: var(--carbon);
  width: 100%; height: auto; font-size: inherit; border-color: transparent;
  border-bottom: 1px solid var(--carbon); background: transparent; }
.node.locked .node-btn  { --nc: transparent; color: var(--steel); box-shadow: none; }
.node.next-up .node-btn { --nc: transparent; color: var(--carbon); }
.node.reward .node-btn  { --nc: transparent; color: var(--carbon); }
.node .bubble, .node .halo, .node-mate, .node .ring-pulse, .node .n-label { display: none; }
.node-btn:hover { border-bottom-color: var(--carbon); }
.node-btn:hover .n-no { color: var(--carbon); }
@media (max-width: 520px) {
  .node-btn { grid-template-columns: 2.8rem 1fr auto; }
  .node-btn .n-lead { display: none; }
}

.meter.grew > i.retr::after, .rev-sweep { display: none; }

/* ============================================================================
   12. THE READING SURROUND
   Only what surrounds the sheet — the sheet itself is another builder's.
   ========================================================================== */

.scene-head { margin: var(--s4) 0 var(--s7); }
.scene-head h2 { font-size: var(--t-6); margin: var(--s3) 0 var(--s1); }
.scene-head .sub { font-family: var(--serif); font-style: italic; color: var(--steel); font-size: var(--t-2); }
.scene-head .orn { color: var(--signal-ink); font-size: var(--t-3); margin-top: var(--s3); }
.divider { color: var(--signal-ink); margin: var(--s8) 0; }

/* same law as the header: the readbar carries eight labels and a count */
#readbar { background: var(--galley); border-top: 2px solid var(--carbon); }
#readbar .inner { padding: var(--s2) var(--s3); gap: var(--s2); }
#readbar .fill { background: var(--signal); height: 3px; }
/* «31 sentences left · ≈3 min» was --steel — a paper-ink token — sitting on a
   bar the chromatic skin had repainted navy: a measured 1.30:1 against a
   4.5:1 requirement. It cannot be a fixed colour, because the bar is paper in
   one skin and navy in the other. It inherits the bar's own text colour and
   steps back from it, so it is legible on whichever surface it lands. */
#readbar .left-note { font-size: var(--t-0); color: inherit; opacity: .72; }
#readbar .icon-btn { box-shadow: none; }
#readbar .icon-btn:hover { box-shadow: var(--lift-sm); }

.tr-para {
  font-family: var(--sans); font-size: var(--t-0); line-height: 1.5;
  color: var(--steel); border-left: 2px solid var(--rule); padding-left: var(--s3);
}
.tr-reveal {
  border-radius: 0; border-left: 5px solid var(--signal);
  font-size: var(--t-1); padding: var(--s3) var(--s4);
}
.read-on {
  clip-path: none; box-shadow: var(--lift-sm);
  font-family: var(--bill); font-size: var(--t-0); letter-spacing: .14em;
  text-transform: uppercase; color: var(--signal-ink); border-color: var(--signal);
  min-height: 40px; padding: 0 var(--s3);
}
.read-on:active { transform: translate(3px, 3px); box-shadow: 0 0 0 var(--edge); }

/* ПОМЕТА — the mark a word wears.
   head.html's .w rule reads `background-size: 100% 0` and paints
   `var(--wmark, transparent)`, with a comment pointing at a block that was to
   define both. That block is not in the file, so --wmark resolves to
   transparent at a height of zero: every word state in the product — new,
   learning, held, skipped, reading-only — renders identically to untouched
   prose. The reader cannot see what they have already decided.

   A hairline under the baseline, never a highlighter, and the weights are
   rationed the same way the hue is. On a first page roughly nine words in ten
   are `new`, so `new` gets the QUIET mark — a 1px --rule, the weight head.html
   specifies for a hairline on the sheet (Lc 25.7). Setting it in сурик was
   the first thing tried here and it turned a page of Chekhov into a page of
   proof corrections. The loud mark belongs to `l`, the words the reader has
   actually taken into the case, because those are rare and they are the point.

   Specificity is kept to two classes so the sheet that eventually owns the
   reading plane wins by name rather than by weight. */
/* THE READING PLANE IS NOT STYLED HERE. It was, and it broke the mechanic.

   This sheet grew a complete second mark system on a --wmark custom property.
   head.html has the real one (ПОМЕТА, head.html:1428) and it drives the
   underline from --mark, so every --wmark declaration here was simply never
   read. What DID take effect were the background-size lines beside them, and
   those fought the real system: `body.hint-new .w.l { background-size: 100% 0 }`
   zeroed the memory gauge in the DEFAULT configuration, and .w.ronly stopped
   painting at all. Measured on real marked words in the built page — learning
   at 1px instead of 2, reading-only invisible.

   Two systems competing for one surface is the bug, not any one of their
   declarations, so the whole block is gone rather than corrected. The mark is
   the single most frequent thing this product draws and the thing it is for;
   it has one owner, and the owner is head.html. */

/* ============================================================================
   13. THE TICKET, THE EX-LIBRIS, THE MANIFESTO
   ========================================================================== */

.ticket { border-radius: 0; border: 2px solid var(--carbon); padding: var(--s5); }
.ticket::before { inset: 5px; border: 1px solid var(--carbon); border-radius: 0; }
.ticket .tk-head { font-family: var(--bill); letter-spacing: .3em; font-size: var(--t-00); color: var(--steel); }
.ticket .tk-name { font-family: var(--serif); font-size: var(--t-4); margin: var(--s2) 0 var(--s1); }
.ticket .tk-sub { font-size: var(--t-0); color: var(--steel); }
.exlibris {
  width: 88px; height: 88px; border-radius: 0; border: 1px solid var(--edge);
  background: var(--galley); box-shadow: var(--lift-sm); gap: 2px;
}
.exlibris:hover { transform: none; border-color: var(--signal-ink); box-shadow: var(--lift); }
.exlibris.sel { border-color: var(--signal); box-shadow: inset 0 0 0 2px var(--signal); }
.exlibris .xl-orn { color: var(--signal); font-size: var(--t-3); }
.exlibris .xl-cap { font-family: var(--bill); font-size: var(--t-00); letter-spacing: .2em; color: var(--steel); }
/* THE LIVING BOOKPLATES — a resident stands in each plate, small and alive;
   the plate is a recessed compartment of the case (the pale species — the
   little dog, the tooth — need a ground darker than paper to stand on),
   and nothing may clip a gesture */
.exlibris { cursor: pointer; background: var(--case); }
.exlibris.sel { background: var(--sheet); }
.exlibris .xl-fig { width: 52px; height: 52px; margin: 2px auto 0; pointer-events: none; }
.exlibris .xl-fig svg { width: 100%; height: 100%; overflow: visible; display: block; }
.exlibris .xl-mono { font-family: var(--serif); font-weight: 700; font-size: var(--t-0); letter-spacing: .06em; }
.exlibris .xl-mono:empty { display: none; }
/* the house companion presides over the registry */
.cer-host { width: 66px; height: 66px; margin: 2px auto 6px; }
.cer-host svg { width: 100%; height: 100%; overflow: visible; display: block; }
/* the chosen mark, struck large on the stamped page and on the ticket seal */
.cer-stamped .stamp { width: 64px; height: 64px; }
.ticket .tk-seal .stamp { width: 64%; height: 64%; }
.ticket .tk-mark .stamp { width: 1.35em; height: 1.35em; vertical-align: -.3em; }
.manifesto { font-size: var(--t-2); line-height: 1.7; }
.manifesto .rule-orn { color: var(--signal-ink); }
.found-object { border: 1px solid var(--edge); border-radius: 0; transform: none; background: var(--galley); }
.squint-hint, .almanac { font-size: var(--t-00); }
.squint-hint {
  border-radius: 0; background: var(--galley); border: 1px solid var(--edge);
  color: var(--steel); letter-spacing: .14em; padding: var(--s1) var(--s3);
}
.frost { background: var(--galley) !important; border-color: var(--rule) !important; }
.frost::after { color: var(--steel); }

/* ============================================================================
   14. THE ACCOUNT PANEL — nothing here had a stylesheet at all
   ========================================================================== */

.k-tabs { display: flex; gap: 0; margin-bottom: var(--s4); border: 1px solid var(--edge); }
.k-tabs button {
  flex: 1; min-height: 44px; border: none; border-right: 1px solid var(--edge);
  background: var(--galley); color: var(--steel); cursor: pointer;
  font-family: var(--sans); font-size: var(--t-1); font-weight: 500;
}
.k-tabs button:last-child { border-right: none; }
.k-tabs button.on { background: var(--carbon); color: var(--galley); font-weight: 600; }
.k-lede { font-size: var(--t-1); line-height: 1.6; color: var(--steel); margin: 0 0 var(--s4); }
/* the social doors — quoted marks on the house's own plain button, and a
   ruled "or" so the address form below reads as an equal way in, not a
   fallback for people without the right kind of account */
.k-social { display: grid; gap: var(--s2); margin: 0 0 var(--s4); }
/* `display: grid/flex` on the class BEATS the browser's own [hidden] rule —
   author styles always win over the UA sheet — so without these two lines
   the doors rendered even when the server said they do not exist, and a
   button with no handler behind it answered every press with nothing. */
.k-social[hidden], .k-oauth[hidden] { display: none !important; }
.k-oauth {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  min-height: 46px; width: 100%; cursor: pointer;
  border: 1px solid var(--carbon); border-radius: 2px;
  background: var(--sheet); color: var(--carbon);
  font-family: var(--sans); font-size: var(--t-1); font-weight: 600;
}
.k-oauth:hover { background: var(--galley); }
.k-oauth svg { flex: none; }
.k-or {
  display: flex; align-items: center; gap: var(--s3);
  margin-top: var(--s2); color: var(--steel);
  font-family: var(--serif); font-style: italic; font-size: var(--t-0);
}
.k-or::before, .k-or::after { content: ''; flex: 1; border-top: 1px solid var(--rule); }
.k-sent { font-size: var(--t-1); line-height: 1.6; }
.k-err {
  font-size: var(--t-0); color: var(--signal-ink); background: var(--galley);
  border: 1px solid var(--signal); padding: var(--s2) var(--s3); margin: var(--s3) 0;
}
.k-rows { list-style: none; padding: 0; margin: var(--s4) 0; }
.k-rows li { padding: var(--s3) 0; border-bottom: 1px solid var(--rule); font-size: var(--t-1); }
.k-alt { display: flex; flex-direction: column; align-items: flex-start; margin-top: var(--s3); }
/* the subscription's own paragraph — set off from the rooms/standing rows above
   and the sign-out row below by a ruled frame, so the button reads as a button
   and its note reads as a note, not one run-on line */
.sub-manage { border: 1px solid var(--rule); padding: var(--s4); margin: var(--s4) 0; }
.sub-manage .sm-head { font-family: var(--sans); font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--steel); margin-bottom: var(--s2); }
.sub-manage .sm-confirm { margin-top: var(--s3); border-top: 1px solid var(--rule); padding-top: var(--s3); }
.sub-manage .sm-confirm p { font-size: var(--t-0); line-height: 1.55; margin: 0 0 var(--s2); }
.k-danger {
  background: none; border: 1px solid var(--signal); color: var(--signal-ink);
  font-family: var(--sans); font-size: var(--t-0); min-height: 44px;
  padding: 0 var(--s4); cursor: pointer; border-radius: 0;
}
.k-danger:hover { background: var(--signal); color: var(--on-signal); }
.plans { display: grid; gap: var(--s3); grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
.plan {
  display: flex; flex-direction: column; gap: var(--s1); text-align: left;
  padding: var(--s4); min-height: 44px; cursor: pointer; border-radius: 0;
  background: var(--galley); border: 1px solid var(--edge); box-shadow: var(--lift-sm);
}
.plan:hover { border-color: var(--signal-ink); box-shadow: var(--lift); }
.plan .p-amt { font-size: var(--t-4); font-weight: 700; line-height: 1; }
.plan .p-per { font-size: var(--t-0); color: var(--steel); }
.plan .p-note {
  font-family: var(--bill); font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--signal-ink); margin-top: var(--s1);
}
.plan.best .p-per, .plan.best .p-note { color: var(--on-signal); }
.cer-keep { border-top: 1px solid var(--rule); margin-top: var(--s4); padding-top: var(--s4); font-size: var(--t-0); }

/* ============================================================================
   15. IT HAS TO HOLD AT 390
   ========================================================================== */

/* head.html hides .brand under 900px, but .wordmark.small is declared later in
   the same file with `display: inline-flex` and wins the cascade, so the
   wordmark stayed in the bar on a phone and pushed four of the eight nav tabs
   off the right edge. Named specifically enough to settle it. */
@media (max-width: 900px) {
  header .bar .brand.wordmark { display: none; }
  .bar { gap: var(--s1); padding: 0 var(--s2); }
  /* the spacer stays: rooms scroll on the left, the cameo holds the right */
  /* the tabs scroll instead of overflowing the page; the room controls stay put */
  .nav-tabs {
    flex: 1 1 auto; min-width: 0; overflow-x: auto;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .nav-tabs::-webkit-scrollbar { display: none; }
  .nav-tabs button { padding: 0 var(--s2); font-size: var(--t-0); }
  .theme-dots, #btn-settings { flex: none; }
}

@media (max-width: 640px) {
  :root { --pad-x: 16px; --pad-y: 16px; }
  /* breathing room under the last line without a third of a screen of dead
     scroll — 34vh was 290px of nothing on a phone, and the page felt longer
     than the reading it held */
  .view { padding: var(--s5) var(--s3) clamp(96px, 16vh, 180px); }
  .theme-dots button { width: 44px; }        /* a thumb, not a pointer */
  h1, .as-h1 { font-size: var(--t-6); }
  h2 { font-size: var(--t-4); }
  .frontier-card .fr-n { font-size: var(--t-6); }
  .tiles { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }
  .shelf { grid-template-columns: 1fr; }
  .filters input { margin-left: 0; width: 100%; min-width: 0; }
  .set-row { flex-wrap: wrap; }
  .set-row select, .set-row input[type="text"] { min-width: 0; width: 100%; }
  .overlay { padding: var(--s3); }
  .overlay .panel { max-height: 92vh; }
  #pop { width: auto; left: var(--s2) !important; right: var(--s2);
         max-width: calc(100vw - var(--s4)); }
  .toast { left: var(--s3); right: var(--s3); max-width: none; }
  #wordlist li { flex-wrap: wrap; gap: var(--s2); }
  #wordlist .wf { min-width: 0; flex: 1 1 100%; }
  .rev-btns button, .btn { flex: 1 1 auto; }
}

/* Every reduced fallback is a finished state. Nothing here animates, so the
   only thing to answer for is the pressed transform, which is a state change
   the reader asked for by pressing. It stays. */


/* THE BOOKPLATE — the stranger's first page says what the room is: a gift,
   entire. Furniture, not a banner: ink and paper, no motion, and it leaves
   for good when the enrolment ceremony arrives. */
#gift-plate {
  border: 2px solid var(--carbon); background: var(--sheet);
  padding: 10px 14px 12px; margin: 0 0 18px; position: relative;
}
#gift-plate::before {
  content: ''; position: absolute; inset: 3px;
  border: 1px solid var(--carbon); pointer-events: none;
}
#gift-plate .gp-head {
  font-family: var(--bill); font-weight: 600; letter-spacing: .22em;
  font-size: var(--t-00); text-transform: uppercase; color: var(--steel);
}
#gift-plate .gp-body {
  font-size: var(--t-0); line-height: 1.55; color: var(--carbon);
  margin-top: 4px; max-width: 60ch;
}


/* THE ANNOUNCED ENDING + THE WEATHER AT THE COPYING DESK. Focus is the
   tunnel granted when the run has earned it; steady is the lamp brought
   closer in a rough patch — shelter, not commentary. Applied only between
   cards; one right answer lifts it. */
#v-review[data-act="close"] #rev-note { color: var(--carbon); font-family: var(--serif); font-style: italic; }
/* THE ACTS, WORN BY THE CARD: the opening sheet lies flat on the desk, the
   middle of the sitting presses deeper into it.
   ONE OWNER, MANY VOICES. These used to write box-shadow outright, and so did
   the heat (66-rooms), the lamp (29-alive) and the arcade slab (90-chromatic)
   — four files assigning the same property on selectors of the same weight,
   so whichever happened to be last won and the other three were dead code.
   The visible cost: the card carried NO shadow for a whole sitting and then
   snapped to a hard black slab the instant data-act changed. Each act now
   names only its DEPTH; the single rule that assembles the shadow lives in
   90-chromatic-game.css, and every state crossfades on one timing. */
#v-review[data-act="open"] #rev-card { --rev-depth: 3px; }
#v-review[data-act="mid"] #rev-card { --rev-depth: 6px; }
#v-review[data-act="close"] #rev-card { --rev-depth: 8px; }
.rev-head, #v-review #btn-rev-exit {
  transition: opacity 1100ms var(--e-std), filter 1100ms var(--e-std);
}
#v-review[data-mood="focus"] .rev-head,
#v-review[data-mood="focus"] #btn-rev-exit { opacity: .45; filter: blur(.6px); }
#v-review[data-mood="steady"] .rev-head { opacity: .7; }
/* the card's own colours settle as fast as its contents change; 900ms of
   background meant the sheet was still cold white under text that had
   already arrived */
#rev-card { transition: background 380ms var(--e-std); }
#v-review[data-mood="steady"] #rev-card { --rev-depth: 4px; }

/* THE CASE OF TYPE — compartments by the book where each word was met,
   captioned in that book's road pigment. Provenance, never status. */
#wordlist .wl-cap {
  display: flex; flex-wrap:wrap; gap:6px 16px; justify-content: space-between; align-items: baseline;
  border: none; border-left: 5px solid var(--ink, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 8%, var(--sheet));
  padding: 7px 12px; margin-top: 14px;
  font-family: var(--bill); letter-spacing: .06em;
}
#wordlist .wl-cap:first-child { margin-top: 0; }
#wordlist .wl-cap .wl-cap-t { font-weight: 700; }
#wordlist .wl-cap .wl-cap-n {
  font-family: var(--sans); font-size: var(--type-meta); color: var(--steel);
  font-variant-numeric: tabular-nums;
}

/* ink settles as the memory does — the palest row stays legible */
#wordlist li.wl-warm .wf { opacity: calc(.62 + .38 * var(--w, 1)); }


/* A DIFFERENT DRAWER SLIDES OPEN — one plane, decelerating, never row by
   row. Fires only on a filter change: search must not strobe under the keys. */
@media (prefers-reduced-motion: no-preference) {
  #wordlist.wl-drawer { animation: wl-drawer .26s var(--e-std, ease) both; }
  @keyframes wl-drawer { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
}

/* the key driven home: decelerate only; runs once, on arrival, never on repaint.
   Targets the buttons that EXIST — the old .nav-key/.nav-profile-btn classes
   matched nothing and the settle never played. */
body:not(.motion-low) #nav-profile.key-just,
body:not(.motion-low) #nav-reader.key-just {
  animation: key-home calc(.4s * var(--motion, 1)) var(--e-std, ease) both;
}
@keyframes key-home { from { transform: translateX(4px); } to { transform: none; } }


/* ============================================================================
   THE TOUR SPEAKS UP. The guide is the house's HOST, not a tooltip: the
   bubble takes reading-size type, the figure stands taller, the panel enters
   as a sheet laid, and each step carries the room's ink on its left rule.
   Same bones, real presence.
   ============================================================================ */
#tour-panel { width: min(660px, calc(100vw - 32px)); }
#tour-panel .t-who { width: 78px; height: 78px; }
#tour-panel .t-bubble {
  font-size: var(--t-2); line-height: 1.6; padding: 18px 20px 13px;
  border-left: 5px solid var(--signal);
  border-radius: 4px 16px 16px 4px;
}
#tour-panel .t-next { font-size: .78rem; }
#tour-panel .t-dots i { width: 8px; height: 8px; }
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) #tour:not([hidden]) #tour-panel {
    animation: tour-lay 420ms var(--e-std, ease) both;
  }
  @keyframes tour-lay { from { opacity: 0; transform: translateX(-50%) translateY(18px); }
                        to   { opacity: 1; transform: translateX(-50%); } }
}


/* THE DEEPER SHELF ON A REVEAL — more examples and the root family fold
   away behind a margin-note summary; the fold opens itself only on a miss.
   The constant surface stays word + gloss + sentence + chips. */
#rev-card .rev-more { margin-top: var(--s3); text-align: left; }
#rev-card .rev-more summary {
  cursor: pointer; list-style: none;
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .08em;
  text-transform: uppercase; color: var(--steel);
}
#rev-card .rev-more summary::-webkit-details-marker { display: none; }
#rev-card .rev-more summary::after { content: ' ▸'; }
#rev-card .rev-more[open] summary::after { content: ' ▾'; }

/* the end sheet's unfinished business: the words still deciding, each said
   aloud on a tap — names, never a count owed */
#rev-card .still-row { margin-top: var(--s4); }
#rev-card .still-chip {
  display: inline-block; margin: 6px 4px 0 0; padding: 3px 12px;
  border: 1px solid var(--line); border-radius: 999px; background: var(--sheet);
  font-family: var(--serif); font-size: var(--t-0); color: var(--carbon);
  cursor: pointer;
}
#rev-card .still-chip:hover { border-color: var(--carbon); }

/* EVERY CARD WEARS ITS BOOK: the home volume's pigment on the top rule and
   its title printed small in the corner. Provenance, never status. */
#rev-card { border-top: 4px solid var(--ink, var(--carbon)); position: relative; }
#rev-card .rev-home {
  position: absolute; top: 8px; right: 12px;
  font-family: var(--serif); font-style: italic; font-size: var(--t-00);
  color: var(--ink, var(--carbon)); opacity: .8; pointer-events: none;
}
[data-theme="dark"] #rev-card .rev-home {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 40%, var(--carbon));
}

/* THE FINISHED-STORY PLATE — inline in the page flow at the story's end,
   never an overlay. The fact first, then the door; dismissible for good. */
.fin-plate { max-width: 560px; margin: 22px auto 0; text-align: left; }
.fin-plate .fin-fact { font-family: var(--serif); font-size: 1.08rem; line-height: 1.5;
  margin: 8px 0 2px; }
.fin-plate .btnrow { margin-top: 12px; }
/* the locked card's naming line */
#locked-panel .lc-line { margin-top: 6px; color: var(--faint); }
#locked-panel .lc-desc { max-width: 44ch; margin-left: auto; margin-right: auto; }


/* ===== 25-course.css ===== */
/* ============================================================================
   THE COURSE.

   Two kinds of thing on one screen, and they must not look alike: a STAGE is a
   promise (a number and what it buys), a UNIT is work (eight words and the
   sentence they open). Stages are wide and flat; units are sheets with the
   Russian line set in the reading face, because the line is the point and the
   words are the price.
   ============================================================================ */

#v-course .cs-lede {
  font-family: var(--serif); font-size: var(--t-2); line-height: 1.55;
  color: var(--steel); max-width: 62ch; margin: 10px 0 0;
}
#v-course section { margin: 34px 0 0; }
#v-course .bill-head {
  background: var(--carbon); color: var(--galley);
  padding: 7px 18px; font-family: var(--bill); font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase; font-size: var(--t-00);
}
#v-course .cs-note {
  font-family: var(--serif); color: var(--steel);
  font-size: var(--t-1); max-width: 68ch; margin: 12px 0 18px;
}

/* --- the alphabet, grouped by what it costs ----------------------------- */
#v-course .cs-group { margin: 22px 0 0; }
#v-course .cs-group h3 {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .1em;
  font-size: var(--t-2); margin: 0 0 4px;
}
#v-course .cs-letters {
  display: grid; gap: 1px;
  grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
  background: var(--rule); border: 1px solid var(--carbon);
}
#v-course .cs-letter {
  background: var(--galley); padding: 10px 12px;
  display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; align-items: baseline;
}
/* THE LETTER HAS TO GROW WITH THE THINGS THAT EXPLAIN IT. This was 30px raw —
   the only hard size in the sheet, and off the nine-step ladder besides. Once
   96-scale.css put the tokens in rem, a wide desk grew .cs-say and .cs-lnote
   by 18.75% while the glyph they annotate sat still, so the card the reader
   saw was not the card that was drawn. --t-5 is 29px (a pixel down) and tops
   out at 34.4px, which is where .gx-stage .cs-c already caps (31-stages.css:126)
   — the two alphabet plates now agree. Nothing pins the old number: .cs-letter
   is `auto 1fr`, so the column sizes to whatever the glyph turns out to be. */
#v-course .cs-c {
  grid-row: 1 / 3; font-family: var(--serif); font-size: var(--t-5);
  line-height: 1; color: var(--carbon);
}
#v-course .cs-say { font-family: var(--sans); font-weight: 600; font-size: var(--t-1); }
#v-course .cs-lnote { font-size: var(--t-00); color: var(--steel); line-height: 1.4; }
/* The six that mislead are the only ones marked. Marking every group would
   make the marking mean nothing, which is the same rationing that keeps
   сурик worth something everywhere else in the product. */
#v-course .cs-trap .cs-letter { border-left: 3px solid var(--signal); }
#v-course .cs-trap .cs-c { color: var(--signal-ink); }

/* --- a stage is a promise ------------------------------------------------ */
/* The stage cards, unit sheets and inline gap-check that used to be styled
   here are gone with the markup that wore them — the route (27-route.css) and
   the exercise room (26-games.css, 28-feel.css) superseded all three. */

/* --- the grammar that arrives with a stage ------------------------------
   Set apart from the units below it, because it is a different kind of thing:
   the units are work you do, these are things you can now be shown. They lead
   the stage rather than following it — you read the explanation, then meet the
   words that make it true. */
#v-course .cs-gram {
  background: var(--galley); border: 1px solid var(--carbon);
  padding: 16px 18px; margin: 0 0 26px;
}
#v-course .cs-gram-lede {
  font-family: var(--serif); font-size: var(--t-1); color: var(--steel);
  max-width: 62ch; margin-bottom: 12px;
}
#v-course .cs-lesson {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--sheet); border: 1px solid var(--rule); border-radius: 0;
  padding: 10px 14px; margin: 0 0 -1px; font-family: inherit; color: inherit;
}
#v-course .cs-lesson:hover { border-color: var(--carbon); }
#v-course .cs-lesson:focus-visible { outline: 2px solid var(--signal); outline-offset: -3px; }
#v-course .cs-lesson b { display: block; font-family: var(--sans); font-weight: 600; font-size: var(--t-1); }
#v-course .cs-lesson i {
  font-style: normal; font-family: var(--serif); font-size: var(--t-0); color: var(--steel);
}

/* ============================================================================
   THE CLIMB — the Learn page, scroll-driven.

   Native CSS scroll timelines: zero bytes of library, and their Firefox
   fallback is "the element is already in its finished state", which is a good
   state rather than a broken one. That is the whole reason ScrollTrigger's
   44KB was not worth buying.

   Every move below is a printing move. Nothing fades and rises — that is what
   every other site does, and it says nothing about what this one is. A sort is
   SET into a line from the side and stops dead; a rule is DRAWN; a sheet is
   LAID DOWN and its shadow arrives with it. Those are the three verbs, and
   they are all this page uses.
   ============================================================================ */

@media (prefers-reduced-motion: no-preference) {

  /* The stage-card, unit-sheet and book-rule timelines went with their
     markup; the letters below are the one bounded set still on the page. */

  /* --- THE ALPHABET ARRIVES IN ITS GROUPS, and the six that lie to you arrive
     LAST within their group and hold their red a moment longer. The one place
     on the page where the motion is carrying an argument rather than a mood. */
  @keyframes cs-letter-in {
    from { transform: translateY(10px); opacity: 0; }
    to   { transform: none; opacity: 1; }
  }
  #v-course .cs-letter {
    animation: cs-letter-in linear both;
    animation-timeline: view();
    animation-range: entry 4% entry 44%;
  }
  @keyframes cs-warn {
    from { border-left-color: var(--signal); background: var(--sheet); }
    to   { border-left-color: var(--signal); background: var(--galley); }
  }
  #v-course .cs-trap .cs-letter {
    animation: cs-letter-in linear both, cs-warn linear both;
    animation-timeline: view(), view();
    animation-range: entry 4% entry 44%, entry 30% entry 90%;
  }
}

/* Firefox, and anyone who asked for less motion, gets the finished state —
   which is what every rule above ends on. Nothing is hidden by default, so
   nothing can be permanently hidden by a timeline that never runs. */


/* ============================================================================
   THE ROAD COMES OFF THE GOLD (2026-08-25, the owner's call).

   The daylight ground was returned to its cool near-white the same day
   (head.html --room/--case), and the road was left as the one place still
   printing solid #f6c445: a 6px gold rail running its whole length, a gold
   chip on the book you are reading, and six gold band markers. Against cool
   paper that read as the only warm thing on the screen, which is what Tom saw.

   It is also what the palette law says should not happen. «Gold is sacred:
   celebration/illumination only, never semantic» — and a rail, a you-are-here
   chip and six CEFR labels are all structure, not celebration.

   THE WARNING THIS RESPECTS. 90-chromatic-game.css:2656 records an earlier
   attempt that replaced the here-chip with the book's own pigment and «lost
   the road's one right-now signal». So the chip does not merely lose its
   gold — it takes the room's strongest ink, --play, and stays the only
   saturated block on a page of pale ones. The rail drops BELOW it instead,
   to a bordered pale-blue track, so the eye still lands on the chip first.
   Wayfinding is preserved by making the surroundings quieter, not the mark.

   These live here rather than in 90 or 95 because both of those sheets were
   dirty in the worktree with other sessions' work. `#v-course` outranks 90's
   bare class selectors on specificity, so sheet order is not load-bearing.
   ========================================================================== */

/* the road's rail: a track to walk, not a thing to look at */
#v-course ol.rd::before { background: var(--play-tint); }

/* the book you are on — the one saturated block on the page */
#v-course .rd-here .rd-vol {
  background: var(--play);
  box-shadow: inset 8px 0 0 var(--play-deep), 0 5px 0 var(--press);
}
/* AND THE COUNT HAS TO STOP INVERTING ITSELF. 27-route.css:90 draws every
   volume's count with `mix-blend-mode: difference; filter: invert(1)` — a
   trick that makes one colour legible on any chip, and it was tuned when the
   here-chip was LIGHT gold. Against a dark chip it inverts the other way and
   prints navy on navy: computed style still reported the cream, because this
   goes wrong at paint rather than in the cascade, and only a screenshot
   catches it. The chip below is dark on purpose, so the count stops being
   clever and is simply cream. */
#v-course .rd-here .rd-vol b {
  mix-blend-mode: normal;
  filter: none;
  color: var(--on-play);
}

/* the word-count marks are labels, and labels are not celebrations. (They read
   A1 to C2 when this was written; the road stopped claiming a level on
   2026-08-28 and the seal carries the count it measured instead — course.js
   §the mark. The rule is unchanged: whatever the seal says, it says it quietly.) */
#v-course .rd-m-dot {
  background: var(--play);
  color: var(--on-play);
}


/* ===== 26-games.css ===== */
/* ============================================================================
   THE EXERCISES.

   One question fills the panel. Nothing else is on screen: no score, no
   progress bar racing you, no timer. The step counter says where you are and
   the Leave button says you may — both of those are the opposite of a streak,
   and they are there on purpose.
   ============================================================================ */

.gx { display: grid; gap: 18px; }
.gx-top { display: flex; align-items: center; justify-content: space-between; }
.gx-step {
  font-family: var(--sans); font-variant-numeric: tabular-nums;
  font-size: var(--t-00); letter-spacing: .16em; color: var(--steel);
}
.gx-quit {
  background: none; border: 0; cursor: pointer; padding: 4px 0;
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .14em;
  font-size: var(--t-00); color: var(--steel);
}
.gx-quit:hover { color: var(--carbon); }
.gx-ask {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .12em;
  font-size: var(--t-1); color: var(--carbon);
}

/* --- the prompt. Russian is always in the reading face; a word being learned
   should look the way it looks in the book, not the way a button looks. --- */
.gx-word { font-family: var(--serif); font-size: clamp(30px, 6vw, 46px); line-height: 1.1; }
.gx-gloss { font-family: var(--serif); font-size: var(--t-4); line-height: 1.3; }
.gx-en { font-family: var(--serif); font-style: italic; font-size: var(--t-3); color: var(--steel); }
.gx-line { font-family: var(--serif); font-size: var(--t-3); line-height: 1.5; }
.gx-blank {
  display: inline-block; min-width: 4.5em; text-align: center;
  color: var(--signal-ink); border-bottom: 2px solid var(--signal);
}
.gx-say {
  font-family: var(--sans); font-size: var(--t-2); cursor: pointer;
  background: var(--galley); border: 1px solid var(--carbon); border-radius: 0;
  padding: 10px 20px; color: inherit;
}

/* --- answering ---------------------------------------------------------- */
.gx-opts { display: grid; gap: 8px; }
.gx-opt {
  font-family: var(--serif); font-size: var(--t-2); text-align: left;
  background: var(--galley); border: 1px solid var(--carbon); border-radius: 0;
  padding: 12px 16px; cursor: pointer; color: inherit;
  transition: background 120ms linear;
  /* AN ANSWER NEVER BREAKS ITS WORD (owner's proof-tile find: «неприятност/ь»
     split mid-word with no hyphen). A word that will not fit its tile keeps
     itself whole and the TYPE yields instead — the snug steps below, applied
     by the deal's measuring pass. Ellipsis stays forbidden: a reader must be
     able to read the whole of an answer before choosing it. */
  overflow-wrap: normal; word-break: keep-all; hyphens: none; min-width: 0;
}
.gx-opt.gx-snug { font-size: .84em; }
.gx-opt.gx-snugger { font-size: .7em; letter-spacing: -0.01em; }
.gx-pw.gx-snug, .gx-pg.gx-snug { font-size: .84em; }
.gx-pw.gx-snugger, .gx-pg.gx-snugger { font-size: .72em; }
.gx-opt:hover:not(:disabled) { background: var(--sheet); }
.gx-opt:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.gx-opt:disabled { opacity: .5; cursor: default; }
.gx-right { background: var(--malachite) !important; color: var(--on-malachite) !important; border-color: var(--malachite) !important; opacity: 1 !important; }
.gx-wrong { border-color: var(--signal); color: var(--signal-ink); opacity: 1 !important; }

.gx-input {
  font-family: var(--serif); font-size: var(--t-4); width: 100%;
  background: var(--sheet); border: 1px solid var(--carbon); border-radius: 0;
  padding: 12px 14px; color: var(--carbon);
}
.gx-input:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* --- setting a line. The stick is where the words go, and they come back out
   again: half a sentence is not a wrong sentence, so nothing is graded until
   the line is full. --- */
.gx-stick {
  min-height: 54px; background: var(--sheet); border: 1px solid var(--carbon);
  padding: 10px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center;
}
.gx-stick-empty { font-size: var(--t-0); color: var(--steel); letter-spacing: .08em; }
.gx-tiles { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.gx-tile, .gx-set {
  font-family: var(--serif); font-size: var(--t-2); cursor: pointer;
  background: var(--galley); border: 1px solid var(--carbon); border-radius: 0;
  padding: 7px 13px; color: inherit;
}
.gx-set { background: var(--carbon); color: var(--sheet); }
.gx-tile:disabled { opacity: .28; cursor: default; }

/* --- the answer, and the line it came from ------------------------------
   Shown whether you were right or not, and the same size either way. The
   sentence is the reward and it is not withheld for a wrong answer. */
.gx-verdict { font-family: var(--bill); text-transform: uppercase;
  letter-spacing: .12em; font-size: var(--t-1); }
/* THE REVEAL IS EVIDENCE, NOT A HEADING. The verdict line is billboard type
   and should shout, but the book's own words are set inside it — and
   uppercasing them destroyed the one thing the reader needed to see. A miss
   on «Баю баюшки баю» was answered with БАЮ БАЮШКИ БАЮ, which hides the
   capital that decided it: the correction could not be read as a correction. */
.gx-verdict .gx-reveal { text-transform: none; letter-spacing: .02em; }
.gx-ok { color: var(--carbon); }
.gx-no { color: var(--signal-ink); }
.gx-src {
  font-family: var(--serif); font-size: var(--t-2); line-height: 1.5;
  border-left: 3px solid var(--carbon); padding-left: 14px; margin: 12px 0;
}
.gx-src span { display: block; font-style: italic; font-size: var(--t-1); color: var(--steel); margin-top: 4px; }
.gx-after { display: grid; gap: 10px; }
.gx-done .gx-ask { font-size: var(--t-5); letter-spacing: .04em; }

/* THE PATH that used to live here (.cx-books, stage → book → lesson) moved to
   27-route.css as the road: the shelf itself is the course's spine now, and
   the chapter list draws with the .rd- vocabulary there. */

/* ============================================================================
   THE EXERCISE, WITH ITS LESSON'S INK AND A RHYTHM.

   It was six question types in one grey panel with no sense of being inside
   anything, and no moment when a lesson was finished — which is the whole
   difference between working through exercises and finishing something.
   ============================================================================ */

.gx-topic {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .14em;
  font-size: var(--t-00); color: var(--ink, var(--carbon));
}

/* --- THE RAIL. One mark per question, inked as it is answered. It is the
   composing stick again: you can see how much of the line is set. It does not
   race you and there is no timer under it. --- */
.gx-rail { display: flex; gap: 3px; }
.gx-rail i {
  flex: 1; height: 4px; display: block;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 16%, var(--rule));
  transition: background 260ms linear;
}
.gx-rail .gx-r-done { background: var(--ink, var(--carbon)); }
.gx-rail .gx-r-now  { background: var(--signal); }

/* The prompt and the answers take the lesson's ink, so the whole panel belongs
   to the topic you opened rather than being a grey box that could be anything. */
.gx .gx-ask { color: var(--ink, var(--carbon)); }
.gx .gx-opt:hover:not(:disabled) { border-color: var(--ink, var(--carbon)); }
/* correct is ALWAYS malachite, in every room — feedback colour must never
   drift with the topic, or the reader has to relearn it per lesson */
.gx .gx-right { background: var(--malachite) !important; border-color: var(--malachite); }
.gx .gx-src { border-left-color: var(--ink, var(--carbon)); }
.gx .gx-set { background: var(--ink, var(--carbon)); }

/* --- ONE QUESTION GIVES WAY TO THE NEXT. Set from the left, like everything
   else that arrives in this product. Short, because it happens eight times a
   lesson and anything longer would be a wait. --- */
@keyframes gx-in { from { transform: translateX(-10px); opacity: 0; } to { transform: none; opacity: 1; } }
.gx-ask, .gx-head, .gx-body { animation: gx-in 220ms var(--e-std, ease) both; }
.gx-head { animation-delay: 40ms; }
.gx-body { animation-delay: 80ms; }
@media (prefers-reduced-motion: reduce) {
  .gx-ask, .gx-head, .gx-body { animation: none; }
}

/* --- THE CARD THAT LOST ITS VOICE. A listening question whose sound never
   arrived is unanswerable, so it says so and hands over the word — the reader
   loses the exercise, not the turn. `color: inherit` because the proof plate
   repaints .gx-head on a dark ground and a fixed ink would vanish there. --- */
.gx-head .gx-mute {
  margin-top: .45rem;
  font-size: .8125rem;
  line-height: 1.35;
  color: inherit;
  opacity: .72;
}
.gx-head .gx-mute b { font-weight: 600; opacity: 1; }

/* --- THE LESSON IS PRINTED. The line it was for is struck onto the sheet, the
   way the press strikes a title at the end of a book. The same gesture at a
   smaller scale, because it is the same kind of thing. --- */
.gx-print {
  background: var(--sheet); border: 1px solid var(--carbon);
  box-shadow: 6px 6px 0 var(--ink, var(--carbon));
  padding: 26px 24px; text-align: center;
  animation: gx-strike 560ms var(--e-snap, cubic-bezier(.2,.9,.3,1.2)) both;
}
@keyframes gx-strike {
  0%   { transform: scale(1.04); box-shadow: 0 0 0 var(--ink, var(--carbon)); }
  46%  { transform: scale(.985); }
  100% { transform: scale(1); box-shadow: 6px 6px 0 var(--ink, var(--carbon)); }
}
.gx-print-ru {
  font-family: var(--serif); font-size: clamp(20px, 3.6vw, 30px); line-height: 1.35;
  color: var(--carbon);
  animation: gx-ink 620ms 180ms var(--e-std, ease) both;
}
@keyframes gx-ink { from { opacity: 0; letter-spacing: .06em; } to { opacity: 1; letter-spacing: 0; } }
.gx-print-en {
  font-family: var(--serif); font-style: italic; font-size: var(--t-1);
  color: var(--steel); margin-top: 8px;
}
.gx-tally {
  font-family: var(--sans); font-weight: 200; font-variant-numeric: tabular-nums;
  font-size: clamp(26px, 4vw, 38px); color: var(--carbon);
}
@media (prefers-reduced-motion: reduce) {
  .gx-print, .gx-print-ru { animation: none; }
}


/* ============================================================================
   THE ROOM.

   The exercise used to borrow the account overlay: a small centred panel over
   a dimmed page. A lesson is the thing you came here to do, and it was
   presented as an interruption of something else — small type, no air, and the
   whole product visible behind it to look at instead.

   It gets the screen. The page behind it is not dimmed, it is gone: nothing to
   look past, nothing to be distracted by, and the type can be the size it
   ought to be.
   ============================================================================ */

body.gx-on { overflow: hidden; }

.gx-stage {
  position: fixed; inset: 0; z-index: 240;
  background: var(--room);
  /* Anchored near the top rather than centred. Centring put the question in
     the middle of a tall window with a third of the screen empty above it, so
     the eye had to travel to find where the room begins — and the answers
     drifted further down every time an option list was longer than the last.
     A fixed top edge means the question is always in the same place. */
  display: grid; justify-items: center; align-content: start;
  padding: clamp(24px, 9vh, 88px) 24px 40px;
  overflow-y: auto;
  transition: opacity 220ms var(--e-std, ease), transform 260ms var(--e-std, ease);
}
/* The room arrives by opening rather than by fading: the ink band sweeps down
   the top edge and the panel settles. A door, not a dissolve. */
.gx-stage::before {
  content: ''; position: absolute; left: 0; right: 0; top: 0; height: 6px;
  background: var(--ink, var(--carbon));
  transform-origin: 0 50%;
  animation: gx-band 420ms var(--e-std, ease) both;
}
@keyframes gx-band { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* THE CHAPTER HAUNTS ITS OWN ROOM: the printer's ornament, chapter-sized,
   ghosted into the lower corner in the chapter's ink. Twenty lessons in
   twenty chapters become twenty rooms — the strongest cheap cure for the
   "every lesson looks the same" monotony, and it costs one glyph. */
.gx-stage::after {
  content: attr(data-orn);
  position: fixed; right: -3vmin; bottom: -7vmin;
  font-size: clamp(180px, 38vmin, 400px); line-height: 1;
  color: var(--ink, var(--carbon)); opacity: .055;
  pointer-events: none; user-select: none;
  transform: rotate(-8deg);
}
[data-theme="dark"] .gx-stage::after { opacity: .08; }
.gx-entering { opacity: 0; transform: scale(.985); }
.gx-leaving  { opacity: 0; transform: scale(1.01); }

.gx-stage .gx {
  width: min(760px, 100%);
  display: grid; gap: 26px;
  align-content: start;
  /* THE SHEET. The question used to float in the washed room with nothing
     under it — ink on air. Now it is printed on paper: a galley sheet laid
     on the room's boards, edged and casting the book's own pigment as its
     shadow — soft now, under the lamp, still tinted with the book's ink. */
  background: var(--galley);
  border: 1px solid var(--carbon);
  box-shadow: 0 3px 6px color-mix(in srgb, var(--ink, var(--carbon)) 18%, transparent),
              0 18px 40px -16px color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent),
              var(--heatglow, 0 0 0 transparent);
  padding: clamp(22px, 4vw, 48px);
  transition: box-shadow .7s ease;
}
/* THE HEAT OF THE RUN — the sheet itself warms in the chapter's ink as the
   streak climbs: felt at three, seen at six, unmistakable at nine. Feedback
   the reader senses before reading any number; any miss cools it in one
   silent frame (data-heat drops to 0 with the same transition-less law the
   heat band obeys). */
.gx-stage[data-heat="1"] .gx {
  --heatglow: 0 0 22px -6px color-mix(in srgb, var(--ink, var(--carbon)) 35%, transparent);
}
.gx-stage[data-heat="2"] .gx {
  --heatglow: 0 0 30px -4px color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 70%, var(--carbon));
}
.gx-stage[data-heat="3"] .gx {
  --heatglow: 0 0 40px -2px color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent),
              0 0 80px -20px color-mix(in srgb, var(--signal) 60%, transparent);
  border-color: var(--signal);
}
/* the sheet sorts the room's occupants: the bloom is pigment IN the room and
   lies under the paper (negative z-index, resolved against the stage's own
   stacking context); the resident is company and stands beside the sheet,
   never behind it. */
.gx-stage .gx-bloom { z-index: -1; }
.gx-stage .gx-mate { z-index: 2; }

.gx-top {
  display: flex; gap: 16px; align-items: baseline;
}
.gx-top .gx-topic { flex: 1; min-width: 0; }
.gx-count {
  font-family: var(--sans); font-variant-numeric: tabular-nums;
  font-size: var(--t-00); letter-spacing: .16em; color: var(--steel);
}

/* --- THE ROAD TO THE SEAL. One thin bar of the chapter's ink under the rail;
   past four-fifths the gold breathes into it, because the seal is near. --- */
.gx-chbar {
  height: 3px; margin-top: 5px;
  background: color-mix(in srgb, var(--rule, #ccc) 60%, transparent);
  overflow: hidden;
}
.gx-chbar i {
  display: block; height: 100%;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent);
  transition: width 700ms var(--e-std, ease);
}
.gx-chbar.gx-chbar-near i {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent),
    var(--signal));
}
body.motion-full .gx-chbar.gx-chbar-near i { animation: gx-seal-breathe 2.6s ease-in-out infinite; }
@keyframes gx-seal-breathe {
  0%, 100% { filter: brightness(1); }
  50% { filter: brightness(1.35); }
}
/* the door's honest endowment — what the reader already owns of the scene */
.gx-endow {
  font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--steel);
  margin: 10px 0 0;
}
.gx-endow b { color: var(--carbon); font-style: normal; }
/* the countdown under the bar — spoken only when the seal is near */
.gx-chbar-say {
  margin-top: 4px; text-align: right;
  font-family: var(--sans); font-size: .7rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--steel);
}
/* the payout's closing image */
.gx-src.gx-src-fine { font-size: 1.12em; }

/* the clean proof and the bench total, on the printed sheet */
.gx-clean {
  font-family: var(--serif); font-style: italic;
  color: var(--signal-ink, var(--signal)); margin: 2px 0 0;
}
.gx-clean b { font-style: normal; font-variant-numeric: tabular-nums; }

/* ============ THE NIGHT'S STANDING — tonight's page, and the lamp ============
   Closing every sitting with the day's goal and the run of evenings. The page
   bar is thin and factual; the lamp line is where the ceremony lands. */
.gx-night {
  margin-top: 14px; padding-top: 12px;
  border-top: 1px solid color-mix(in srgb, var(--carbon) 14%, transparent);
  display: grid; gap: 8px;
  animation: gx-night-in .5s ease .35s both;
}
@keyframes gx-night-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.gx-page {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .08em; color: var(--steel);
}
.gx-page-lab { text-transform: uppercase; letter-spacing: .16em; white-space: nowrap; }
.gx-page-bar {
  flex: 1; height: 4px; min-width: 60px;
  background: color-mix(in srgb, var(--carbon) 12%, transparent);
  overflow: hidden;
}
.gx-page-bar i {
  display: block; height: 100%;
  background: var(--ink, var(--signal));
  transition: width .6s cubic-bezier(.22, 1, .36, 1);
}
/* the bar FILLS when the sheet is laid down — progress seen arriving */
body.motion-full .gx-page-bar i { animation: gx-bar-grow .9s cubic-bezier(.22, 1, .36, 1) .3s both; }
@keyframes gx-bar-grow { from { width: 0; } }
.gx-page-done .gx-page-bar i { background: var(--signal); }
.gx-page-done .gx-page-n { color: var(--signal-ink, var(--signal)); font-weight: 600; }
.gx-page-n { font-variant-numeric: tabular-nums; white-space: nowrap; }
.gx-lamp {
  font-family: var(--serif); font-size: .92rem; color: var(--ink-1, var(--carbon));
}
.gx-lamp b { font-variant-numeric: tabular-nums; }
.gx-flame {
  font-style: normal; color: var(--signal);
  display: inline-block; margin-right: 4px;
}
body.motion-full .gx-flame { animation: gx-flame-sway 2.6s ease-in-out infinite; }
@keyframes gx-flame-sway {
  0%, 100% { transform: rotate(-6deg) scale(1); opacity: .85; }
  50% { transform: rotate(6deg) scale(1.12); opacity: 1; }
}
.gx-lamp-gold {
  color: var(--gold-leaf, #a5842c); font-size: 1.02rem;
}
body.motion-full .gx-lamp-gold { animation: gx-gild 1.6s ease .9s both; }
.gx-bridge { font-size: .8em; color: var(--steel); }
/* the milestone flame takes a crown: swells once, keeps a warm halo */
body.motion-full .gx-lamp-gold .gx-flame { animation: gx-flame-crown 1.8s ease .8s both; }
@keyframes gx-flame-crown {
  0% { transform: scale(1); text-shadow: none; }
  40% { transform: scale(1.7) rotate(-8deg);
        text-shadow: 0 0 18px color-mix(in srgb, var(--gold-leaf, #b8860b) 90%, transparent),
                     0 0 36px color-mix(in srgb, var(--gold-leaf, #b8860b) 45%, transparent); }
  100% { transform: scale(1.2);
         text-shadow: 0 0 9px color-mix(in srgb, var(--gold-leaf, #b8860b) 50%, transparent); }
}

/* THE GLINT — one pass of lamplight across the sheet on a gold strike */
.gx-glint { position: relative; overflow: hidden; }
.gx-glint::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 55%; left: -70%;
  background: linear-gradient(105deg, transparent,
    color-mix(in srgb, var(--gold-leaf, #b8860b) 26%, transparent), transparent);
  pointer-events: none;
  animation: gx-glint-sweep 1.1s cubic-bezier(.4, 0, .2, 1) .1s both;
}
@keyframes gx-glint-sweep { to { left: 125%; } }

/* the clean-proof bounty is STAMPED onto the sheet, not faded in */
body.motion-full .gx-clean { animation: gx-stamp-in .55s cubic-bezier(.2, 1.4, .4, 1) .5s both; }
@keyframes gx-stamp-in {
  from { transform: scale(1.7) rotate(-4deg); opacity: 0; }
  60% { transform: scale(.94) rotate(1deg); opacity: 1; }
  to { transform: none; opacity: 1; }
}
.gx-topic { font-size: var(--t-0); letter-spacing: .18em; }

/* Everything gets bigger, because there is room for it now. */
.gx-stage .gx-ask { font-size: var(--t-3); letter-spacing: .1em; }
.gx-stage .gx-word {
  font-size: clamp(44px, 8vw, 76px);
  /* the word itself wears the chapter's ink — the sheet's one saturated
     thing, exactly where the eye lives */
  color: color-mix(in srgb, var(--ink, var(--carbon)) 72%, var(--carbon));
}
.gx-stage .gx-gloss { font-size: clamp(24px, 3.6vw, 34px); }
.gx-stage .gx-line { font-size: clamp(22px, 3.2vw, 30px); line-height: 1.5; }
.gx-stage .gx-en { font-size: clamp(20px, 3vw, 26px); }
.gx-stage .gx-opt { font-size: var(--t-3); padding: 16px 20px; }
.gx-stage .gx-input { font-size: clamp(26px, 4vw, 36px); padding: 16px 18px; }
.gx-stage .gx-tile, .gx-stage .gx-set { font-size: var(--t-3); padding: 10px 16px; }
.gx-stage .gx-say { font-size: var(--t-3); padding: 16px 30px; }
.gx-stage .gx-rail i { height: 6px; }

/* The question itself changes; the frame does not. Keyed on the question index
   so the middle re-enters each time and the header stays put — which is what
   makes it feel like one room with a sequence in it rather than eight screens. */
.gx-mid { display: grid; gap: 22px; }
@media (prefers-reduced-motion: no-preference) {
  .gx-mid { animation: gx-turn 300ms var(--e-std, ease) both; }
  @keyframes gx-turn {
    from { opacity: 0; transform: translateX(-22px); }
    to   { opacity: 1; transform: none; }
  }

  /* EVERY SHAPE ARRIVES ITS OWN WAY. Eight questions entering identically is
     the loudest part of the monotony the owner named — so each exercise kind
     gets its own door, all from the print-shop's physics, all ≤320ms and
     decelerating, all dying to the correct final state under reduced motion:
       a SORT (word) is dropped into the case from above and lands dead;
       a LINE with a gap slides in from the right, the way a galley slides;
       a HEARD word opens from silence — scale, no travel;
       a LINE TO SET is laid down as a sheet (the room's one 3D beat);
       a WORD TO WRITE rises from the paper's foot to meet the hand. */
  @keyframes gx-in-drop  { from { opacity: 0; transform: translateY(-26px); } to { opacity: 1; transform: none; } }
  @keyframes gx-in-slide { from { opacity: 0; transform: translateX(26px); }  to { opacity: 1; transform: none; } }
  @keyframes gx-in-open  { from { opacity: 0; transform: scale(.94); }        to { opacity: 1; transform: none; } }
  @keyframes gx-in-lay   { from { opacity: 0; transform: perspective(900px) rotateX(14deg) translateY(-10px); }
                           to   { opacity: 1; transform: none; } }
  @keyframes gx-in-rise  { from { opacity: 0; transform: translateY(22px); }  to { opacity: 1; transform: none; } }
  /* meaning — the commonest question in the house — was the one shape with
     no entrance of its own: ink settling into the paper, wet to dry */
  @keyframes gx-in-ink { from { opacity: 0; filter: blur(4px); transform: translateY(6px); }
                         to   { opacity: 1; filter: blur(0);   transform: none; } }
  .gx-mid[data-kind="meaning"] { animation-name: gx-in-ink; animation-duration: 300ms; }
  .gx-mid[data-kind="word"]    { animation-name: gx-in-drop; animation-duration: 260ms; }
  .gx-mid[data-kind="gap"]     { animation-name: gx-in-slide; }
  .gx-mid[data-kind="listen"]  { animation-name: gx-in-open; animation-duration: 340ms; }
  .gx-mid[data-kind="order"]   { animation-name: gx-in-lay; animation-duration: 320ms; transform-origin: 50% 0; }
  .gx-mid[data-kind="write"]   { animation-name: gx-in-rise; }
  .gx-mid[data-kind="stress"]  { animation-name: gx-in-drop; animation-duration: 280ms; }
  .gx-mid[data-kind="form"]    { animation-name: gx-in-slide; }
  .gx-mid[data-kind="pairs"]   { animation-name: gx-in-rise; animation-duration: 300ms; }
  .gx-mid[data-kind="hearline"]{ animation-name: gx-in-open; animation-duration: 340ms; }

  /* THE SORTS ARRIVE ONE BY ONE. A compositor sets a line sort by sort, not
     as a block — the options follow their question in a run of small drops,
     each ≤60ms behind the last, all dead still inside a third of a second.
     This is the single cheapest cure for eight identical screens: the case
     is visibly SET each time, and no two questions land the same. */
  @keyframes gx-opt-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
  .gx-stage .gx-opts .gx-opt,
  .gx-stage .gx-tiles .gx-tile,
  .gx-stage .gx-pcol > * {
    animation: gx-opt-in 200ms var(--e-std, ease) both;
  }
  .gx-stage :is(.gx-opts, .gx-tiles, .gx-pcol) > :nth-child(2) { animation-delay: 45ms; }
  .gx-stage :is(.gx-opts, .gx-tiles, .gx-pcol) > :nth-child(3) { animation-delay: 90ms; }
  .gx-stage :is(.gx-opts, .gx-tiles, .gx-pcol) > :nth-child(4) { animation-delay: 135ms; }
  .gx-stage :is(.gx-opts, .gx-tiles, .gx-pcol) > :nth-child(5) { animation-delay: 180ms; }
  .gx-stage :is(.gx-opts, .gx-tiles, .gx-pcol) > :nth-child(n+6) { animation-delay: 220ms; }

  /* THE HALFWAY MARK: the set half of the rail is swept once, left to right —
     ink catching the light as the compositor passes his thumb along the line.
     Fired once a lesson, by the verdict that says "Half the case is set." */
  @keyframes gx-rail-sweep {
    0% { filter: brightness(1); } 45% { filter: brightness(1.6); } 100% { filter: brightness(1); }
  }
  .gx-rail-half .gx-r-done { animation: gx-rail-sweep 500ms var(--e-std, ease) both; }
  .gx-rail-half .gx-r-done:nth-child(2) { animation-delay: 60ms; }
  .gx-rail-half .gx-r-done:nth-child(3) { animation-delay: 120ms; }
  .gx-rail-half .gx-r-done:nth-child(4) { animation-delay: 180ms; }
  .gx-rail-half .gx-r-done:nth-child(5) { animation-delay: 240ms; }
  .gx-rail-half .gx-r-done:nth-child(6) { animation-delay: 300ms; }
}

/* THE ROOM'S HEAT. The ink band at the head of the stage thickens with the
   run — each consecutive right answer feeds it, a miss lets it settle back.
   The room warming is a fact about the reader; it is never a judgement, so
   it has no colour of its own and no sound. */
.gx-stage::before { height: calc(6px + var(--heat, 0) * 10px); }
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .gx-stage::before { transition: height 500ms var(--e-std, ease); }
}
/* a miss drops the heat in ONE silent frame — the room never animates at a
   friction moment, so the fall gets no transition at all */
.gx-stage.gx-heat-cut::before { transition: none; }

/* THE BOOK'S OWN PAPER, AT THE CHAPTER'S DEPTH. The pigment is the BOOK's —
   one ink per spine — for a while this comment claimed each chapter its own
   stock, which the code never did: every chapter of «Шинель» sat on the same
   graphite. What the chapter actually owns is DEPTH. --tint is the scene's
   index, and each chapter deepens the wash by another 2%, so the chapters of
   one book become deepening rooms of one house — further from the street,
   closer to the press. AND THE PAPER BREATHES WITH THE RUN: heat feeds
   another 7% at full stride, the ink taking, as a pressman would say. Always
   far below text contrast; the paper-neutrals clause holds at both ends. */
.gx-stage {
  background: color-mix(in srgb, var(--ink, var(--carbon))
    calc(15% + var(--tint, 0) * 2% + var(--heat, 0) * 7%), var(--room));
  transition: background 900ms var(--e-std, ease);
}
/* THE CUT IS THE BAND'S ALONE (2026-08-25, the owner's call: «just make sure
   it's super smooth»). This line used to read `.gx-stage.gx-heat-cut
   { transition: none }` — the whole stage — which meant that on every miss,
   for the two frames the class is up, the room lost the 900ms background and
   the 1400ms vignette above and SNAPPED: --heat falls to 0 in the same frame,
   and a second miss flips data-mood to "steady", so the ground and the walls
   both jumped at once. A miss is common, so the room appeared to lurch after
   every other question. That is what Tom was reporting.

   What the charter actually asks for is that the room must not COMMENT on
   friction — and the comment it forbids is the visible one: a band shrinking
   in front of you. That still happens in one silent frame (the ::before rule
   above is untouched). The ground and the walls are not commentary; they are
   the room settling, over the better part of a second, below the threshold at
   which anything reads as a reaction. Keeping them silent bought nothing and
   cost the smoothness. */ 

/* THE MOMENT'S MOOD — one press-pass along the ink band when a tier lands:
   the roller crossing the forme, left to right, once. Different weights for
   different moments; gone in under a second; never at friction. */
@media (prefers-reduced-motion: no-preference) {
  @keyframes gx-pass {
    0% { box-shadow: inset 0 0 0 0 transparent; transform: scaleX(0); transform-origin: 0 50%; opacity: .9; }
    100% { transform: scaleX(1); opacity: 0; }
  }
  .gx-stage[class*="gx-m-"]::after {
    content: ''; position: absolute; left: 0; right: 0; top: 0;
    height: calc(6px + var(--heat, 0) * 10px);
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.55), transparent);
    animation: gx-pass 700ms var(--e-std, ease) both;
    pointer-events: none;
  }
  .gx-m-last::after { animation-duration: 950ms; height: 18px; }

  /* ALIVE AT REST, by the letter of the law: one heartbeat on one element.
     The current rail mark breathes on the brand's 4-second tempo — the one
     thing in the room that is about *now*. Nothing else moves at rest. */
  @keyframes gx-now-breath { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
  .gx-rail .gx-r-now { animation: gx-now-breath 4s ease-in-out infinite; }
}

/* the ink answers the hand: options seep toward the chapter's pigment on
   hover — paper taking ink where the finger hovers, settling back when it
   leaves. A transition, not an animation: it follows the reader exactly.
   At an amplitude the eye actually receives — 10% read as nothing on most
   panels — and with the compositor's thumb-rule inked down the left edge. */
.gx-stage .gx-opt {
  transition: background 180ms linear, border-color 180ms linear,
    box-shadow 180ms linear, transform 120ms var(--e-std, ease);
}
.gx-stage .gx-opt:not(:disabled):hover {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 18%, var(--galley));
  border-color: var(--ink, var(--carbon));
  box-shadow: inset 4px 0 0 var(--ink, var(--carbon));
}
/* THE HAND FEELS THE SORT LAND — the ≤100ms law, at the press itself: the
   type presses into the paper the instant the finger commits, before any
   verdict exists. Tactility is what makes the fiftieth question feel like
   a thing done rather than a form filled. */
.gx-stage :is(.gx-opt, .gx-tile, .gx-pw, .gx-pg, .gx-say):not(:disabled):active {
  transform: translateY(1px) scale(.99);
  transition-duration: 40ms;
}
.gx-stage :is(.gx-tile, .gx-pw, .gx-pg, .gx-say) {
  transition: background 140ms linear, border-color 140ms linear,
    opacity 300ms var(--e-std, ease), transform 120ms var(--e-std, ease);
}
/* the run counter is a fact about the reader — it earns the chapter's ink */
.gx-runn { color: var(--ink, var(--carbon)); }
[data-theme="dark"] .gx-runn {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}

/* ============================================================================
   EACH KIND OF QUESTION HAS ITS OWN SILHOUETTE. Eight kinds all rendered as
   the same stack of full-width rows was legible only by reading; the shape
   of the answers should say what is being asked before a word is. All of it
   is layout on data-kind — no script decides anything here.

   WORD, LISTEN — and the short grammar sorts, STRESS and FORM — are single
   sorts to be picked from the case: a tiled grid of centred serif type, the
   compartments of a type case.
   GAP takes loose sorts: the candidate words sit centred under the sentence
   at their natural width, waiting to be picked up.
   MEANING and HEARLINE keep the full-width rows: a meaning is a phrase and
   a heard line is a sentence — both are read left to right, not glanced.
   (ORDER, WRITE and PAIRS carry their own furniture — stick, input, board —
   and need no silhouette from here.)
   ============================================================================ */
.gx-stage .gx-mid[data-kind="word"] .gx-opts,
.gx-stage .gx-mid[data-kind="listen"] .gx-opts,
.gx-stage .gx-mid[data-kind="stress"] .gx-opts,
.gx-stage .gx-mid[data-kind="form"] .gx-opts {
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
}
.gx-stage .gx-mid[data-kind="word"] .gx-opt,
.gx-stage .gx-mid[data-kind="listen"] .gx-opt,
.gx-stage .gx-mid[data-kind="stress"] .gx-opt,
.gx-stage .gx-mid[data-kind="form"] .gx-opt {
  text-align: center;
  font-family: var(--serif);
  font-size: clamp(22px, 3vw, 30px);
}
.gx-stage .gx-mid[data-kind="gap"] .gx-opts {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
}
.gx-stage .gx-mid[data-kind="gap"] .gx-opt { text-align: center; }

/* ============================================================================
   THE WEATHER — the room as a presence. Two ambient states, entered and left
   over more than a second, so they read as light changing in a room rather
   than an interface reacting. Both narrate facts about the reader; neither
   ever fires AT a moment of friction.

   FOCUS (a run of four): the edges dim and the chrome softens out of the
   eye — header and rail lose contrast and take half a hair of blur — so the
   question sits alone in the light. The tunnel is the reward.
   STEADY (a rough patch): the light lowers like a lamp brought closer; the
   band above quiets. Shelter. Nothing moves, nothing is red, and one right
   answer lifts it.
   ============================================================================ */
.gx-stage { transition: background 900ms var(--e-std, ease), box-shadow 1400ms var(--e-std, ease); }
/* THE ROOM HAS WALLS at rest: a quiet vignette so the paper sits in a lit
   place rather than a void — the moods then deepen or lower this base light */
.gx-stage { box-shadow: inset 0 0 11vmax rgba(24, 19, 9, .09); }
[data-theme="dark"] .gx-stage { box-shadow: inset 0 0 11vmax rgba(0, 0, 0, .32); }
.gx-stage[data-mood="focus"] { box-shadow: inset 0 0 14vmax rgba(22, 18, 8, .16); }
.gx-stage[data-mood="focus"] .gx-top,
.gx-stage[data-mood="focus"] .gx-rail {
  opacity: .55; filter: blur(.6px);
}
.gx-stage .gx-top, .gx-stage .gx-rail {
  transition: opacity 1100ms var(--e-std, ease), filter 1100ms var(--e-std, ease);
}
.gx-stage[data-mood="steady"] { box-shadow: inset 0 0 18vmax rgba(30, 22, 8, .12); }
.gx-stage[data-mood="steady"]::before { opacity: .45; transition: opacity 1100ms var(--e-std, ease); }
[data-theme="dark"] .gx-stage[data-mood="focus"] { box-shadow: inset 0 0 14vmax rgba(0, 0, 0, .5); }
[data-theme="dark"] .gx-stage[data-mood="steady"] { box-shadow: inset 0 0 18vmax rgba(0, 0, 0, .4); }

/* ---- THE TYPE EARNS THE ROOM. Full-screen paper can afford generous type,
   and small print is what makes a ceremony feel like a form. Everything the
   reader actually reads in the room comes up a size. ---- */
.gx-stage .gx-src { font-size: clamp(18px, 2.4vw, 24px); }
.gx-stage .gx-src span { font-size: .8em; }
.gx-stage .gx-verdict { font-size: clamp(20px, 3vw, 28px); }
.gx-stage .gx-gr-body { font-size: clamp(18px, 2.4vw, 22px); line-height: 1.7; }
.gx-stage .gx-gr-note { font-size: var(--t-2); }
.gx-stage .gx-toward, .gx-stage .gx-fell-n, .gx-stage .gx-near-n { font-size: var(--t-2); }
.gx-stage .gx-near-line { font-size: var(--t-2); }
.gx-stage .gx-ghost-ru { font-size: clamp(19px, 2.6vw, 24px); }
.gx-stage .gx-facts { font-size: var(--t-1); }
.gx-mate-line { font-size: var(--t-1); }

/* ---- the flooded micro-tale (charter Act II): a little text, harvested.
   The tale reads as set prose; the target forms are sorts a hand can touch,
   and each one found takes its place in the margin case. ---- */
.gx-flood { max-width: 56ch; }
.gx-fl-line { font-family: var(--serif); font-size: 1.24rem; line-height: 1.75; margin: 2px 0 0; }
.gx-fl-en { font-size: .86rem; opacity: .62; margin: 0 0 10px; }
.gx-fl-w {
  font: inherit; color: inherit; background: none; border: 0; padding: 0 1px;
  cursor: pointer; border-bottom: 1px dashed color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
}
.gx-fl-w:hover:not(:disabled) { background: var(--sheet); }
.gx-fl-w.gx-fl-hit {
  border-bottom: 2px solid var(--g-gold, #c9a227);
  background: color-mix(in srgb, var(--g-gold, #c9a227) 14%, transparent);
  font-weight: 600;
}
.gx-harvest { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; min-height: 28px; }
.gx-fl-chip {
  font-family: var(--serif); font-style: normal; font-size: .92rem;
  padding: 2px 9px; background: var(--galley);
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, var(--carbon));
  box-shadow: 0 1px 0 var(--press, rgba(0,0,0,.2));
}

/* ---- the rule hunt (Act III): lines sorted by hand into two named piles,
   then the law said aloud. The piles are cases; a set line is a sort laid
   into its column. ---- */
.gx-piles { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin: 10px 0; }
.gx-pile {
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--sheet));
  padding: 8px 10px; min-height: 64px;
}
.gx-pile h4 {
  margin: 0 0 6px; font-family: var(--sans); font-size: .72rem;
  letter-spacing: .06em; text-transform: uppercase; opacity: .75;
}
.gx-hl { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 5px 0; }
.gx-hl-ru { font-family: var(--serif); font-size: 1.08rem; }
.gx-hl-en { font-size: .8rem; opacity: .6; }
.gx-hl-btns { margin-left: auto; display: inline-flex; gap: 6px; }
.gx-hl-b {
  font-family: var(--serif); font-size: .9rem; line-height: 1;
  padding: 4px 10px; cursor: pointer; background: var(--galley);
  border: 1px solid var(--carbon); border-radius: 0;
}
.gx-hl-b:hover { background: var(--sheet); }
.gx-hl-set .gx-hl-ru { font-weight: 600; }
.gx-hl-no { animation: gx-nudge calc(0.2s * var(--motion, 1)) ease-out both; }
@media (max-width: 560px) { .gx-piles { grid-template-columns: 1fr; } }

/* ---- the outcome theater: one line on the stage, large, and a situation
   only the structure can answer ---- */
.gx-scene-line { font-size: clamp(1.5rem, 4.6vw, 2.1rem); line-height: 1.3; }
.gx-scene-en { opacity: .72; margin-top: 2px; }

/* ---- the grammar sheets read like a fine page: measure, leading, calm ---- */
.gx-gr-body { max-width: 58ch; font-size: 1.04rem; line-height: 1.75; }

/* ---- THE TEACHING PLATE, fourth cutting (the owner: «text boring small
   no love... brand coherent, not just lines»). The plate is set like a
   title page now: the emblem struck in a printed MEDALLION (the same
   ground the residents stand in), the title at display size, the note at
   a book's measure and a book's size, the specimen on a pigment ground
   with the horn as a round inked seal, and a soft wash of the unit's own
   ink behind the sheet instead of outline clip-art. ---- */
.gx-plate { position: relative; max-width: 70ch; margin-inline: auto; }
/* the pigment wash — light, round, behind everything; no outlines */
.gx-tp-wm {
  position: fixed; right: -14vmin; bottom: -16vmin;
  width: 72vmin; height: 72vmin; border-radius: 50%;
  background: radial-gradient(circle at 50% 45%,
    color-mix(in srgb, var(--ink, var(--carbon)) 16%, transparent),
    transparent 70%);
  pointer-events: none; z-index: 0;
}
.gx-tp-wm svg { display: none; }
.gx-tp-head {
  display: flex; align-items: center; gap: 18px; text-align: left;
  margin: 6px 0 8px; position: relative; z-index: 1;
  background: transparent;
}
/* the emblem in its medallion — the residents' own vignette recipe */
.gx-tp-emb {
  width: 76px; height: 76px; flex: 0 0 auto; position: relative;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 38%,
    color-mix(in srgb, var(--ink, var(--carbon)) 8%, var(--galley)),
    color-mix(in srgb, var(--ink, var(--carbon)) 22%, var(--galley)));
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  box-shadow: inset 0 0 0 4px var(--galley),
    inset 0 0 0 5px color-mix(in srgb, var(--ink, var(--carbon)) 40%, transparent),
    0 2px 3px color-mix(in srgb, var(--ink, var(--carbon)) 20%, transparent);
  color: var(--ink, var(--carbon));
}
.gx-tp-emb svg { width: 58%; height: 58%; }
.gx-tp-orn { font-size: 34px; font-style: normal; }
.gx-tp-ti {
  font-family: var(--serif); font-style: normal; font-weight: 600;
  font-size: clamp(1.8rem, 5.2vw, 2.6rem); line-height: 1.12;
  margin: 0; letter-spacing: .01em; text-wrap: balance;
}
.gx-tp-rule {
  display: flex; align-items: center; gap: 12px; width: 100%; max-width: 62ch;
  margin: 4px 0 16px; color: var(--ink, var(--carbon));
  position: relative; z-index: 1;
}
.gx-tp-rule i { flex: 1; border-top: 2px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent); }
.gx-tp-rule span { font-size: 1.1rem; }
.gx-plate .gx-teach-note {
  font-size: 1.14rem; line-height: 1.8; max-width: 62ch;
  font-style: normal; position: relative; z-index: 1; margin: 0;
}
.gx-tp-spec {
  margin: 22px 0 0; padding: 22px 24px 18px; max-width: 56ch;
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 7%, var(--sheet));
  box-shadow: inset 0 0 0 3px var(--sheet),
    inset 0 0 0 4px color-mix(in srgb, var(--ink, var(--carbon)) 30%, transparent),
    0 3px 0 var(--press, rgba(0,0,0,.15));
  text-align: center; position: relative; z-index: 1;
}
.gx-tp-spec .gx-line {
  font-size: clamp(1.6rem, 4.8vw, 2.2rem); font-style: normal; line-height: 1.35;
  display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap;
}
/* the horn as a seal: round, inked, paper glyph — never a boxed browser icon */
.gx-stage .gx-tp-spec .gx-seal-say, .gx-tp-spec .gx-say {
  width: 52px; height: 52px; border-radius: 50%; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--ink, var(--carbon)); color: var(--galley);
  border: 0;
  box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--galley) 35%, transparent),
    0 2px 0 var(--press, rgba(0,0,0,.25));
  flex: 0 0 auto;
}
.gx-stage .gx-tp-spec .gx-seal-say:hover, .gx-tp-spec .gx-say:hover { transform: translateY(-1px); }
.gx-stage .gx-tp-spec .gx-seal-say:active, .gx-tp-spec .gx-say:active { transform: translateY(1px); box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--galley) 35%, transparent); }
.gx-stage .gx-tp-spec .gx-seal-say svg, .gx-tp-spec .gx-say svg { width: 22px; height: 22px; fill: currentColor; }
.gx-tp-spec figcaption { opacity: .72; margin-top: 8px; font-size: 1rem; }
.gx-tp-sorts { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-top: 18px; position: relative; z-index: 1; }
.gx-tp-sorts i {
  font-family: var(--serif); font-style: normal; font-size: .95rem;
  padding: 3px 12px; background: transparent; opacity: .9;
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 35%, transparent);
  border-radius: 999px;
}
.gx-tp-sorts-cap { text-align: center; font-size: .78rem; opacity: .6; margin-top: 8px; position: relative; z-index: 1; }

/* ---- the arc's own face: an epigraph from the shelf on station one, and
   the arc's mark as a faint watermark behind the sheets ---- */
.gx-arc-epi {
  font-family: var(--serif); font-style: normal; font-size: 1.02rem;
  opacity: .68; margin: 6px 0 2px; max-width: 48ch;
}
.gx-arc-wm {
  position: fixed; right: -2vmin; bottom: -6vmin;
  font-size: clamp(200px, 44vmin, 480px); line-height: 1; font-style: normal;
  color: color-mix(in srgb, var(--ink, var(--carbon)) 8%, transparent);
  pointer-events: none; user-select: none; z-index: 0;
}
.gx-verdict .gx-v-gloss { font-size: var(--t-0); }

/* ============================================================================
   THE BLOOM — the scene bleeding into the margin. One wide, deeply blurred
   wash of the chapter's pigment at the paper's edge, the scene's ornament
   ghosted huge inside it. It alternates sides as the lesson walks, enters
   over a second and then HOLDS — the intrigue is in the stillness of a
   colour that was not there a moment ago. Never under the text column,
   never sharp, never a meaning: the book keeping the reader company.
   ============================================================================ */
.gx-bloom {
  position: fixed; top: 12vh; bottom: 12vh; width: min(34vw, 420px);
  pointer-events: none; z-index: 0; opacity: 0;
  background: radial-gradient(ellipse 70% 55% at 50% 42%,
    color-mix(in srgb, var(--ink, var(--carbon)) 36%, transparent), transparent 70%);
  filter: blur(28px);
  display: grid; place-items: center;
}
.gx-bloom i {
  font-style: normal; font-size: clamp(140px, 24vw, 340px);
  color: var(--ink, var(--carbon)); opacity: .26; filter: blur(2px);
  transform: rotate(-8deg);
}
/* A SIDE IS NOT OPTIONAL (2026-08-25). These two rules were the ONLY things
   giving .gx-bloom a horizontal position, and course.js writes
   `panel.dataset.side = ''` on the narrative beats — an empty string, which
   matches neither selector. With no left and no right, a fixed box falls back
   to `auto`, which resolves to its static position: a 420px blurred glyph,
   meant to bleed off the edge of the room, landing IN THE MIDDLE OF THE CARD.
   That is the grey smudge sitting in the empty half of every «where you
   stood» screen — the first thing a reader sees in a lesson.
   90-chromatic-game.css:2298 had already noticed the mechanism was «written
   since August, drawn never». The default now lives on the element itself, so
   the bloom is off-stage whatever the attribute says, and only the OTHER side
   needs an override. */
.gx-bloom { left: -8vw; right: auto; }
.gx-stage[data-side="r"] .gx-bloom { right: -8vw; left: auto; }
@media (prefers-reduced-motion: no-preference) {
  @keyframes gx-bloom-in {
    from { opacity: 0; transform: translateY(24px) scale(.94); }
    to   { opacity: 1; transform: none; }
  }
  .gx-bloom { animation: gx-bloom-in 1300ms var(--e-std, ease) .15s both; }
}
@media (prefers-reduced-motion: reduce) { .gx-bloom { opacity: 1; } }
/* the moods reach the margin too: focus deepens the bleed, a rough patch
   pales it — the paper's edge telling the same truth as its light */
.gx-stage[data-mood="focus"] .gx-bloom { opacity: 1; filter: blur(22px); }
.gx-stage[data-mood="steady"] .gx-bloom { opacity: .5; }
/* narrow screens have no margin to colour, so the bloom slides under the
   foot of the page instead: the book's pigment keeping the reader company
   below the flow — never sharp, never a meaning. (It used to be
   display:none here; a phone deserves the book's colour too.) The entrance
   animation is dropped because its fill would pin opacity at 1. */
@media (max-width: 900px) {
  .gx-stage .gx-bloom {
    top: auto; bottom: -8vh; height: 34vh;
    opacity: .55; animation: none;
  }
}

/* the spoken Russian of a tier: large, in the chapter's ink, with its
   transliteration and the plain English fact set small beneath */
.gx-verdict .gx-v-say {
  display: block; font-family: var(--serif); font-size: 1.5em;
  color: var(--ink, var(--carbon));
}
.gx-verdict .gx-v-gloss {
  display: block; font-family: var(--sans); font-style: normal;
  font-size: var(--t-00); letter-spacing: .06em; color: var(--steel);
  margin-top: 3px;
}
[data-theme="dark"] .gx-verdict .gx-v-say {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}

/* THE COUP — gold, because a genuine upset is a celebration and celebration
   is gold's one lawful job. The Russian illuminates like a manuscript
   initial; the margin bloom flares with it; everything settles inside two
   breaths and the room is calm paper again. */
.gx-verdict.gx-v-coup { font-size: 1.35em; }
.gx-verdict.gx-v-coup .gx-v-say { color: var(--reward, var(--signal)); }
.gx-verdict.gx-v-coup .gx-v-say { animation: goldbloom calc(700ms * var(--motion, 1)) var(--e-snap, ease); }
.gx-stage.gx-m-coup .gx-bloom { opacity: 1; filter: blur(20px) brightness(1.25); }
.gx-stage.gx-m-coup::after { background: linear-gradient(90deg, transparent,
  color-mix(in srgb, var(--reward, var(--signal)) 55%, transparent), transparent); }

/* the marginal note: one line of craft-wisdom at the seal and under each
   instalment's head — set as a printed aside, sepia and small-but-readable,
   the register of a teacher who says one true thing and steps back */
.gx-wis, .margin-note {
  font-family: var(--serif); font-style: normal; font-size: var(--t-1);
  color: var(--steel); max-width: 34em; margin: 6px auto 0;
  text-align: center; line-height: 1.55;
}
.margin-note { margin-top: 10px; }

/* the distance line: the reader's whole holding, said plainly — and lit
   only at a first crossing of a hundred */
.gx-drive { margin: 0; font-family: var(--serif); font-size: var(--t-2); color: var(--carbon); }
.gx-drive b { font-variant-numeric: tabular-nums; }
.gx-drive-gold b { color: var(--signal-ink, var(--signal)); }

/* the seal's cue, and the rest of the ceremony arriving once it is struck.
   Carbon, not steel: this line is an instruction the reader must be able to
   read, and steel at small sizes is reserved for glances, not directions. */
.gx-seal-cue {
  margin: 4px 0 0; font-size: var(--t-0); letter-spacing: .06em;
  color: var(--carbon); text-align: center;
}
.gx-seal-later { display: block; margin: 10px auto 0; }
.gx-seal-rest { display: grid; gap: 12px; justify-items: center; }
@media (prefers-reduced-motion: no-preference) {
  .gx-rest-in { animation: gx-rest-in 480ms var(--e-std, ease) both; }
  @keyframes gx-rest-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
}

/* ---- the grammar lesson: the article's sheets, and its checks ---- */
.gx-gr-body {
  font-family: var(--serif); font-size: var(--t-2); line-height: 1.65;
  max-width: 34em; white-space: pre-line;
}
.gx-gr-note {
  margin: 0; font-family: var(--serif); font-style: normal;
  font-size: var(--t-1); color: var(--carbon);
  border-left: 3px solid var(--ink, var(--carbon)); padding-left: 12px;
}
.gx-gr-hi { color: var(--ink, var(--carbon)); font-weight: 700; }
[data-theme="dark"] .gx-gr-hi {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}

/* ============ THE GRAMMAR PAGE, ALIVE UNDER THE HAND ============
   Every Russian word in the article wears the reading face and the
   chapter's ink, and speaks when touched; endings sit in little trays
   like loose sorts; the first sheet opens with an initial. */
.gx-gr-ru {
  font-family: var(--serif); font-weight: 600; cursor: pointer;
  color: color-mix(in srgb, var(--ink, var(--carbon)) 72%, var(--carbon));
  border-bottom: 1px dotted color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
  transition: color 140ms linear, border-color 140ms linear;
}
.gx-gr-ru:hover { border-bottom-style: solid; }
.gx-gr-ru.gx-gr-said { color: var(--signal-ink, var(--signal)); }
.gx-gr-end {
  font-style: normal; font-family: var(--serif); font-weight: 600;
  color: color-mix(in srgb, var(--ink, var(--carbon)) 78%, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 8%, var(--galley));
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 40%, transparent);
  padding: 0 5px; margin: 0 1px; white-space: nowrap;
}
.gx-gr-body[data-beat="0"]::first-letter {
  font-size: 2.6em; float: left; line-height: .85;
  padding: 2px 8px 0 0; font-weight: 700;
  color: color-mix(in srgb, var(--ink, var(--carbon)) 75%, var(--carbon));
}
.gx-gr-hint {
  margin: 0; font-size: .74rem; font-style: italic; color: var(--steel);
  font-family: var(--serif);
}
/* the grammar sitting's rail sits tight under the header */
.gx-gr-rail { margin: 8px 0 0; }
.gx-gr-rail i { max-width: 44px; }

/* THE HUNT — the line dealt as loose sorts, one of them carrying the point */
.gx-find { display: flex; flex-wrap: wrap; gap: 8px 6px; justify-content: center; }
.gx-find-w {
  font-family: var(--serif); font-size: clamp(20px, 2.8vw, 26px);
  background: var(--galley); border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 35%, transparent);
  border-bottom-width: 3px;
  padding: 8px 12px; cursor: pointer; color: inherit;
  transition: background 140ms linear, border-color 140ms linear, transform 120ms var(--e-std, ease);
}
.gx-find-w:not(:disabled):hover {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 10%, var(--galley));
  border-color: var(--ink, var(--carbon));
  transform: translateY(-2px);
}
.gx-find-w:not(:disabled):active { transform: translateY(1px); transition-duration: 40ms; }
.gx-find-w.gx-right { background: var(--malachite); color: var(--on-malachite); border-color: var(--malachite); }
.gx-find-w.gx-wrong { border-color: var(--signal); color: var(--signal-ink); }
.gx-find-w:disabled { cursor: default; opacity: .75; }
.gx-find-w.gx-right:disabled, .gx-find-w.gx-wrong:disabled { opacity: 1; }
.rd-gram .rd-l-no { font-family: var(--serif); font-weight: 700; }

/* an encore mark on the rail: hollow until cleared — an appointment, kept */
.gx-rail .gx-r-enc:not(.gx-r-done) { background: none; box-shadow: inset 0 0 0 1.5px var(--ink, var(--carbon)); opacity: .7; }

/* THE VERDICT'S WEIGHT IS ITS MEANING. Plain rights stay quiet; the tiers
   earned by true events arrive larger and in the chapter's own ink. */
.gx-verdict.gx-v-first { color: var(--ink, var(--carbon)); }
.gx-verdict.gx-v-won   { color: var(--ink, var(--carbon)); }
.gx-verdict.gx-v-run   { color: var(--ink, var(--carbon)); font-size: 1.25em; }
.gx-verdict.gx-v-half  { color: var(--ink, var(--carbon)); font-size: 1.25em; }
.gx-verdict.gx-v-last  { font-size: 1.4em; }
.gx-last-mark { color: var(--ink, var(--carbon)); }

/* THE INKS ARE PIGMENTS MIXED FOR PAPER, and on the dark ground they drown.
   Anywhere a pigment is used as TEXT, dark mode lifts it by mixing it into
   the theme's own text colour — same hue, readable lightness (the APCA rule
   the brand already keeps for its accent). */
[data-theme="dark"] :is(.gx-verdict.gx-v-first, .gx-verdict.gx-v-won,
  .gx-verdict.gx-v-run, .gx-verdict.gx-v-half, .gx-last-mark, .gx-topic i) {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}
[data-theme="dark"] .gx-rail .gx-r-enc:not(.gx-r-done) {
  box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}

.gx-stage .gx-print { padding: 40px 32px; }
.gx-stage .gx-print-ru { font-size: clamp(28px, 5vw, 44px); }
.gx-stage .gx-tally { font-size: clamp(34px, 6vw, 54px); }

@media (max-width: 620px) {
  .gx-stage { padding: 16px 14px; align-content: start; }
  .gx-stage .gx { gap: 20px; }
  .gx-top { flex-wrap: wrap; row-gap: 4px; }
}


/* THE SORTING BOARD — four sorts back into their compartments. Two columns,
   the chosen sort carrying the chapter ink until it finds its home. */
.gx-pairs { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; }
.gx-pcol { display: grid; gap: 10px; align-content: start; }
.gx-pw, .gx-pg {
  font-family: inherit; color: inherit; text-align: left; cursor: pointer;
  background: var(--galley); border: 1px solid var(--carbon); border-radius: 0;
  padding: 12px 14px; font-size: var(--t-2);
  transition: background 140ms linear, border-color 140ms linear, opacity 300ms var(--e-std, ease);
}
.gx-pw { font-family: var(--serif); }
/* THE SORT IN HAND — unmistakable: the chosen word takes the chapter's whole
   ink and paper-white type, not a 14% wash the eye reads as a rendering
   accident. One glance answers "what am I holding?". */
.gx-p-sel {
  background: var(--ink, var(--carbon)) !important;
  border-color: var(--ink, var(--carbon));
  color: var(--galley) !important;
  box-shadow: 0 2px 8px color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
  transform: translateY(-1px);
}
/* A MATCH IS A FACT ON THE BOARD: both halves keep full legibility, take a
   quiet wash of the ink, and wear the SAME compartment number — the reader
   can read back every pairing they have made without remembering anything. */
.gx-p-done {
  opacity: 1; cursor: default;
  position: relative;
  /* !important: the pairs-board silhouette rule is more specific and would
     otherwise keep its resting background under the matched state */
  background: var(--malachite-tint) !important;
  border-color: color-mix(in srgb, var(--malachite) 60%, transparent);
  color: color-mix(in srgb, var(--malachite-ink) 85%, transparent);
  box-shadow: none;
}
.gx-p-done::after {
  content: attr(data-mate);
  position: absolute; top: -9px; right: -7px;
  width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center;
  background: var(--malachite); color: var(--on-malachite);
  font-family: var(--sans); font-size: var(--t-00); font-weight: 600;
  line-height: 1; box-shadow: 0 1px 3px rgba(0,0,0,.25);
}
/* the two halves settle together — one shared pulse links them spatially */
body.motion-full .gx-p-done { animation: gx-pair-set 360ms var(--e-std, ease); }
@keyframes gx-pair-set {
  0% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--malachite) 65%, transparent); }
  100% { box-shadow: 0 0 0 0 transparent; }
}
@media (max-width: 560px) { .gx-pairs { grid-template-columns: 1fr; } }

/* ============================================================================
   NO SORT IS EVER CUT OFF. «превосходительство» is longer than any tile the
   grid can afford it; every game button must let a long word wrap rather
   than clip at the tin's edge — and anything set ON the ink must be
   paper-white, never ink-on-ink.
   ============================================================================ */
.gx-stage :is(.gx-opt, .gx-tile, .gx-pw, .gx-pg, .gx-find-w, .gx-set) {
  min-width: 0; overflow-wrap: anywhere; white-space: normal;
}
.gx-stage :is(.gx-opt, .gx-tile, .gx-pw, .gx-pg, .gx-find-w, .gx-vowel).gx-right {
  color: var(--on-malachite) !important;
}


/* ============================================================================
   THE MOVEMENTS LOOK DIFFERENT — the owner's law: different things must
   HAPPEN VISUALLY. The stage wears its movement, and each keeps the
   chapter's depth (--tint) under its own wash:
   У ДВЕРИ  — morning paper, light falling from above, and the band opens
              from the middle outward, as a door does;
   НАБОР    — the working wash: the base rule above, breathing with the run;
   КОВКА    — the deep room: strongest pigment, darkened edges, a thicker
              band, the sorts cut heavier — the smithy where words are
              struck hard;
   СБОР     — ruled paper: faint rules across the whole room, a page waiting
              to be assembled — and the gathered lines land on it as SET
              TYPE, each a printed row.
   ============================================================================ */
.gx-stage[data-move="door"] {
  background: color-mix(in srgb, var(--ink, var(--carbon))
    calc(13% + var(--tint, 0) * 2%), var(--room));
  /* light from above: the book's ink falling down the top of the room,
     thinning to nothing before it reaches the work — and a floor under it,
     so the space below the sheet reads as a room and not a void */
  box-shadow: inset 0 45vh 35vh -35vh
    color-mix(in srgb, var(--ink, var(--carbon)) 24%, transparent),
    inset 0 -34vh 32vh -32vh color-mix(in srgb, var(--carbon) 22%, transparent);
}
/* a door opens from the middle, not from the hinge */
.gx-stage[data-move="door"]::before { transform-origin: 50% 50%; }
.gx-stage[data-move="forge"] {
  background: color-mix(in srgb, var(--ink, var(--carbon))
    calc(28% + var(--tint, 0) * 2%), var(--room));
  box-shadow: inset 0 0 16vmax rgba(20, 14, 6, .38);
}
[data-theme="dark"] .gx-stage[data-move="forge"] {
  box-shadow: inset 0 0 16vmax rgba(0, 0, 0, .55);
}
.gx-stage[data-move="forge"]::before { height: calc(14px + var(--heat, 0) * 10px); }
/* the sorts are cut heavier at the forge: a thicker border in a deeper ink */
.gx-stage[data-move="forge"] .gx-opt {
  border-width: 2px;
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
}
/* '— the hard way' used to be conjured here as a ::after on the ask; it now
   travels in the markup as .gx-forge-mark, where a screen reader can find it.
   The register is unchanged: an aside, not a heading. */
.gx-forge-mark {
  font-style: italic; text-transform: none; letter-spacing: 0; opacity: .7;
}
.gx-stage[data-move="gather"] {
  background:
    repeating-linear-gradient(transparent 0 34px,
      color-mix(in srgb, var(--ink, var(--carbon)) 20%, transparent) 34px 35px),
    linear-gradient(180deg,
      color-mix(in srgb, var(--signal) 9%, transparent), transparent 40vh),
    color-mix(in srgb, var(--ink, var(--carbon))
      calc(5% + var(--tint, 0) * 2%), var(--room));
  box-shadow: inset 0 -34vh 32vh -32vh color-mix(in srgb, var(--carbon) 20%, transparent);
}

/* THE RESIDENT KEEPS THE MOVEMENT'S BLOCKING. At the door it meets you at
   the top of the stairs; at the forge it leans in close over the work (the
   figure fits its square, so the square grows with it). Everywhere else it
   keeps its seat low in the corner, out of the line of sight. */
.gx-stage[data-move="door"] .gx-mate {
  bottom: auto; top: clamp(80px, 14vh, 140px);
}
.gx-stage[data-move="forge"] .gx-mate {
  width: clamp(110px, 15vw, 180px);
  height: clamp(110px, 15vw, 180px);
}

/* the gathering's stick is a page being set: each chosen line lands as a
   printed row, ruled, indented like the book's own paragraphs */
.gx-gather-stick { display: grid; gap: 0; padding: 0; min-height: 80px; }
.gx-gathered {
  font-family: var(--serif); font-size: var(--t-2); line-height: 1.5;
  padding: 9px 14px; border-bottom: 1px solid var(--rule);
}
@media (prefers-reduced-motion: no-preference) {
  .gx-gathered { animation: gx-rowset 300ms var(--e-std, ease) both; }
  @keyframes gx-rowset { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
}
.gx-gather-tiles { display: grid; gap: 8px; }
.gx-line-tile { text-align: left; font-size: var(--t-1); white-space: normal; line-height: 1.45; }
.gx-line-tile:disabled { opacity: .25; }


/* each question kind announces itself with its own printer's mark, in the
   chapter's ink — seven shapes, seven marks, read at a glance. The mark is
   STRUCK as a small medallion, a printer's device rather than a stray glyph:
   the one piece of furniture that changes with every question, made visible. */
.gx-kind {
  font-style: normal; margin-right: 11px; color: var(--ink, var(--carbon));
  display: inline-grid; place-items: center;
  width: 34px; height: 34px; font-size: 17px; letter-spacing: 0;
  vertical-align: middle;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 10%, var(--galley));
  border: 1.5px solid var(--ink, var(--carbon)); border-radius: 50%;
  box-shadow: 0 1px 2px color-mix(in srgb, var(--ink, var(--carbon)) 22%, transparent), 0 5px 14px -7px color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
}
[data-theme="dark"] .gx-kind {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}
@media (prefers-reduced-motion: no-preference) {
  @keyframes gx-kind-stamp {
    0% { transform: scale(1.25); opacity: 0; }
    55% { transform: scale(.96); opacity: 1; }
    100% { transform: scale(1); opacity: 1; }
  }
  .gx-kind { animation: gx-kind-stamp 240ms var(--e-std, ease) both; }
}

/* ============================================================================
   THE TEN SHEETS. The room had weather, movements, blooms — and the sheet,
   where the reader's eye actually lives, was the same white panel with the
   same thin-bordered rows for all ten kinds of question. The entrances
   differed for a third of a second and then twenty seconds of sameness.
   Every kind is now its own piece of print, so the eye knows the ask before
   a word is read (dual coding, and simple honesty about what each task is):

   MEANING   — a SPECIMEN SHEET: the word alone between double rules;
   WORD      — a TYPE CASE: sunken compartments, the sort raised by the hand;
   GAP       — a GALLEY: the line on its strip, the blank a sunken slot;
   LISTEN    — the DUSK: the paper dims, a horn button, rings of sound;
   ORDER     — the COMPOSING STICK on its brass rail;
   WRITE     — a MANUSCRIPT: no box, one ruled baseline and a nib;
   PAIRS     — the SORTING BOARD with its centre gutter;
   STRESS    — the ACCENT BENCH, the word held up to the light;
   FORM      — the ENDINGS TRAY: sorts with their ink at the head;
   HEARLINE  — the dusk again, wider, for a whole spoken line.
   All layout on data-kind; no script decides anything here.
   ============================================================================ */

/* ---- MEANING: the specimen sheet ---- */
.gx-stage .gx-mid[data-kind="meaning"] .gx-head { text-align: center; padding: 6px 0 2px; }
.gx-stage .gx-mid[data-kind="meaning"] .gx-word {
  position: relative; display: inline-block; padding: 20px 38px;
}
.gx-stage .gx-mid[data-kind="meaning"] .gx-word::before,
.gx-stage .gx-mid[data-kind="meaning"] .gx-word::after {
  content: ''; position: absolute; left: 8px; right: 8px; height: 5px;
  border-top: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 65%, transparent);
  border-bottom: 3px solid color-mix(in srgb, var(--ink, var(--carbon)) 65%, transparent);
}
.gx-stage .gx-mid[data-kind="meaning"] .gx-word::before { top: 0; }
.gx-stage .gx-mid[data-kind="meaning"] .gx-word::after { bottom: 0;
  border-top-width: 3px; border-bottom-width: 1px; }
.gx-stage .gx-mid[data-kind="meaning"] .gx-opts { grid-template-columns: 1fr 1fr; gap: 12px; }
.gx-stage .gx-mid[data-kind="meaning"] .gx-opt {
  text-align: center; font-style: italic;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 8%, var(--galley));
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
  box-shadow: 0 2px 5px color-mix(in srgb, var(--ink, var(--carbon)) 14%, transparent);
}
.gx-stage .gx-mid[data-kind="meaning"] .gx-opt:nth-child(odd) { transform: rotate(-.3deg); }
.gx-stage .gx-mid[data-kind="meaning"] .gx-opt:nth-child(even) { transform: rotate(.3deg); }
@media (max-width: 560px) {
  .gx-stage .gx-mid[data-kind="meaning"] .gx-opts { grid-template-columns: 1fr; }
}

/* ---- WORD: the type case ---- */
.gx-stage .gx-mid[data-kind="word"] .gx-gloss {
  text-align: center; font-style: italic; color: var(--carbon);
  padding-bottom: 14px; position: relative;
}
.gx-stage .gx-mid[data-kind="word"] .gx-gloss::after {
  content: ''; position: absolute; left: 50%; bottom: 0; width: 90px; height: 0;
  transform: translateX(-50%);
  border-bottom: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
}
.gx-stage .gx-mid[data-kind="word"] .gx-opts {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 9%, var(--sheet));
  border: 2px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  padding: 12px; gap: 10px;
  box-shadow: inset 0 2px 8px color-mix(in srgb, var(--carbon) 14%, transparent);
}
.gx-stage .gx-mid[data-kind="word"] .gx-opt {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--sheet));
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 30%, transparent);
  box-shadow: inset 0 1px 4px color-mix(in srgb, var(--carbon) 12%, transparent);
}
.gx-stage .gx-mid[data-kind="word"] .gx-opt:not(:disabled):hover {
  background: var(--galley); transform: translateY(-2px);
  box-shadow: 0 3px 7px color-mix(in srgb, var(--ink, var(--carbon)) 25%, transparent);
}

/* ---- GAP: the galley strip ---- */
.gx-stage .gx-mid[data-kind="gap"] .gx-line {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 6%, var(--galley));
  border-left: 4px solid var(--ink, var(--carbon));
  padding: 20px 22px;
  box-shadow: inset 0 1px 5px color-mix(in srgb, var(--carbon) 8%, transparent);
}
.gx-stage .gx-mid[data-kind="gap"] .gx-blank {
  background: color-mix(in srgb, var(--carbon) 7%, transparent);
  border-bottom-style: dashed; padding: 0 12px;
  box-shadow: inset 0 2px 4px color-mix(in srgb, var(--carbon) 14%, transparent);
}
.gx-stage .gx-mid[data-kind="gap"] .gx-opt {
  border-bottom: 4px solid color-mix(in srgb, var(--ink, var(--carbon)) 60%, var(--carbon));
  min-width: 110px;
}

/* ---- LISTEN and HEARLINE: the dusk. The sheet itself dims — sound asks
   the eye to step back — and the horn sits centred with its rings. ---- */
/* The dusk was mixed from #33271a, an oak brown the palette law retired,
   so the one sheet that dims itself dimmed warm — and, being a literal,
   dimmed by the same warmth in all three rooms. Every other dim in this
   file mixes --carbon into the galley; this one now does too. */
.gx-stage .gx:has(.gx-mid[data-kind="listen"]),
.gx-stage .gx:has(.gx-mid[data-kind="hearline"]) {
  background: color-mix(in srgb, var(--carbon) 9%, var(--galley));
}
.gx-stage .gx-mid[data-kind="listen"] .gx-head,
.gx-stage .gx-mid[data-kind="hearline"] .gx-head {
  display: grid; place-items: center; padding: 10px 0;
}
.gx-stage .gx-mid[data-kind="listen"] .gx-head .gx-say,
.gx-stage .gx-mid[data-kind="hearline"] .gx-head .gx-say {
  position: relative;
  width: 118px; height: 118px; border-radius: 50%;
  display: grid; place-items: center; padding: 0;
  font-size: var(--t-1); letter-spacing: .04em;
  border: 2px solid var(--ink, var(--carbon));
  background: radial-gradient(circle at 50% 38%,
    color-mix(in srgb, var(--ink, var(--carbon)) 6%, var(--galley)),
    color-mix(in srgb, var(--ink, var(--carbon)) 16%, var(--galley)));
  box-shadow: 0 2px 4px color-mix(in srgb, var(--ink, var(--carbon)) 22%, transparent),
    0 10px 24px -10px color-mix(in srgb, var(--ink, var(--carbon)) 50%, transparent);
}
body.motion-full .gx-stage .gx-mid[data-kind="listen"] .gx-head .gx-say::after,
body.motion-full .gx-stage .gx-mid[data-kind="hearline"] .gx-head .gx-say::after {
  content: ''; position: absolute; inset: -2px; border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
  animation: gx-ring 3.2s ease-out infinite;
  pointer-events: none;
}
@keyframes gx-ring {
  0% { transform: scale(1); opacity: .7; }
  70% { transform: scale(1.45); opacity: 0; }
  100% { transform: scale(1.45); opacity: 0; }
}

/* ---- ORDER: the composing stick on its brass rail ---- */
.gx-stage .gx-mid[data-kind="order"] .gx-stick {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--carbon) 7%, var(--galley)),
    color-mix(in srgb, var(--carbon) 13%, var(--galley)));
  border: 1px solid color-mix(in srgb, var(--carbon) 55%, transparent);
  /* The rail was #8a6a3a, a retired tea brown: the same mud at midnight as
     at noon, under a stick that otherwise follows the room. head.html:203
     keeps --brass aliased to --steel for precisely this word, so the rail
     can go on being called brass and still be the house's metadata ink. */
  border-bottom: 6px solid var(--brass);
  box-shadow: inset 0 2px 7px color-mix(in srgb, var(--carbon) 16%, transparent);
}
.gx-stage .gx-mid[data-kind="order"] .gx-tile {
  border-bottom: 4px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
}
.gx-stage .gx-mid[data-kind="order"] .gx-study {
  text-align: center; font-size: clamp(24px, 3.4vw, 32px);
}

/* ---- WRITE: the manuscript. No box — a ruled baseline and the nib. ---- */
.gx-stage .gx-mid[data-kind="write"] .gx-body { position: relative; }
.gx-stage .gx-mid[data-kind="write"] .gx-input {
  background: transparent; border: none; border-radius: 0;
  border-bottom: 2px solid color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent);
  text-align: center; caret-color: var(--ink, var(--carbon));
  padding-left: 48px; padding-right: 48px;
}
.gx-stage .gx-mid[data-kind="write"] .gx-input:focus-visible {
  outline: none; border-bottom-color: var(--ink, var(--carbon));
  box-shadow: 0 2px 0 color-mix(in srgb, var(--ink, var(--carbon)) 30%, transparent);
}
.gx-stage .gx-mid[data-kind="write"] .gx-body::before {
  content: '✒'; position: absolute; left: 10px; top: 12px;
  font-size: 26px; color: color-mix(in srgb, var(--ink, var(--carbon)) 75%, transparent);
  pointer-events: none;
}
.gx-stage .gx-mid[data-kind="write"] .gx-gloss { text-align: center; font-style: italic; }

/* ---- PAIRS: the sorting board with its centre gutter ---- */
.gx-stage .gx-mid[data-kind="pairs"] .gx-pairs { position: relative; }
.gx-stage .gx-mid[data-kind="pairs"] .gx-pairs::before {
  content: ''; position: absolute; left: 50%; top: 4px; bottom: 4px; width: 2px;
  transform: translateX(-50%);
  background: linear-gradient(transparent,
    color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent) 18%,
    color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent) 82%, transparent);
}
.gx-stage .gx-mid[data-kind="pairs"] .gx-pw {
  text-align: center;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--sheet));
  box-shadow: inset 0 1px 4px color-mix(in srgb, var(--carbon) 12%, transparent);
}
.gx-stage .gx-mid[data-kind="pairs"] .gx-pg {
  font-style: italic;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 3%, var(--galley));
}
@media (max-width: 560px) {
  .gx-stage .gx-mid[data-kind="pairs"] .gx-pairs::before { display: none; }
}

/* ---- STRESS: the accent bench ---- */
.gx-stage .gx-mid[data-kind="stress"] .gx-head { text-align: center; }
.gx-stage .gx-mid[data-kind="stress"] .gx-gloss { font-style: italic; }
.gx-stage .gx-mid[data-kind="stress"] .gx-body { text-align: center; }

/* ---- FORM: the endings tray ---- */
.gx-stage .gx-mid[data-kind="form"] .gx-line {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 4%, var(--galley));
  padding: 18px 20px;
  border-left: 4px solid color-mix(in srgb, var(--ink, var(--carbon)) 50%, transparent);
}
.gx-stage .gx-mid[data-kind="form"] .gx-opts {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;
}
.gx-stage .gx-mid[data-kind="form"] .gx-opt {
  min-width: 130px; flex: 0 1 auto;
  border-top: 4px solid var(--ink, var(--carbon));
}

/* ============================================================================
   THE MOVEMENTS GET THEIR TEMPERATURE. The door was pale, the forge deep,
   the gathering ruled — correct, but all in the same ink. The forge now
   carries an EMBER in its band (the smithy's one red allowed: heat is the
   fact being told) and its paper toasts; the gathering carries a GOLD
   thread (a page being finished is a small celebration) and its paper
   creams. The chapter's pigment still rules both.
   ============================================================================ */
.gx-stage[data-move="forge"]::before {
  background: linear-gradient(90deg, var(--ink, var(--carbon)),
    color-mix(in srgb, var(--signal) 55%, var(--ink, var(--carbon))));
}
.gx-stage[data-move="forge"] .gx {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--galley));
}
.gx-stage[data-move="gather"]::before {
  background: linear-gradient(90deg, var(--ink, var(--carbon)),
    color-mix(in srgb, var(--signal) 50%, var(--ink, var(--carbon))));
}
.gx-stage[data-move="gather"] .gx {
  background: color-mix(in srgb, var(--signal) 3%, var(--galley));
}

/* ============================================================================
   THE CHAPTER'S PRESENCE, LOUD ENOUGH TO EXIST. The ghosted ornament and the
   margin bloom were tuned so shy they read as rendering artifacts. They are
   the room's memory of the book; they may be seen.
   ============================================================================ */
.gx-stage::after { opacity: .085; }
[data-theme="dark"] .gx-stage::after { opacity: .11; }
.gx-bloom i { opacity: .34; }

/* ============================================================================
   THE OPENER IS A PAGE, NOT A FORM. The scene's first line gets the drop
   cap a printed chapter would give it.
   ============================================================================ */
.gx-stage .gx-mid[data-kind="gram"] .gx-study {
  font-size: clamp(24px, 3.6vw, 34px); line-height: 1.55;
}
.gx-stage .gx-mid[data-kind="gram"] .gx-study::first-letter {
  font-size: 2.4em; float: left; line-height: .82;
  padding: 4px 12px 0 0; font-weight: 600;
  color: var(--ink, var(--carbon));
}

/* ============================================================================
   THE RESIDENT KEEPS OUT OF THE WORK. On anything narrower than a wide
   desk, the door-movement's "top of the stairs" blocking lands the medallion
   on the question itself — company become obstruction. Narrow rooms seat it
   low and small, always.
   ============================================================================ */
@media (max-width: 1100px) {
  .gx-stage[data-move="door"] .gx-mate { top: auto; bottom: clamp(12px, 3vh, 34px); }
}
@media (max-width: 700px) {
  .gx-mate { width: 72px; height: 72px; right: 10px; bottom: 10px; }
  .gx-mate-line { max-width: 170px; font-size: .74rem; }
}

/* THE SHARE ROW under the patent of rank. The picture button leads, because
   the picture is what travels; the named services are the desktop path, where
   navigator.share has no file support and a download is a dead end. */
.jp-share-row { display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin-top: 14px; }
/* --t--1 is not a step of the scale (it runs --t-00 … --t-7), so this
   silently inherited its size instead of setting one */
.jp-share-row .jp-s { font-size: var(--type-control); line-height:1.5; min-height:44px; white-space:normal; padding: 8px 12px;
  text-decoration: none; }


/* ============================================================
   THE UPRIGHT CYRILLIC LAW, GENERALIZED (the owner's ruling, given twice:
   first on the teach sheet, then on the check notes and the epigraph).
   Italic Cyrillic is halfway to handwriting — т reads as m, и as u, д as
   something else again — and the surfaces that TEACH are exactly the ones
   read by people who cannot cope with that yet. Every element that prints
   the room's own language stands upright; italics survive only on
   pure-English captions (.gx-en, .gx-print-en, figcaption, the bindery).
   ============================================================ */
.gx-gr-note, .gx-arc-epi, .gx-wis, .margin-note,
.gx-mate-line, .gx-mate .gx-slip, .gx-mate-note,
.gx-line, .gx-scene-line, .gx-verdict, .gx-fl-line,
.gx-hl-ru, .gx-tp-sorts i { font-style: normal; }


/* the plate keeps ONE primary door; the reference is a quiet footnote */
.gx-plate .btnrow { margin-top: 10px; }
.gx-plate #gx-teach-art {
  background: transparent; border: 0; box-shadow: none; padding: 4px 0;
  font-family: var(--sans); font-size: .8rem; letter-spacing: .03em;
  text-decoration: underline; text-underline-offset: 3px; opacity: .75;
}
.gx-plate #gx-teach-art:hover { opacity: 1; background: transparent; }


/* the ask row breathes: the meta note («nothing here is graded», «it kept
   its appointment») steps down to a quiet second line instead of a boxed
   chip elbowing the question — the owner's «overwhelming» verdict */
.gx-ask .gx-again {
  display: block; margin: 4px 0 0; padding: 0;
  background: transparent; border: 0; box-shadow: none;
  font-size: .72rem; letter-spacing: .04em; opacity: .62;
}


/* ============================================================
   THE SHAPES' OWN STAGES (the adventure law): each board in its own
   material. Letterpress sorts for the word-family; catalog cards with
   corner ticks for meaning; a sunken tray for the gap; aligned pair
   columns joined by drawn ink threads; the listening seal that rings.
   ============================================================ */

/* letterpress sorts — raised type-blocks that press when struck */
.gx-stage .gx-mid[data-kind="word"] .gx-opts .gx-opt,
.gx-stage .gx-mid[data-kind="listen"] .gx-opts .gx-opt,
.gx-stage .gx-mid[data-kind="stress"] .gx-opts .gx-opt,
.gx-stage .gx-mid[data-kind="form"] .gx-opts .gx-opt {
  border: 2px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press, rgba(0,0,0,.22)),
    inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);
  background: var(--galley);
  transition: transform .1s ease, box-shadow .1s ease, background .12s linear;
}
.gx-stage .gx-mid[data-kind="word"] .gx-opts .gx-opt:hover:not(:disabled),
.gx-stage .gx-mid[data-kind="listen"] .gx-opts .gx-opt:hover:not(:disabled),
.gx-stage .gx-mid[data-kind="stress"] .gx-opts .gx-opt:hover:not(:disabled),
.gx-stage .gx-mid[data-kind="form"] .gx-opts .gx-opt:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--press, rgba(0,0,0,.22)),
    inset 0 1px 0 color-mix(in srgb, #fff 45%, transparent);
}
.gx-stage .gx-mid[data-kind="word"] .gx-opts .gx-opt:active,
.gx-stage .gx-mid[data-kind="listen"] .gx-opts .gx-opt:active,
.gx-stage .gx-mid[data-kind="stress"] .gx-opts .gx-opt:active,
.gx-stage .gx-mid[data-kind="form"] .gx-opts .gx-opt:active {
  transform: translateY(2px);
  box-shadow: 0 1px 0 var(--press, rgba(0,0,0,.22));
}

/* meaning — the struck tick. (The corner brackets this block once drew
   fought the house nick for ::before and rendered as broken chips at 375;
   the nick — 97-lessons — is the card's one ornament, and the tick lands
   on ::after, which nothing else claims.) */
.gx-stage .gx-mid[data-kind="meaning"] .gx-opts .gx-opt { position: relative; padding: 14px 18px; }
.gx-cat-tick::after {
  content: '✓'; position: absolute; top: 2px; right: 7px;
  font-size: 1rem; color: var(--g-gold, #c9a227);
}
@media (prefers-reduced-motion: no-preference) {
  .gx-cat-tick::after { animation: cs-slip .3s var(--spring-slip, ease-out) both; }
}

/* the gap — a sunken tray the word settles into */
.gx-stage .gx-mid[data-kind="gap"] .gx-blank {
  display: inline-block; min-width: 5.5ch; text-align: center;
  padding: 0 8px; margin: 0 2px;
  background: color-mix(in srgb, var(--carbon) 8%, transparent);
  box-shadow: inset 0 2px 3px color-mix(in srgb, var(--carbon) 25%, transparent);
  border-bottom: 2px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
  color: color-mix(in srgb, var(--carbon) 45%, transparent);
}
.gx-stage .gx-mid[data-kind="gap"] .gx-blank.gx-blank-set {
  color: var(--ink, var(--carbon)); font-weight: 600;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 8%, transparent);
  box-shadow: none; letter-spacing: .01em;
}
.gx-fly {
  font-family: var(--serif); font-size: 1.1rem; color: var(--ink, var(--carbon));
  pointer-events: none; font-weight: 600;
}

/* pairs — aligned rows, and the drawn thread with its wax point */
.gx-stage .gx-pairs { position: relative; }
.gx-stage .gx-pairs .gx-pcol { display: grid; gap: 10px; grid-auto-rows: minmax(56px, auto); align-items: stretch; }
.gx-thread {
  position: absolute; inset: 0; width: 100%; height: 100%;
  pointer-events: none; overflow: visible;
}
.gx-thread path {
  fill: none; stroke: color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent);
  stroke-width: 2;
}
.gx-thread .gx-thread-dot { fill: var(--g-gold, #c9a227); stroke: var(--carbon); stroke-width: 1; }

/* the listening seal was struck long ago — the pale dome with its ambient
   ring, §1366 above. This block only finishes it: the gramophone glyph in
   the room's own ink (it arrived galley-white and vanished on the dome),
   and a RING ON PRESS — two quick waves off the rim while it speaks.
   ::before, because the ambient ring owns ::after. */
.gx-stage .gx-mid[data-kind="listen"] .gx-listen-big { color: var(--ink, var(--carbon)); }
.gx-stage .gx-mid[data-kind="listen"] .gx-listen-big svg { width: 40px; height: 40px; }
@media (prefers-reduced-motion: no-preference) {
  .gx-listen-big.gx-ringing::before {
    content: ''; position: absolute; inset: -8px; border-radius: 50%;
    border: 2.5px solid color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent);
    animation: gx-ring-press .95s ease-out 2; pointer-events: none;
  }
  @keyframes gx-ring-press {
    from { transform: scale(.92); opacity: .95; }
    to { transform: scale(1.5); opacity: 0; }
  }
}


/* THE ASK'S OWN ALPHABET (the owner's third wrong-letters find): the ask
   speaks English in the poster caps; a run of the room's own language
   inside it — a Cyrillic word, a «quoted» form — steps out of that face
   into the book serif, natural case, upright, a shade larger. The room's
   language is never costumed. */
.gx-stage .gx-ask .gx-r,
.gx-stage .gx-verdict .gx-r {
  font-family: var(--serif);
  text-transform: none;
  letter-spacing: normal;
  font-style: normal;
  font-weight: 700;
  font-size: 1.14em;
  line-height: 1;
}

/* THE QUESTION'S OWN STAGE — after the tale and its note, the one
   question opens on its own ground: air above, a hairline, an ink tab.
   Never crammed under the paragraph it follows. */
.gx-stage .gx-q-stage {
  margin-top: 32px;
  padding-top: 20px;
  border-top: 1px solid color-mix(in srgb, var(--carbon) 22%, transparent);
  position: relative;
}
.gx-stage .gx-q-stage::before {
  content: ''; position: absolute; top: -2px; left: 0;
  width: 46px; height: 3px; background: var(--ink, var(--carbon));
}
.gx-stage .gx-q-stage .gx-opts { margin-top: 14px; }


/* ============================================================
   THE GAME MADE LEGIBLE (gamification charter v2 — no hearts, nothing
   lost; the chain, the seal at the end of the rail, the day's commission,
   the gilded deal). Dressed in the print house throughout.
   ============================================================ */

/* the run chip becomes the CHAIN — gold sorts, and it pops as it grows */
.gx-stage .gx-runn {
  font-family: var(--bill); font-weight: 800; font-size: var(--t-00);
  letter-spacing: .08em; text-transform: uppercase;
  background: var(--g-gold, #c9a227); color: var(--carbon);
  border: 1.5px solid var(--carbon); border-radius: 999px;
  padding: 2px 10px; box-shadow: 0 2px 0 var(--press, rgba(0,0,0,.25));
  white-space: nowrap;
}
.gx-stage .gx-runn.gx-runn-off { visibility: hidden; }
@media (prefers-reduced-motion: no-preference) {
  .gx-runn-pop { animation: gx-runn-pop .42s var(--spring-slip, ease-out); }
  @keyframes gx-runn-pop {
    0% { transform: scale(1); }
    45% { transform: scale(1.22) rotate(-2deg); }
    100% { transform: scale(1); }
  }
}

/* the rail ends at a seal: every sitting visibly runs TOWARD the wax */
.gx-stage .gx-rail .gx-r-seal {
  flex: 0 0 auto; width: 18px; height: 18px; border-radius: 50%;
  background: transparent; border: 1.5px solid color-mix(in srgb, var(--carbon) 45%, transparent);
  color: color-mix(in srgb, var(--carbon) 40%, transparent);
  display: grid; place-items: center; font-size: 11px; line-height: 1;
  margin-left: 4px; align-self: center;
  transition: color .3s linear, border-color .3s linear, background .3s linear,
    box-shadow .3s linear;
}
.gx-stage .gx-rail.gx-rail-near .gx-r-seal {
  border-color: var(--g-gold, #c9a227); color: var(--carbon);
  background: color-mix(in srgb, var(--g-gold, #c9a227) 30%, transparent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--g-gold, #c9a227) 25%, transparent);
}

/* the gilded deal — a chain of four earns the next card its gold edging */
.gx-stage .gx-mid[data-rare="gold"] {
  outline: 2px solid var(--g-gold, #c9a227);
  outline-offset: -2px;
  box-shadow: inset 0 0 0 5px color-mix(in srgb, var(--g-gold, #c9a227) 22%, transparent);
  position: relative;
}
.gx-stage .gx-mid[data-rare="gold"]::after {
  content: '❋'; position: absolute; top: 6px; right: 10px;
  color: var(--g-gold, #c9a227); font-size: 1.1rem; pointer-events: none;
}

/* THE DAY'S COMMISSION — a printed job card above the course */
.gx-comm {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 16px;
  border: 2px solid var(--carbon); background: var(--galley, #f4efe4);
  box-shadow: 0 3px 0 var(--press, rgba(0,0,0,.22));
  padding: 10px 14px; margin: 0 0 18px; position: relative;
}
.gx-comm-ti {
  font-family: var(--bill); font-weight: 900; letter-spacing: .1em;
  text-transform: uppercase; font-size: var(--t-00); color: var(--carbon);
  border-right: 1.5px solid color-mix(in srgb, var(--carbon) 30%, transparent);
  padding-right: 14px;
}
.gx-comm-row {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--sans); font-size: var(--t-0);
  color: color-mix(in srgb, var(--carbon) 80%, transparent);
}
.gx-comm-row b { font-variant-numeric: tabular-nums; color: var(--carbon); }
.gx-comm-tick {
  width: 15px; height: 15px; border: 1.5px solid var(--carbon);
  border-radius: 2px; display: inline-grid; place-items: center;
  font-style: normal; font-size: 11px; line-height: 1; color: transparent;
  background: transparent; transition: background .25s linear, color .25s linear;
}
.gx-comm-done .gx-comm-tick { background: var(--carbon); color: var(--galley, #f4efe4); }
.gx-comm-done .gx-comm-tick::before { content: '✓'; }
.gx-comm-seal {
  margin-left: auto; width: 26px; height: 26px; border-radius: 50%;
  display: grid; place-items: center; font-style: normal; font-size: 13px;
  border: 1.5px dashed color-mix(in srgb, var(--carbon) 40%, transparent);
  color: color-mix(in srgb, var(--carbon) 35%, transparent);
  transition: all .3s linear;
}
.gx-comm-sealed .gx-comm-seal {
  border-style: solid; border-color: var(--carbon);
  background: var(--g-gold, #c9a227); color: var(--carbon);
  box-shadow: 0 2px 0 var(--press, rgba(0,0,0,.25));
}
@media (prefers-reduced-motion: no-preference) {
  .gx-comm-just .gx-comm-seal { animation: gx-comm-stamp .5s var(--spring-slip, ease-out); }
  @keyframes gx-comm-stamp {
    0% { transform: scale(2.2); opacity: 0; }
    60% { transform: scale(.92); opacity: 1; }
    100% { transform: scale(1); }
  }
}

/* ============================================================
   THE HEADER IN TWO TIERS AT PHONE WIDTH (the 168px stack at 375):
   name on the first line, cut honestly with an ellipsis; the scene and
   the count on the second; Leave keeps its corner. The stamp disc sits
   out — at this width it was an empty white circle doing nothing.
   ============================================================ */
@media (max-width: 600px) {
  .gx-stage .gx-top {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: 'ti quit' 'sub sub';
    row-gap: 4px; align-items: center;
  }
  .gx-stage .gx-top .gx-topic {
    grid-area: ti; display: block; min-width: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .gx-stage .gx-top .gx-topic i { display: none; }
  .gx-stage .gx-top .gx-topic em {
    display: block; white-space: nowrap; overflow: hidden;
    text-overflow: ellipsis; letter-spacing: .06em;
  }
  .gx-stage .gx-top .gx-quit { grid-area: quit; justify-self: end; }
  .gx-stage .gx-top .gx-runn { grid-area: sub; justify-self: start; }
  .gx-stage .gx-top .gx-count { grid-area: sub; justify-self: end; }
}


/* ===== 27-route.css ===== */
/* ============================================================================
   THE ROAD — the shelf, walked.

   The route used to be six abstract nodes (A1…C1) with the whole level's
   contents inside each. It is now the fourteen books themselves, easiest
   first, each a spine on the page; a book opens into its chapters, a chapter
   into its lessons. The CEFR levels still appear — as milestones BETWEEN the
   books, where they actually fall on the road, not as its structure.

   Each book carries one ink from the drawer (set as --ink by the renderer),
   so the road reads as bands of colour the way a shelf reads as spines.
   ============================================================================ */

.rd { list-style: none; margin: 0; padding: 0; position: relative; display: grid; gap: 12px; }

/* The line itself. Drawn behind the spines and stopping at the last one, so it
   reads as a road with an end rather than an axis running off the page. */
.rd::before {
  content: ''; position: absolute; left: 27px; top: 30px; bottom: 34px;
  width: 2px; background: var(--rule-room);
}

.rd-book, .rd-alpha, .rd-beyond, .rd-mark { position: relative; }
/* EVERY BOOK ROW IS A SHEET, and the sheet wears the book's ink: a pigment
   band at its left edge, a whisper of the same pigment in its paper, and a
   shadow tinted by it. Fifteen rows, fifteen colours — the road finally as
   bright as the shelf it walks. The milestones stay OPEN on the road between
   the sheets, which gives the page its rhythm of card · card · waypoint. */
.rd-book, .rd-alpha, .rd-cy {
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--ink, var(--carbon)) 6%, var(--galley)),
    var(--galley) 58%);
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 28%, var(--rule));
  border-left: 6px solid var(--ink, var(--carbon));
  box-shadow: 0 1px 3px color-mix(in srgb, var(--ink, var(--carbon)) 12%, transparent),
              0 14px 28px -18px color-mix(in srgb, var(--ink, var(--carbon)) 48%, transparent);
  transition: transform 180ms var(--e-std, ease), box-shadow 180ms var(--e-std, ease);
}
.rd-book:hover, .rd-alpha:hover, .rd-cy:hover {
  transform: translateY(-3px);
  box-shadow: 0 3px 6px color-mix(in srgb, var(--ink, var(--carbon)) 16%, transparent),
              0 22px 40px -18px color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
}
/* an open book lies flat and stays put — the lift belongs to closed covers */
.rd-book.rd-open:hover, .rd-alpha.rd-open:hover, .rd-cy.rd-open:hover { transform: none; }
.rd-hit {
  display: grid; grid-template-columns: 64px 1fr; gap: 0 18px;
  width: 100%; text-align: left; align-items: start;
  background: none; border: 0; border-radius: 0; padding: 16px;
  font-family: inherit; color: inherit; cursor: pointer;
}
.rd-hit:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

/* --- the spine. A STANDING VOLUME now, not a chip: taller than wide, its
   cloth in the book's washed pigment, a solid spine-band at the left, and a
   block of page-edges at the right — and it FILLS with full ink as its
   chapters close, so the road itself is the progress and there is no second
   bar. On hover the volume is taken off the shelf: it lifts, tips a degree,
   and its shadow arrives — a book answering the hand. --- */
.rd-vol {
  position: relative; width: 58px; height: 80px; overflow: hidden;
  background: linear-gradient(105deg,
    color-mix(in srgb, var(--ink, var(--carbon)) 42%, var(--galley)),
    color-mix(in srgb, var(--ink, var(--carbon)) 26%, var(--galley)) 45%,
    color-mix(in srgb, var(--ink, var(--carbon)) 34%, var(--galley)));
  border: 2px solid var(--ink, var(--carbon));
  box-shadow: inset 6px 0 0 var(--ink, var(--carbon));
  display: grid; place-items: center;
  /* the volume STANDS on the shelf at a reader's angle — real depth, not a
     chip. The hand takes it: it straightens and lifts (hover below). */
  transform: perspective(340px) rotateY(-10deg);
  transform-origin: left center;
  transition: transform 220ms var(--e-std, ease), box-shadow 220ms var(--e-std, ease);
}
/* the page block: the paper itself showing at the fore-edge, above the fill */
.rd-vol::after {
  content: ''; position: absolute; top: 3px; bottom: 3px; right: 3px; width: 5px;
  background: repeating-linear-gradient(to bottom,
    var(--galley) 0 3px, color-mix(in srgb, var(--carbon) 18%, var(--galley)) 3px 4px);
  z-index: 1;
}
.rd-vol i {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: var(--ink, var(--carbon)); transition: height 400ms var(--e-std, ease);
}
.rd-vol b {
  position: relative; z-index: 1; font-family: var(--bill); font-weight: 900;
  font-size: var(--t-0); letter-spacing: .02em; color: var(--carbon);
  font-variant-numeric: tabular-nums;
  mix-blend-mode: difference; filter: invert(1);
}
.rd-hit:hover .rd-vol {
  /* the scroll-entrance animation pins transform with its fill; the hand
     wins — the animation yields for the length of the hover */
  animation: none;
  transform: perspective(340px) rotateY(-2deg) translateY(-4px);
  box-shadow: inset 6px 0 0 var(--ink, var(--carbon)),
    0 2px 4px color-mix(in srgb, var(--ink, var(--carbon)) 24%, transparent), 0 12px 26px -12px color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
}
.rd-vol-a { border-color: var(--carbon); height: 54px; }
.rd-vol-a::after { display: none; }
.rd-vol-a b { mix-blend-mode: normal; filter: none; font-size: var(--t-3); }

/* THE BOOK'S FACE IN THE MARGIN: its own ornament, ghosted large in its own
   pigment at the row's right edge. Fourteen rows, fourteen marks, fourteen
   pigments — the shelf's variety finally ON the road. It leans in on hover. */
.rd-book { overflow: hidden; }
.rd-book::after {
  content: attr(data-orn); position: absolute; right: 6px; top: 50%;
  transform: translateY(-50%) rotate(-8deg);
  font-size: 72px; line-height: 1; color: var(--ink, var(--carbon));
  opacity: .08; pointer-events: none;
  transition: opacity 240ms linear, transform 240ms var(--e-std, ease);
}
.rd-book:hover::after { opacity: .16; transform: translateY(-50%) rotate(-3deg); }
.rd-book.rd-open::after { opacity: 0; }
/* the title takes the book's ink when the hand is over it — the row answering */
.rd-hit:hover .rd-ti { color: var(--ink, var(--carbon)); }
[data-theme="dark"] .rd-hit:hover .rd-ti {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}
.rd-ti { transition: color 160ms linear; }

/* Where you are. The only сурик on the page, because it is the only thing
   here that is about right now. */
.rd-here .rd-vol { box-shadow: 0 0 0 3px var(--room), 0 0 0 5px var(--signal); }
.rd-done .rd-vol { background: var(--ink, var(--carbon)); }
.rd-done .rd-vol b { color: var(--galley); mix-blend-mode: normal; filter: none; }
.rd-ahead .rd-vol { border-color: var(--rule-room); }
.rd-ahead .rd-body { opacity: .62; }
.rd-beyond { display: grid; grid-template-columns: 64px 1fr; gap: 0 18px; padding: 14px 16px; opacity: .58; }

/* --- what the spine says ------------------------------------------------- */
.rd-body { display: grid; gap: 3px; padding-top: 2px; min-width: 0; }
.rd-ti {
  font-family: var(--serif); font-size: var(--t-3); font-weight: 700;
  color: var(--carbon);
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
}
.rd-ti em {
  font-style: normal; font-family: var(--sans); font-weight: 400;
  letter-spacing: .04em; font-size: var(--t-00); color: var(--steel);
}
.rd-can { font-family: var(--sans); font-size: var(--t-0); color: var(--steel); font-variant-numeric: tabular-nums; }
.rd-opens { font-family: var(--serif); font-style: italic; font-size: var(--t-1); color: var(--carbon); }
/* the pull: the current book's next chapter, named, in the book's own ink */
.rd-next { font-size: var(--t-0); color: var(--ink, var(--carbon)); font-variant-numeric: tabular-nums; }
.rd-next b { font-family: var(--serif); }
[data-theme="dark"] .rd-next {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}

/* --- the milestones. A level is a fact about a word count, passed on the way;
   it is drawn lighter than the books because it is ABOUT the road, not on it. */
.rd-mark {
  display: grid; grid-template-columns: 64px 1fr; gap: 0 18px;
  padding: 10px 16px; align-items: start;
}
/* a WAX SEAL, not a dashed placeholder: the milestone is one of the few
   saturated things on the road, because passing it is one of the few
   ceremonies. Vermilion wax, pressed letters, a soft red halo. */
.rd-m-dot {
  width: 42px; height: 42px; margin: 0 auto; position: relative;
  background: radial-gradient(circle at 35% 30%,
    color-mix(in srgb, var(--signal) 78%, #fff),
    var(--signal) 55%,
    color-mix(in srgb, var(--signal) 72%, #000));
  border: none; border-radius: 50%;
  box-shadow: 0 0 0 3px var(--room),
    0 0 0 4px color-mix(in srgb, var(--signal) 45%, transparent),
    0 3px 10px color-mix(in srgb, var(--signal) 45%, transparent);
  display: grid; place-items: center;
  font-family: var(--bill); font-weight: 900; font-size: var(--t-00);
  color: #fff; letter-spacing: .04em;
  text-shadow: 0 1px 2px rgba(0, 0, 0, .35);
}
.rd-m-body { font-size: var(--t-0); color: var(--steel); display: grid; gap: 2px; padding-top: 5px; }
.rd-m-body b { color: var(--carbon); font-variant-numeric: tabular-nums; }
.rd-m-can { font-family: var(--serif); font-style: italic; }
.rd-m-buy { font-variant-numeric: tabular-nums; }

/* --- what a book opens into ---------------------------------------------- */
.rd-open-box {
  margin: 0 16px 20px 16px; padding-top: 4px;
  animation: rd-unfold 320ms var(--e-std, ease) both;
}
@keyframes rd-unfold { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .rd-open-box { animation: none; } }

/* THE RESIDENT AT THE DOOR. Every book has a presiding figure; it stands at
   the head of the open book with its handover line beside it. One figure on
   the whole page, morphing from book to book — never a rack of them. */
.rd-who {
  display: grid; grid-template-columns: 72px 1fr; gap: 14px; align-items: center;
  margin-bottom: 14px;
}
/* THE ENGRAVED VIGNETTE. The figure stands inside a printed medallion — a
   ring of the book's ink on the book's paper — so the resident reads as an
   illustration OF this book rather than a sticker beside it. The same frame
   the lesson room gives it (26-games.css), because it is the same somebody. */
.rd-who-fig {
  width: 72px; height: 72px; position: relative;
  display: grid; place-items: center; padding: 6px;
}
.rd-who-fig::before {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  background: radial-gradient(circle at 50% 40%,
    color-mix(in srgb, var(--ink, var(--carbon)) 8%, var(--galley)),
    color-mix(in srgb, var(--ink, var(--carbon)) 22%, var(--galley)));
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 60%, var(--carbon));
  box-shadow: inset 0 0 0 3px var(--galley),
    inset 0 0 0 4px color-mix(in srgb, var(--ink, var(--carbon)) 40%, transparent);
}
.rd-who-fig svg { position: relative; filter: saturate(.8) sepia(.08) contrast(1.03); }
.rd-who-say {
  font-family: var(--serif); font-style: italic; font-size: var(--t-2);
  color: var(--carbon);
}

/* --- a chapter: A BAND OF PIGMENT. The ink drawer exists so colour comes
   from the subject, and the chapter head is where the whole pigment sits on
   the page — the pale-card version was measured against it and looked like a
   form. An unopened band runs at 76% strength and takes full ink on hover and
   when open, the same recipe the old book-heads used. --- */
.rd-chapters { display: grid; gap: 6px; }
.rd-ch { background: var(--sheet); }
/* the band deepens with --tint: each chapter of a book a further room of the
   same house, so a book's chapter list reads as a walk inward, not a repeat */
.rd-ch-head {
  display: grid; grid-template-columns: 34px 1fr auto; gap: 0 12px;
  align-items: center; width: 100%; text-align: left;
  background: color-mix(in srgb, var(--ink, var(--carbon))
    calc(74% + var(--tint, 0) * 4%), var(--room));
  color: #fff; border: 0; border-radius: 0; padding: 12px 16px 15px;
  font-family: inherit; cursor: pointer; position: relative;
  box-shadow: inset 5px 0 0 rgba(0, 0, 0, .28);
  transition: background 140ms linear;
  overflow: hidden;
}
/* the dingbat ghost is retired: the band's leading mark is now the book's
   own stamp (see .rd-orn .stamp in 66-rooms.css), and a second stock glyph
   floating behind it was exactly the incoherence the owner named */
.rd-ch-head::before {
  content: none; position: absolute; right: 84px; top: 50%;
  transform: translateY(-50%) rotate(-8deg);
  font-size: 64px; line-height: 1; color: #fff; opacity: .1;
  pointer-events: none;
  transition: opacity 200ms linear, transform 260ms var(--e-std, ease);
}
.rd-ch-head:hover::before { opacity: .2; transform: translateY(-50%) rotate(-2deg) scale(1.06); }
.rd-ch-head:hover, .rd-ch.rd-open .rd-ch-head { background: var(--ink, var(--carbon)); }
.rd-ch-head:focus-visible { outline: 2px solid var(--signal); outline-offset: -3px; }
.rd-orn { font-size: var(--t-3); color: #fff; opacity: .92; text-align: center; }
.rd-ch-mid { display: grid; gap: 1px; min-width: 0; }
.rd-ch-ti {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .07em;
  font-weight: 700; font-size: var(--t-2);
}
.rd-ch-ru { font-family: var(--serif); font-style: italic; font-size: var(--t-00); opacity: .8; }
.rd-ch-state {
  font-family: var(--sans); font-variant-numeric: tabular-nums;
  font-size: var(--t-00); letter-spacing: .12em; opacity: .85;
}
/* A FINISHED CHAPTER IS ILLUMINATED: the ornament takes gold leaf. That is
   what gold is for — celebration and illumination, never semantics — and a
   scene made readable is both. Nothing else on the band changes. */
.rd-ch-done .rd-orn { color: var(--gold-leaf, #b8860b); opacity: 1; }
.rd-ch-done .rd-ch-state { opacity: 1; }

/* the fill: the chapter's own page-edge inking in along the band's foot.
   Paper-white on the pigment — never gold, which would be gold meaning
   "progress", and gold does not mean things in this product. */
.rd-ch-fill {
  position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
  background: rgba(0, 0, 0, .25);
}
.rd-ch-fill i {
  display: block; height: 100%; background: rgba(255, 255, 255, .88);
  transition: width 400ms var(--e-std, ease);
}

.rd-ch-body { padding: 4px 14px 14px; display: grid; gap: 12px; }
/* THE PROMISE, AT THE SIZE OF A PROMISE. «111 words, 14 lessons — and then
   all 53 lines of this scene read cold, with nothing in the way» is the best
   sentence on this screen and the whole argument for staying on it; it was
   set at 15px, the size of a caption. It leads now. */
.rd-ch-buy {
  margin: 0; font-family: var(--serif);
  font-size: clamp(16px, .98rem + .3vw, 20px); line-height: 1.5;
  color: var(--carbon); text-wrap: pretty;
}
.rd-ch-buy b { font-variant-numeric: tabular-nums; }

/* ================= THE SCENE, SET AS A GALLEY =================
   This list was a spreadsheet: every row a bordered box of the same weight,
   the Russian at 15px and everything else at 11px — which put Turgenev at
   the smallest size on a page about Turgenev. The line a reader came for was
   the least of what they saw.

   It is a galley proof now. The lines sit on paper under hairline rules, the
   way a compositor's pull looks before it is cut: no boxes, no cells, one
   column of type with its numbers in the margin and its costs in the other
   margin. Nothing was added to the page — the weight was moved to the thing
   that deserved it. */
.rd-lessons {
  display: grid; gap: 0;
  border-top: 1px solid color-mix(in srgb, var(--rule) 70%, transparent);
}
.rd-lesson {
  display: grid; grid-template-columns: 30px 1fr auto; gap: 2px 14px;
  align-items: baseline; width: 100%; text-align: left;
  background: transparent; border: 0; border-radius: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--rule) 70%, transparent);
  padding: 13px 14px 14px; font-family: inherit; color: inherit; cursor: pointer;
  /* ink seeping into paper — never a border snapping on */
  transition: background-color 180ms cubic-bezier(.22, 1, .36, 1);
}
/* the sheet takes the ink where the hand rests. A hairline of the chapter's
   own pigment at the left edge says WHICH book this line belongs to without
   printing its name again. */
.rd-lesson:hover {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 4%, var(--galley));
  box-shadow: inset 2px 0 0 color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
}
.rd-lesson:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
/* THE LINE NUMBER, IN THE MARGIN WHERE A PRINTED EDITION PUTS IT — right
   ranged against the text block, tabular so the column stays a column, and
   quiet: it numbers the work, it is not the work. */
.rd-l-no {
  font-family: var(--bill); font-weight: 900; font-size: var(--t-0);
  color: color-mix(in srgb, var(--steel) 70%, transparent);
  font-variant-numeric: tabular-nums; text-align: right;
  align-self: start; padding-top: .18em;
}
/* YOU ARE HERE — the compositor's manicule at the one row that is next.
   The row itself takes the chapter's ink at its left edge and a touch of
   its tint; the hand breathes, the way a waiting thing does. */
/* THE ONE LIVING ROW. On a sheet of forty lines exactly one is the reader's
   next, and it should be findable from the doorway — this is the row that
   answers «where was I», which is the question that decides whether an
   evening happens at all. It stands on the chapter's own ink, lifts off the
   sheet, and breathes; every other row stays flat, because a page where
   everything is emphasised has emphasised nothing. */
.rd-l-here {
  border-left: 3px solid var(--ink, var(--signal));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 9%, var(--galley));
  grid-template-columns: 44px 1fr auto;
  padding-top: 16px; padding-bottom: 17px;
  box-shadow: 0 1px 0 color-mix(in srgb, var(--ink, var(--carbon)) 18%, transparent);
}
.rd-l-here:hover {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 13%, var(--galley));
}
.rd-l-here .rd-l-no {
  color: var(--ink, var(--carbon)); white-space: nowrap;
  font-size: var(--t-1);
}
.rd-l-here .rd-l-ru { font-size: clamp(17.5px, 1.06rem + .34vw, 22.5px); }
.rd-l-here .rd-l-state { color: var(--ink, var(--carbon)); }
.rd-hand {
  font-style: normal; display: inline-block; margin-right: 4px;
  color: var(--ink, var(--signal)); font-size: 1.35em;
}
body.motion-full .rd-hand { animation: rd-hand-beckon 2.2s ease-in-out infinite; }
@keyframes rd-hand-beckon {
  0%, 100% { transform: translateX(0); }
  50% { transform: translateX(3px); }
}
/* chapter progress draws itself when a book opens — goal gradient, felt */
body.motion-full .rd-open .rd-ch-fill i { animation: rd-fill-grow .9s cubic-bezier(.22, 1, .36, 1) .2s backwards; }
@keyframes rd-fill-grow { from { width: 0; } }
.rd-l-mid { display: grid; gap: 3px; min-width: 0; }
/* THE LITERATURE IS THE LARGEST THING ON THE PAGE, which took a redesign to
   arrive at and should never have needed one. It grows with the window
   because a line of Turgenev on a desk monitor and the same line on a phone
   are not the same reading, and 15px served neither. */
.rd-l-ru {
  font-family: var(--serif);
  font-size: clamp(16.5px, 1.01rem + .32vw, 21px);
  line-height: 1.5; color: var(--carbon);
  text-wrap: pretty;
}
.rd-l-en {
  font-size: var(--t-0); line-height: 1.45;
  color: color-mix(in srgb, var(--steel) 88%, transparent);
  font-style: italic; text-wrap: pretty;
}
/* THE PRICE, IN THE OTHER MARGIN. «8 words» is the most motivating fact on
   the row — it is the whole distance between not having this line and having
   it — and it was set in the same grey whisper as everything else. It keeps
   its modesty (this is a ledger, not a scoreboard) and gains its legibility. */
.rd-l-state {
  font-family: var(--bill); font-size: var(--t-0); font-weight: 700;
  color: var(--steel); letter-spacing: .06em;
  font-variant-numeric: tabular-nums; white-space: nowrap;
  align-self: start; padding-top: .28em;
}
/* A SET LINE IS BOUND WORK, NOT DELETED WORK. It was struck at .66 opacity,
   which is how a page shows a mistake — and these are the reader's four
   victories, the only lines on the sheet they can already read cold. They
   stay fully legible (that is the point of them), the strike thins to a
   compositor's rule in the chapter's ink, and the row rests on a warmer
   paper: work that has been pulled and filed. */
.rd-l-done {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--galley));
}
.rd-l-done .rd-l-ru {
  text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
  text-decoration-thickness: 1px;
  opacity: .82;
}
.rd-l-done .rd-l-no { color: color-mix(in srgb, var(--ink, var(--carbon)) 60%, transparent); }
.rd-l-done .rd-l-state {
  color: var(--ink, var(--carbon));
  text-transform: uppercase; letter-spacing: .16em; font-size: var(--t-00);
}
/* THE NUMERAL AND THE STATE NEVER GOT THE DARK-ROOM LIFT. The book's
   ink arrives as an inline hex, so it cannot follow the room — and after dark
   these three sit on a ground of that same hex over the night galley — 9% of
   it on the current row, 5% on the struck one — which is to say they are very
   nearly the colour of the paper under them (grafit 1.30:1, lazur 1.34:1).
   Lifted toward the room's warm off-white by exactly the mix .rd-hit:hover
   .rd-ti, .rd-next and .rd-l-mv already use, so the pigment survives and the
   numeral and the price stay readable: 6.4–7.9:1 on all six masstones over
   both grounds. The light room is untouched. */
[data-theme="dark"] .rd-l-here .rd-l-no,
[data-theme="dark"] .rd-l-here .rd-l-state,
[data-theme="dark"] .rd-l-done .rd-l-state {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}

/* THE MOVEMENTS, ON THE LIST. The chapter's arc — У двери, Ковка, Сбор —
   plays differently in the room; the rows now wear it, so the list reads as
   a one-act piece rather than a column of identical chores. The label sits
   in the chapter's own ink; the marked rows carry a matching weight:
   the door a threshold band, the forge a heavier cut, the gathering the
   ruled paper it will be assembled on. The plain settings stay plain —
   marking every row would make the marking mean nothing. */
/* the movement's name is a stage direction, not a tag — it sits above its
   line with air around it and the tracking of a printed heading */
.rd-l-mv {
  font-family: var(--bill); font-size: var(--t-00); text-transform: uppercase;
  letter-spacing: .2em; color: var(--ink, var(--carbon));
  margin-top: 5px; opacity: .85;
}
[data-theme="dark"] .rd-l-mv {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}
/* THE THREE MOVEMENTS, RE-CUT FOR A SHEET WITH NO BOXES ON IT. The forge
   used to declare itself with a 2px border, which on a galley reads as a
   stray rule; each movement now speaks in ground and edge instead, so the
   scene still plays as an opening, a forging and a gathering — and the
   plain settings stay plain, because marking every row marks none. */
.rd-lesson.rd-mv-door:not(.rd-l-done) {
  box-shadow: inset 3px 0 0 var(--ink, var(--carbon));
}
.rd-lesson.rd-mv-forge:not(.rd-l-done) {
  box-shadow: inset 5px 0 0 color-mix(in srgb, var(--ink, var(--carbon)) 78%, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 7%, var(--galley));
}
.rd-lesson.rd-mv-gather:not(.rd-l-done) {
  background:
    repeating-linear-gradient(transparent 0 12px,
      color-mix(in srgb, var(--ink, var(--carbon)) 10%, transparent) 12px 13px),
    var(--galley);
}
/* the marked rows keep their ground when the hand rests, so a hover never
   erases the movement it is hovering */
.rd-lesson.rd-mv-forge:not(.rd-l-done):hover {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 11%, var(--galley));
}

/* ---- EVERY SCREEN. Below the fold of a phone the three columns stop being
   columns: the cost drops under its line rather than squeezing the type into
   a gutter, and the margin narrows to what a two-digit number needs. The
   literature keeps its size — it is the one thing that must not shrink. ---- */
@media (max-width: 560px) {
  .rd-lesson {
    grid-template-columns: 22px 1fr;
    padding: 12px 10px 13px; gap: 2px 10px;
  }
  .rd-l-here { grid-template-columns: 34px 1fr; }
  .rd-l-state {
    grid-column: 2; justify-self: start;
    padding-top: 5px; letter-spacing: .12em;
  }
  .rd-l-done .rd-l-state { padding-top: 6px; }
}

/* BEHIND GLASS, ON THE ROAD — the shelf's pane, echoed (see .glassed in
   65-home.css). The spine keeps its ink at full strength, because nothing
   here is wrong; a pane of the room's light lies over the volume, and the
   promise line cools to steel. The lessons inside stay free to play. */
.rd-glass .rd-vol::before {
  content: ''; position: absolute; inset: 0; z-index: 2;
  background: linear-gradient(115deg, transparent 32%,
    rgba(255, 255, 255, .16) 40%, rgba(255, 255, 255, .05) 47%, transparent 56%);
  pointer-events: none;
}
.rd-glass .rd-opens { color: var(--steel); }

/* THE LOCKED CHAPTER — glass, not fog. The band keeps its whole pigment,
   because a thing dimmed into invisibility is a thing not worth wanting;
   only the pane's light lies across it, the same stroke the glassed spine
   wears above. What it opens into is the quiet row below, not a lesson list. */
.rd-ch-glass .rd-ch-head { position: relative; }
.rd-ch-glass .rd-ch-head::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(115deg, transparent 30%,
    rgba(255, 255, 255, .18) 40%, rgba(255, 255, 255, .06) 48%, transparent 58%);
  pointer-events: none;
}

/* the glass row: the librarian's one sentence and one door. The chapter's
   ink stays on it as a spine-stripe — the pane cools nothing but the play. */
.rd-glass-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px;
  padding: 12px 16px;
  background: var(--sheet); border: 1px solid var(--rule);
  box-shadow: inset 4px 0 0 var(--ink, var(--carbon));
}
.rd-glass-say {
  flex: 1 1 240px; font-family: var(--serif); font-style: italic;
  font-size: var(--t-1); color: var(--carbon);
}
.rd-glass-say b { font-style: normal; font-variant-numeric: tabular-nums; }

/* THE ASK ON THE ROAD — one landing where owned crosses into glass. It sits
   full-width across the road's line, plate-built like the alphabet's payoff
   (sheet, carbon, the cast shadow), and carries the pane's own light. Paper
   and carbon only: the page's single vermilion stays on "where you are". */
.rd-ask { position: relative; padding: 18px 0; }
.rd-ask-plate {
  position: relative; overflow: hidden;
  display: grid; grid-template-columns: auto 1fr auto;
  gap: 10px 18px; align-items: center;
  padding: 16px 20px;
  background: var(--sheet); border: 1px solid var(--carbon);
  box-shadow: var(--lift);
}
.rd-ask-plate::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(115deg, transparent 34%,
    rgba(255, 255, 255, .16) 42%, rgba(255, 255, 255, .05) 49%, transparent 58%);
  pointer-events: none;
}
.rd-ask-orn { font-size: var(--t-4); color: var(--carbon); }
.rd-ask-mid { display: grid; gap: 2px; min-width: 0; }
.rd-ask-ti {
  font-family: var(--bill); text-transform: uppercase; font-weight: 700;
  letter-spacing: .1em; font-size: var(--t-1); color: var(--carbon);
}
.rd-ask-line {
  font-family: var(--serif); font-style: italic; font-size: var(--t-1);
  color: var(--carbon); font-variant-numeric: tabular-nums;
}
.rd-ask-btn { position: relative; }
@media (max-width: 620px) {
  .rd-ask-plate { grid-template-columns: auto 1fr; }
  .rd-ask-btn { grid-column: 1 / -1; justify-self: start; }
}

.cs-standing b { font-variant-numeric: tabular-nums; }

.cs-ref { margin-top: 30px; border-top: 2px solid var(--carbon); padding-top: 6px; }
.cs-ref .rd-ref-hit .rd-vol { background: var(--sheet); }

@media (max-width: 620px) {
  .rd::before { left: 21px; }
  .rd-hit, .rd-mark, .rd-beyond { grid-template-columns: 48px 1fr; gap: 0 12px; }
  .rd-hit { padding: 12px; }
  .rd-vol { width: 46px; height: 62px; }
  .rd-vol-a { height: 46px; }
  .rd-book::after { font-size: 52px; }
  .rd-ch-head::before { right: 64px; font-size: 48px; }
  .rd-open-box { margin: 0 10px 16px 10px; }
  .rd-who { grid-template-columns: 56px 1fr; }
  .rd-who-fig { width: 56px; height: 56px; padding: 4px; }
}

/* ============================================================================
   THE ROAD DRAWS ITSELF. Native scroll timelines; the Firefox fallback is the
   finished state — a fully drawn road, which is exactly right.
   ============================================================================ */

@media (prefers-reduced-motion: no-preference) {

  /* THE LINE IS DRAWN as you go down it, one line being inked rather than
     fourteen segments arriving. */
  .rd::before { transform-origin: 50% 0; }
  @keyframes rd-draw { from { transform: scaleY(0); } to { transform: scaleY(1); } }
  .rd::before {
    animation: rd-draw linear both;
    animation-timeline: view();
    animation-range: entry 30% cover 62%;
  }

  /* A SPINE IS SHELVED, from the left, and stops dead. The body follows a
     beat later, so the mark arrives before what it says. The volume's
     keyframes END at its standing tilt — a `to` of none would flatten the
     perspective the moment the fill pinned it. */
  @keyframes rd-set { from { transform: translateX(-18px); opacity: 0; } to { transform: none; opacity: 1; } }
  @keyframes rd-vol-set {
    from { transform: perspective(340px) rotateY(-10deg) translateX(-18px); opacity: 0; }
    to { transform: perspective(340px) rotateY(-10deg); opacity: 1; }
  }
  .rd-vol {
    animation: rd-vol-set linear both;
    animation-timeline: view();
    animation-range: entry 8% entry 46%;
  }
  .rd-body {
    animation: rd-set linear both;
    animation-timeline: view();
    animation-range: entry 16% entry 58%;
  }

  /* WHERE YOU ARE breathes, once every eight seconds, and nothing else on the
     page moves at rest. One heartbeat on one element is a place; a page of
     them is a fidget. */
  @keyframes rd-here {
    0%, 92%, 100% { box-shadow: 0 0 0 3px var(--room), 0 0 0 5px var(--signal); }
    96%           { box-shadow: 0 0 0 3px var(--room), 0 0 0 8px var(--signal); }
  }
  .rd-here .rd-vol { animation: rd-here 8s ease-in-out infinite; }
}

/* THE BOOK THAT WAS JUST FINISHED. Not confetti and not a badge: the spine
   fills with its ink and the numbers reverse out of it. Fired once, by the
   code that notices the transition — never on a repaint. */
@keyframes rd-seal {
  0%   { transform: scale(1); }
  38%  { transform: scale(1.12); }
  100% { transform: scale(1); }
}
.rd-sealing .rd-vol {
  animation: rd-seal 620ms var(--e-snap, cubic-bezier(.2,.9,.3,1.2)) both;
  background: var(--ink, var(--carbon));
}
.rd-sealing .rd-vol b { color: var(--galley); mix-blend-mode: normal; filter: none; }
@media (prefers-reduced-motion: reduce) { .rd-sealing .rd-vol { animation: none; } }

/* ============================================================================
   THE ALPHABET'S PAYOFF.

   The letters are not a chore before the Russian — they are the thing that
   turns the Russian on. After each group the reader is shown, in real numbers
   from the shelf, how much of these books they can now decode, then real words
   to try, then a real line. The arc from "I can't read this" to "I just read
   Chekhov" is the whole first ten minutes.
   ============================================================================ */

.cs-group .cs-letter {
  cursor: pointer; text-align: left; font-family: inherit; color: inherit;
  transition: background 120ms linear;
}
.cs-group .cs-letter:hover { background: var(--sheet); }
.cs-group .cs-letter:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }

.ab-win {
  margin-top: 14px; padding: 16px 18px;
  background: var(--sheet); border: 1px solid var(--carbon);
  box-shadow: var(--lift);
  display: grid; gap: 12px;
}
.ab-count { font-family: var(--serif); font-size: var(--t-2); color: var(--carbon); }
.ab-count b { font-variant-numeric: tabular-nums; }
.ab-read { display: flex; flex-wrap: wrap; gap: 8px; }
.ab-w {
  font-family: var(--serif); font-size: var(--t-2); cursor: pointer;
  background: var(--galley); border: 1px solid var(--carbon); border-radius: 0;
  padding: 5px 12px; color: inherit;
}
.ab-w:hover { background: var(--room); }
.ab-w:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.ab-line {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  border-top: 1px solid var(--rule); padding-top: 12px;
}
.ab-ru { font-family: var(--serif); font-size: var(--t-4); color: var(--carbon); }
.ab-en { font-family: var(--serif); font-style: italic; font-size: var(--t-1); color: var(--steel); }
.ab-say-line { padding: 4px 10px; }

/* ============================================================================
   THE LESSON ROOM'S RESIDENT, AND THE CHAPTER SEAL.

   The figure sits low in the corner of the room, small, out of the line of
   sight to the question — present, never presiding. At the chapter seal it
   comes to the middle, because that moment is about the two of you.
   ============================================================================ */

.gx-mate {
  position: fixed; right: clamp(14px, 4vw, 48px); bottom: clamp(12px, 3vh, 34px);
  width: clamp(96px, 13vw, 160px); height: clamp(96px, 13vw, 160px);
  pointer-events: none; z-index: 1;
  display: grid; justify-items: center;
}
/* THE ENGRAVED VIGNETTE, in the room too: the figure stands in a printed
   medallion of the chapter's ink on the chapter's paper — an illustration
   from the book it presides over, not a sticker on the interface. The
   toning pulls its colours a step toward the pressroom. */
.gx-mate::before {
  content: ''; position: absolute; inset: 4%; border-radius: 50%;
  background: radial-gradient(circle at 50% 38%,
    color-mix(in srgb, var(--ink, var(--carbon)) 7%, var(--galley)),
    color-mix(in srgb, var(--ink, var(--carbon)) 20%, var(--galley)));
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  box-shadow: inset 0 0 0 4px var(--galley),
    inset 0 0 0 5px color-mix(in srgb, var(--ink, var(--carbon)) 40%, transparent),
    0 2px 3px color-mix(in srgb, var(--ink, var(--carbon)) 18%, transparent), 0 9px 22px -10px color-mix(in srgb, var(--ink, var(--carbon)) 40%, transparent);
}
.gx-mate svg { position: relative; filter: saturate(.8) sepia(.08) contrast(1.03); }
/* the resident's one arrival line — its own species' words, set small under
   the figure, gone after a breath. It never overlaps the work: pointer-events
   none, and it dies with the first repaint. */
.gx-mate-line {
  position: absolute; bottom: 100%; right: 0; margin-bottom: 6px;
  max-width: 220px; width: max-content;
  font-family: var(--serif); font-style: italic; font-size: var(--t-0);
  color: var(--carbon); background: var(--sheet);
  border: 1px solid var(--carbon); box-shadow: var(--lift-sm);
  padding: 6px 12px; text-align: right;
  transition: opacity 600ms var(--e-std, ease);
}
.gx-mate-line-gone { opacity: 0; }
.gx-topic i { font-style: normal; margin-right: 8px; color: var(--ink, var(--carbon)); }

/* THE STRESS QUESTION: the word set large, each vowel a pressable sort. No
   red and no green — the right vowel takes the chapter's own ink, a wrong
   press simply loses its weight. (The room's silhouettes key off
   data-kind="stress" in 26-games.css.) */
.gx-stress-word {
  font-family: var(--serif); font-size: clamp(38px, 7vw, 64px);
  line-height: 1.15; color: var(--carbon);
}
.gx-vowel {
  font: inherit; color: inherit; background: none; cursor: pointer;
  border: 0; border-bottom: 3px solid var(--rule); border-radius: 0;
  padding: 0 2px; margin: 0 1px;
}
.gx-vowel:hover:not(:disabled) { border-bottom-color: var(--ink, var(--carbon)); }
.gx-vowel:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.gx-vowel:disabled { cursor: default; }
.gx-vowel.gx-right {
  border-bottom-color: var(--ink, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 14%, transparent);
}
.gx-vowel.gx-wrong { opacity: .45; }

/* the exposure phase of SET THE LINE: the sentence held out to be read,
   quoted the way the source line is quoted everywhere else in the room */
.gx-study {
  font-family: var(--serif); border-left: 4px solid var(--ink, var(--carbon));
  padding: 6px 0 6px 18px;
}

/* where the reader stands in the chapter, next to its name */
.gx-topic em {
  font-style: normal; margin-left: 12px; letter-spacing: .04em;
  font-size: var(--t-00); color: var(--steel); font-variant-numeric: tabular-nums;
}

/* ============================================================================
   THE PAYOUT AND THE PULL — what a lesson opened, what is one word away,
   and the line under the door at the seal. All of it is literature; none of
   it is predictable from inside the lesson. That is the whole machine.
   ============================================================================ */

.gx-fell, .gx-near { display: grid; gap: 8px; margin-top: 4px; }
.gx-fell-n, .gx-near-n {
  font-family: var(--serif); font-style: italic; font-size: var(--t-1);
  color: var(--carbon);
}
.gx-near-line {
  font-family: var(--serif); font-size: var(--t-1); color: var(--carbon);
  border-left: 3px solid var(--ink, var(--carbon)); padding-left: 12px;
}
.gx-near-line b {
  display: block; font-family: var(--sans); font-weight: 400;
  font-size: var(--t-00); color: var(--steel); letter-spacing: .04em;
}

.gx-cliff { display: grid; gap: 6px; justify-items: center; margin-top: 10px; }
.gx-cliff:empty { display: none; }
.gx-cliff-rule { width: 120px; height: 1px; background: var(--rule); margin-bottom: 6px; }
.gx-cliff-next {
  font-family: var(--sans); font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--steel);
}
.gx-cliff-next b { color: var(--carbon); }
.gx-cliff-line { font-family: var(--serif); font-size: var(--t-2); color: var(--carbon); text-align: center; }
.gx-cliff-line span {
  display: block; font-style: italic; font-size: var(--t-0); color: var(--steel);
}
.gx-cliff-cost {
  font-size: var(--t-00); color: var(--steel); letter-spacing: .06em;
  font-variant-numeric: tabular-nums;
}

/* THE INSTALMENT PLATE on the finish sheet — the serial's reward in the
   payout's own grammar: a fact and a door. Never a meter, never a count-
   down; the chapter's ink marks it as the scene's own offer. */
.gx-inst {
  display: grid; gap: 8px; justify-items: start; margin-top: 10px;
  border-left: 3px solid var(--ink, var(--carbon)); padding-left: 12px;
}
.gx-inst-n { font-family: var(--serif); font-size: var(--t-1); color: var(--carbon); }

/* the endowed count on an open chapter: what is already yours, said first */
.rd-endow { margin: -2px 0 0; font-family: var(--serif); font-style: italic;
  font-size: clamp(15px, .93rem + .22vw, 18px); line-height: 1.5;
  color: var(--carbon); text-wrap: pretty; }
.rd-endow b { font-variant-numeric: tabular-nums; font-style: normal; }

/* the chapter row's instalment standing — a fact of the serial, set in
   the endowment's voice; the door is a plain underlined act, not a chip */
.rd-inst { margin: 0; font-family: var(--serif); font-style: italic;
  font-size: clamp(15px, .93rem + .22vw, 18px); line-height: 1.5;
  color: var(--carbon); text-wrap: pretty; }
.rd-inst-read {
  background: none; border: 0; border-radius: 0; padding: 0; cursor: pointer;
  font: inherit; color: inherit; font-style: normal;
  text-decoration: underline dotted; text-underline-offset: 3px;
}
.rd-inst-read:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

/* the ledger's returns, named once on the finish sheet — one line, one
   true count, no meter (overhaul item 22) */
.gx-due { margin: 0; font-family: var(--serif); font-size: var(--t-1); color: var(--steel); }
.gx-due b { font-variant-numeric: tabular-nums; color: var(--carbon); }

/* THE GHOST LINE — a real line with its unheld words as low-ink gaps. The
   gaps NEVER animate and never fill on screen: they are facts of the road,
   not a meter. Closing them happens in the reader's head, where it counts. */
.gx-ghostline {
  margin: 2px 0 0; font-family: var(--sans); font-size: var(--t-0);
  color: var(--steel); display: grid; gap: 4px;
}
.gx-ghost-ru { font-family: var(--serif); font-size: var(--t-2); color: var(--carbon); }
.gx-ghost { opacity: .38; letter-spacing: .04em; }
.gx-ghost-shy { font-style: italic; }
.tn-ghost { display: block; font-family: var(--serif); font-size: var(--t-2); color: var(--carbon); margin-top: 3px; }
.tn-ghost .gx-ghost { opacity: .38; }
.tn-ghost-shy { display: block; font-style: italic; font-size: var(--t-00); color: var(--steel); margin-top: 2px; }

/* the take-word button inside a near-miss line: a plain act, not a chip */
.gx-take {
  background: none; border: 0; border-radius: 0; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-decoration: underline dotted;
  text-underline-offset: 3px;
}
.gx-take:disabled { text-decoration: none; cursor: default; color: var(--steel); }
.gx-take:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

.gx-sealed { justify-items: center; text-align: center; }
.gx-seal-wrap { margin-top: 6px; }
.gx-sealed-ti { margin: 6px 0 0; font-family: var(--serif); font-size: clamp(26px, 4.6vw, 40px); }
.gx-sealed-ru { font-size: var(--t-1); color: var(--steel); }
.gx-sealed-buy { margin: 8px 0 0; font-family: var(--serif); font-size: var(--t-2); }
.gx-sealed-buy b { font-variant-numeric: tabular-nums; }
.gx-toward { margin: 0; font-family: var(--serif); font-style: italic; font-size: var(--t-1); color: var(--carbon); }
.gx-bye { margin: 0; font-family: var(--serif); font-style: italic; font-size: var(--t-1); color: var(--steel); }
.gx-bookdone { margin: 0; font-family: var(--serif); font-size: var(--t-2); color: var(--carbon); }
.gx-mate-done {
  position: relative; width: 84px; height: 84px; margin: 4px auto 0;
  pointer-events: none;
}

/* ============================================================================
   THE CHAPTER OPENS LIKE A DRAWER OF TYPE — lesson rows lay themselves down
   in order when a chapter unfolds, each a beat after the last. Interleaving
   research says the arc (door → forge → gather) is the pedagogy; this makes
   the arc something the eye watches happen instead of a list that appears.
   ============================================================================ */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .rd-ch.rd-open .rd-lesson,
  body:not(.motion-low) .rd-book.rd-open .rd-lesson {
    animation: rd-l-in 320ms var(--e-std, ease) backwards;
  }
  body:not(.motion-low) .rd-lesson:nth-child(2) { animation-delay: 45ms; }
  body:not(.motion-low) .rd-lesson:nth-child(3) { animation-delay: 90ms; }
  body:not(.motion-low) .rd-lesson:nth-child(4) { animation-delay: 135ms; }
  body:not(.motion-low) .rd-lesson:nth-child(5) { animation-delay: 180ms; }
  body:not(.motion-low) .rd-lesson:nth-child(6) { animation-delay: 225ms; }
  body:not(.motion-low) .rd-lesson:nth-child(n+7) { animation-delay: 260ms; }
  @keyframes rd-l-in {
    from { opacity: 0; transform: translateX(-7px); }
    to { opacity: 1; transform: none; }
  }
}

/* ============================================================
   THE SYLLABUS v2 (the owner's «horrible» verdict, answered): band
   plates with ink spines and real progress; the subjects as CARDS with
   emblems, obviously pressable; the arcs with station ledgers; the
   bindery receded to a quiet line. Everything that looks pressable is.
   ============================================================ */
.syl { margin: 6px 0 30px; }
.syl-head h2 {
  font-family: var(--serif); font-weight: 600; margin: 0 0 4px;
  font-size: clamp(1.3rem, 3.6vw, 1.7rem);
}
.syl-head p { max-width: 64ch; opacity: .8; margin: 0 0 14px; font-size: .95rem; line-height: 1.55; }
.syl-bands { display: grid; gap: 12px; }
.syl-band {
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 40%, var(--carbon));
  border-left: 6px solid var(--ink, var(--carbon));
  background: color-mix(in srgb, var(--ink, var(--carbon)) 4%, var(--sheet, transparent));
  box-shadow: 0 2px 0 var(--press, rgba(0,0,0,.12));
}
.syl-band summary {
  display: flex; align-items: center; gap: 14px; padding: 14px 16px;
  cursor: pointer; list-style: none;
}
.syl-band summary::-webkit-details-marker { display: none; }
.syl-band summary:hover { background: color-mix(in srgb, var(--ink, var(--carbon)) 7%, transparent); }
.syl-rom {
  font-family: var(--serif); font-size: 2rem; line-height: 1;
  color: var(--ink, var(--carbon)); width: 40px; text-align: center; flex: 0 0 auto;
}
.syl-band-mid { display: grid; gap: 3px; min-width: 0; flex: 1; }
.syl-band-mid b { font-family: var(--serif); font-size: 1.1rem; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.syl-band-mid em { font-style: normal; font-size: .84rem; opacity: .72; }
.syl-you {
  font-style: normal; font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .07em; text-transform: uppercase;
  padding: 2px 8px; color: var(--galley);
  background: var(--ink, var(--carbon));
}
.syl-bar {
  display: block; height: 5px; max-width: 300px; margin-top: 3px;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 18%, transparent);
}
.syl-bar i { display: block; height: 100%; background: var(--ink, var(--carbon)); }
.syl-band-n { margin-left: auto; font-size: .8rem; opacity: .75; text-align: right; flex: 0 0 auto; }
.syl-body { padding: 4px 16px 16px; border-top: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 22%, transparent); }
.syl-note { font-size: .93rem; line-height: 1.55; opacity: .85; max-width: 64ch; margin: 12px 0; }

/* the subjects, as pressable cards */
.syl-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 10px; margin: 12px 0; }
.syl-card {
  display: flex; align-items: center; gap: 11px; text-align: left;
  padding: 11px 13px; background: var(--galley); color: inherit; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  box-shadow: 0 3px 0 var(--press, rgba(0,0,0,.16));
  position: relative;
}
.syl-card:hover { background: var(--sheet); transform: translateY(-1px); box-shadow: 0 4px 0 var(--press, rgba(0,0,0,.16)); }
.syl-card:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--press, rgba(0,0,0,.16)); }
.syl-c-emb { width: 34px; height: 34px; flex: 0 0 auto; color: var(--ink, var(--carbon)); display: flex; align-items: center; justify-content: center; }
.syl-c-emb svg { width: 100%; height: 100%; }
.syl-c-emb b { font-size: 24px; font-weight: 400; }
.syl-c-mid { display: grid; min-width: 0; }
.syl-c-mid b { font-family: var(--serif); font-size: .99rem; line-height: 1.25; }
.syl-c-mid em { font-style: normal; font-size: .76rem; opacity: .68; }
.syl-c-done { opacity: .82; }
.syl-c-seal {
  position: absolute; top: 6px; right: 8px;
  font-size: .8rem; color: var(--ink, var(--carbon));
}
.syl-c-mod .syl-c-mid em::after { content: ' \00b7 exam set'; letter-spacing: .02em; }

/* the arcs, with their station ledgers */
.syl-arcs { display: grid; gap: 9px; margin: 12px 0; }
.syl-arc {
  display: flex; align-items: center; gap: 13px; text-align: left;
  padding: 11px 14px; background: var(--galley); color: inherit; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  box-shadow: 0 3px 0 var(--press, rgba(0,0,0,.16));
}
.syl-arc:hover { background: var(--sheet); transform: translateY(-1px); }
.syl-mark { font-style: normal; font-size: 1.6rem; color: var(--ink, var(--carbon)); width: 32px; text-align: center; flex: 0 0 auto; }
.syl-arc-mid { display: grid; gap: 3px; min-width: 0; flex: 1; }
.syl-arc-mid b { font-family: var(--serif); font-size: 1.02rem; }
.syl-arc-mid em { font-style: normal; font-size: .78rem; opacity: .7; }
.syl-dots { display: flex; gap: 4px; flex-wrap: wrap; }
.syl-dots i {
  width: 9px; height: 9px; border-radius: 50%;
  border: 1.5px solid color-mix(in srgb, var(--ink, var(--carbon)) 60%, transparent);
}
.syl-dots .syl-d-held { background: var(--ink, var(--carbon)); border-color: var(--ink, var(--carbon)); }
.syl-go { margin-left: auto; font-size: 1.4rem; opacity: .5; flex: 0 0 auto; }

/* the bindery, receded: plainly not buttons */
.syl-planned {
  margin-top: 12px; font-size: .82rem; line-height: 1.8; opacity: .55;
  font-style: italic; max-width: 70ch;
}
.syl-pl-cap {
  font-style: normal; font-variant: all-small-caps; letter-spacing: .06em;
  margin-right: 8px; opacity: .9;
  border-bottom: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 40%, transparent);
}

/* ============================================================
   THE TWO WINGS (30 Aug): the course and the road stand apart. The
   rail is two letterpress door-plates; the open wing's plate is
   raised and carries its ink underline; the shut one recedes but
   stays plainly a door.
   ============================================================ */
.cs-wings {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  margin: 18px 0 22px;
}
.cs-wing-tab {
  display: flex; align-items: center; gap: 13px; text-align: left;
  padding: 13px 16px; cursor: pointer; color: inherit;
  background: color-mix(in srgb, var(--carbon) 4%, var(--sheet, transparent));
  border: 1px solid color-mix(in srgb, var(--carbon) 45%, transparent);
  box-shadow: none; opacity: .74;
}
.cs-wing-tab:hover { opacity: .95; background: var(--galley); }
.cs-wing-tab[aria-selected="true"] {
  opacity: 1; background: var(--galley);
  border-color: var(--carbon);
  border-bottom: 4px solid var(--accent, var(--carbon));
  box-shadow: 0 3px 0 var(--press, rgba(0,0,0,.16));
}
.cs-wt-dev {
  font-style: normal; font-family: var(--serif); font-size: 1.7rem;
  line-height: 1; color: var(--accent, var(--carbon));
  width: 34px; text-align: center; flex: 0 0 auto;
}
.cs-wt-mid { display: grid; gap: 2px; min-width: 0; }
.cs-wt-mid b { font-family: var(--serif); font-size: 1.04rem; line-height: 1.25; }
.cs-wt-mid em { font-style: normal; font-size: .78rem; opacity: .7; }
#v-course[data-wing="course"] .cs-wing-books { display: none; }
#v-course[data-wing="books"] .cs-wing-course { display: none; }
.cs-shelf-lede { max-width: 70ch; margin: 0 0 16px; }
@media (max-width: 560px) {
  .cs-wings { gap: 8px; }
  .cs-wing-tab { padding: 10px 10px; gap: 9px; }
  .cs-wt-dev { font-size: 1.3rem; width: 24px; }
  .cs-wt-mid b { font-size: .92rem; }
  .cs-wt-mid em { font-size: .7rem; }
}

/* the closed band shows its hand: a chevron that turns, and a peek line
   naming what the drawer holds — nothing pressable is invisible now */
.syl-chev {
  font-style: normal; flex: 0 0 auto; margin-left: 6px;
  font-size: .95rem; opacity: .55;
  transition: transform .25s ease;
}
.syl-band[open] > summary .syl-chev { transform: rotate(90deg); }
.syl-peek {
  font-size: .78rem; line-height: 1.5; opacity: .62; font-style: italic;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; max-width: 72ch; margin-top: 2px;
}
.syl-band[open] > summary .syl-peek { display: none; }


/* the Learn opening at phone width: the board leads, the essay folds —
   three lines of the walk-note, then the course itself */
@media (max-width: 600px) {
  #v-course .room-head .cs-lede:not(.cs-standing) {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
  }
}


/* ===== 28-feel.css ===== */
/* ============================================================================
   28-feel.css — THE FEEL OF THE LESSON ROOM.

   The exercise room had information and no sensation: a class changed, a tone
   played, and nothing on the screen acknowledged the hand. This sheet adds the
   print-shop's own physics, under the laws that govern everything else:

   - The first frame acknowledges. Every effect's expressive part comes AFTER
     an instant state change, never instead of it (the ≤100ms law).
   - The press has mass. Impact arrives fast and stops dead — the one
     sanctioned overshoot is ≤2% of scale, which is paper compliance, not
     bounce. Nothing elastic, nothing rubber.
   - Errors cost LESS sensory time than successes, not more: one decaying
     nudge of the touched element, quieter and shorter than the stamp.
   - Permanence over flash: the reward is ink that stays — the rail mark
     filling, the line struck onto the sheet, the ornament landing.
   - Everything here fires dozens of times a sitting, so every amplitude is
     divided for the fiftieth repetition, and every animation dies under
     reduced motion into its correct final state.
   ========================================================================== */

/* ---- the stamp: a correct answer is SET. Compress, dwell, release. ---- */
@keyframes gx-stamp {
  0%   { transform: scale(1); }
  22%  { transform: scale(0.965); }     /* the platen lands…            */
  55%  { transform: scale(0.965); }     /* …and DWELLS — the 60ms hold  */
  100% { transform: scale(1); }         /* released; no overshoot       */
}
.gx-stamped { animation: gx-stamp calc(0.26s * var(--motion)) var(--e-std) both; }

/* ---- the nudge: a wrong answer. One decaying oscillation, then stillness.
   Never the viewport, never the panel — only the thing the reader touched. */
@keyframes gx-nudge {
  0% { transform: translateX(0); }
  30% { transform: translateX(-4px); }
  65% { transform: translateX(2px); }
  100% { transform: translateX(0); }
}
.gx-nudged { animation: gx-nudge calc(0.2s * var(--motion)) ease-out both; }

/* ---- the rail: a word cleared is ink set into the line, from the foot up,
   the same gesture as the route's rising dots. Marks are ≤8 and bounded. ---- */
@keyframes gx-rail-set {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}
.gx-rail i { transform-origin: 50% 100%; }
.gx-rail .gx-r-done { animation: gx-rail-set calc(0.18s * var(--motion)) var(--e-std) both; }

/* ---- the run: a printed numeral, not a meter. Appears at three, increments
   with an ink change, never pulses, and its disappearance is silent — a
   broken run gets zero dedicated feedback. ---- */
.gx-top { grid-template-columns: 1fr auto auto auto; gap: 0 14px; }
.gx-runn {
  font-family: var(--mono); font-size: var(--t-00);
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--steel);
  align-self: center;
}
.gx-again {
  display: block;
  font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .06em; text-transform: none;
  color: var(--steel); margin-top: 3px;
}

/* ---- type striking paper: the corrected word, and the printed line at the
   end, arrive glyph by glyph — full ink from the first frame, over-scaled a
   hair, compressed to rest. A fade would be a ghost; type is struck. ---- */
@keyframes gx-glyph-strike {
  0%   { opacity: 1; transform: scale(1.12); font-weight: 700; }
  60%  { transform: scale(0.98); }
  100% { opacity: 1; transform: scale(1); }
}
.gx-glyph {
  display: inline-block;
  animation: gx-glyph-strike calc(0.14s * var(--motion)) var(--e-std) both;
}
.gx-reveal { font-style: normal; }

/* ---- the finish: the sheet is LAID DOWN — the room's one 3D beat. A deboss
   is z-travel under weight, which is a press; a spin would be a coin. ---- */
@keyframes gx-lay3d {
  0%   { opacity: 0; transform: perspective(900px) rotateX(-9deg) translateY(-14px); box-shadow: 0 0 0 var(--ink, var(--carbon)); }
  55%  { opacity: 1; transform: perspective(900px) rotateX(0deg) translateY(0);      box-shadow: 2px 2px 0 var(--ink, var(--carbon)); }
  100% { opacity: 1; transform: none; box-shadow: 6px 6px 0 var(--ink, var(--carbon)); }
}
.gx-done .gx-print.gx-lay {
  transform-origin: 50% 100%;
  animation: gx-lay3d calc(0.5s * var(--motion)) var(--e-std) both;
}

/* the rule draws itself under the printed line, and the ornament lands at its
   end — drawn, then stamped: the page's two verbs, in order */
.gx-rule { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.gx-rule i {
  flex: 1; height: 2px; background: var(--carbon); display: block;
  transform-origin: 0 50%;
  animation: gx-rule-draw calc(0.32s * var(--motion)) var(--e-std) calc(0.5s * var(--motion)) both;
}
@keyframes gx-rule-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.gx-orn {
  color: var(--signal); font-size: var(--t-2); line-height: 1;
  animation: gx-orn-stamp calc(0.2s * var(--motion)) var(--e-std) calc(0.82s * var(--motion)) both;
}
@keyframes gx-orn-stamp {
  /* audited 2026-08-22: the dwell sat at 0.95 — a 5% dip through rest,
     past the ≤2% paper-compliance ceiling this sheet's own header sets.
     Held to 0.98: still a press, no longer a spring. */
  0%   { opacity: 0; transform: scale(1.3); }
  40%  { opacity: 1; transform: scale(0.98); }
  70%  { transform: scale(0.98); }
  100% { opacity: 1; transform: scale(1); }
}

/* the facts row: mono numbers, dotted rules — the record, not a scoreboard */
.gx-facts {
  display: flex; flex-wrap: wrap; gap: 8px 26px;
  justify-content: center;
  font-family: var(--sans); font-size: var(--t-0); color: var(--steel);
}
.gx-facts b {
  font-family: var(--mono); font-weight: 400;
  font-size: var(--t-3); color: var(--carbon);
  margin-right: 5px;
}

/* ---- reduced motion: everything above dies into its finished state.
   The gate mirrors 30-choreo's: the OS preference is the default, an explicit
   choice in Settings beats it. ---- */
body.motion-low .gx-stamped,
body.motion-low .gx-nudged,
body.motion-low .gx-rail .gx-r-done,
body.motion-low .gx-glyph,
body.motion-low .gx-done .gx-print.gx-lay,
body.motion-low .gx-rule i,
body.motion-low .gx-orn { animation: none; }
@media (prefers-reduced-motion: reduce) {
  body:not(.motion-full) .gx-stamped,
  body:not(.motion-full) .gx-nudged,
  body:not(.motion-full) .gx-rail .gx-r-done,
  body:not(.motion-full) .gx-glyph,
  body:not(.motion-full) .gx-done .gx-print.gx-lay,
  body:not(.motion-full) .gx-rule i,
  body:not(.motion-full) .gx-orn { animation: none; }
}

/* ============================================================
   THE REFERENCE, BROUGHT INTO THE ROOM.
   The sixteen grammar articles still wore the previous design generation —
   rounded cards, soft borders, gold washes — and opening one from the course
   was a visible change of product. Same plane, same ink, same bars now.
   ============================================================ */
.glist { gap: 2px; }
.glesson-card {
  border-radius: 0; border: 1px solid var(--carbon);
  background: var(--galley); box-shadow: none;
  padding: 12px 16px;
}
.glesson-card:hover { background: var(--sheet); transform: none; box-shadow: none; }
.glesson-card .gru { font-family: var(--bill); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--carbon); }
.glesson-card .gen2 { font-family: var(--serif); font-style: italic; color: var(--steel); }
/* the § numeral: each article is a numbered paragraph of one reference work,
   not an interchangeable card. It warms to signal under the hand. */
.glesson-card { position: relative; padding-left: 56px; }
.glesson-card .g-no {
  position: absolute; left: 14px; top: 13px;
  font-family: var(--serif); font-style: italic; font-size: var(--t-2);
  color: var(--steel); transition: color 160ms var(--e-std, ease);
}
.glesson-card:hover .g-no { color: var(--signal-ink); }
/* an article already read files itself: quieter face, a mark in the corner */
.glesson-card .g-mark { position: absolute; right: 12px; top: 10px; color: var(--signal-ink); font-weight: 700; }
.glesson-card.g-read { background: color-mix(in srgb, var(--sheet) 55%, var(--galley)); }
.glesson-card.g-read .gru { color: color-mix(in srgb, var(--carbon) 72%, var(--galley)); }
.gex {
  border-radius: 0; border: none; border-left: 3px solid var(--carbon);
  background: var(--galley); padding: 12px 16px; margin: 12px 0;
}
.gex mark { background: transparent; color: inherit; box-shadow: inset 0 -2px 0 var(--signal); }
.gex .gru-s { font-family: var(--serif); font-size: var(--t-2); }
.gex .gen-s { font-family: var(--serif); font-style: italic; color: var(--steel); }
.gex .gnote { font-size: var(--t-00); color: var(--steel); letter-spacing: .04em; margin-top: 4px; }
.gchip {
  border-radius: 0; border: 1px solid var(--carbon);
  background: var(--galley); color: var(--carbon);
}
#lesson-body p { font-family: var(--serif); font-size: var(--t-2); line-height: 1.65; max-width: 62ch; }
#v-lesson h2 { font-family: var(--bill); text-transform: uppercase; letter-spacing: .04em; }


/* ===== 29-alive.css ===== */
/* ============================================================================
   29-alive.css — THE WHOLE HOUSE IS ALIVE, by one law.

   The lesson room learned to breathe (weather, blooms, foley, ink under the
   hand); this sheet extends the same nervous system to every view, so the
   product is ONE living building rather than one living room in a dead one.
   Nothing here invents a new vocabulary: it is the print-shop's physics —
   sheets laid, ink seeping, paper answering the hand — applied everywhere,
   under the same charter: decelerations only, nothing bounces, nothing moves
   at friction, the reader's motion setting governs all of it.
   ============================================================================ */

/* ---- THE CURTAIN. The first thing anyone ever sees: warm paper, the
   master lockup, three ink dots filling on the house rhythm. It hides the
   dataless shell entirely and lifts in one decelerating breath when boot
   is ready; a 12-second CSS failsafe guarantees it can never stick. ---- */
#curtain {
  position: fixed; inset: 0; z-index: 400;
  display: grid; place-content: center; gap: 20px;
  background: var(--room);
  transition: opacity 450ms var(--e-std, ease), visibility 450ms;
  animation: curtain-failsafe 1ms linear 12s forwards;
}
body.booted #curtain { opacity: 0; visibility: hidden; }
@keyframes curtain-failsafe { to { opacity: 0; visibility: hidden; } }
.curtain-mark {
  display: grid; justify-items: center;
  font-family: var(--bill); text-transform: uppercase;
  letter-spacing: .3em; font-size: clamp(15px, 2.6vw, 22px);
  color: var(--carbon);
}
.curtain-mark .cm-t {
  font-family: var(--serif); font-style: italic; text-transform: none;
  letter-spacing: .05em; font-size: .82em; margin: 3px 0;
  color: var(--signal, #f6c445);   /* the hinge prints signal: gold-leaf is
                                      ceremony foil now (concordat clause c) */
}
.curtain-dots { display: flex; gap: 11px; justify-content: center; }
.curtain-dots i {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--carbon); opacity: .22;
  animation: curtain-dot 1.9s ease-in-out infinite;
}
.curtain-dots i:nth-child(2) { animation-delay: .32s; }
.curtain-dots i:nth-child(3) { animation-delay: .64s; }
@keyframes curtain-dot { 0%, 100% { opacity: .22; } 30% { opacity: 1; } }

/* ---- EVERY VIEW ARRIVES AS A SHEET LAID. One entrance, everywhere, so
   moving through the house feels like turning pages of one book rather than
   switching screens. The reader view keeps its own overture and is exempt:
   nothing may move beside running text. ---- */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .view.show:not(#v-read) {
    animation: alive-sheet 300ms var(--e-std, ease) both;
  }
  @keyframes alive-sheet {
    from { opacity: 0; transform: translateY(7px); }
    to   { opacity: 1; transform: none; }
  }
}

/* ---- THE INK ANSWERS THE HAND, everywhere. Buttons and standing cards
   seep toward ink on hover and settle back when the hand leaves — a
   transition, never an animation, so it follows the reader exactly. The
   word-tokens of the reading page are untouched. ---- */
.btn { transition: background 160ms linear, border-color 160ms linear,
                   transform 160ms var(--e-std, ease); }
.btn:not(:disabled):hover { transform: translateY(-1px); }
.btn:not(:disabled):active { transform: translateY(0); transition-duration: 60ms; }
.card, .tn-keys button, .seals .seal {
  transition: border-color 180ms linear, box-shadow 180ms var(--e-std, ease);
}
.tn-keys button:hover { border-color: var(--carbon); }

/* ---- THE HERE-MARKS BREATHE, one per surface, on the house tempo. The
   road's current book already does; the desk's begin-button ring joins it —
   the single slow pulse that says "this is where you are" without a word.
   One heartbeat per page, never two. ---- */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) #tn-begin,
  body:not(.motion-low) #btn-resume {
    animation: alive-here 8s ease-in-out infinite;
  }
  @keyframes alive-here {
    0%, 92%, 100% { box-shadow: 0 0 0 0 transparent; }
    96%           { box-shadow: 0 0 0 4px color-mix(in srgb, var(--signal) 35%, transparent); }
  }
}

/* ---- NUMBERS SETTLE WHEN THEY CHANGE. The bill's counts are the desk's
   truth; when a view repaints them they arrive as set type, not as swapped
   pixels. Class is added by the renderers that know a value moved. ---- */
@media (prefers-reduced-motion: no-preference) {
  .num-settle { animation: alive-num 260ms var(--e-std, ease) both; }
  @keyframes alive-num {
    from { opacity: 0; transform: translateY(-5px); }
    to   { opacity: 1; transform: none; }
  }
}


/* ---- THE RAIL MARKS THE CURRENT ROOM WITH LIFE: the active tab carries an
   ink underline that DRAWS on arrival — one decelerating stroke, the pen
   marking where you now are. ---- */
.nav-tabs button { position: relative; }
.nav-tabs button.active::after {
  content: ''; position: absolute; left: 9px; right: 9px; bottom: 2px;
  height: 2px; background: var(--verm, var(--signal));
  transform-origin: 0 50%;
}
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .nav-tabs button.active::after {
    animation: nav-draw 260ms var(--e-std, ease) both;
  }
  @keyframes nav-draw { from { transform: scaleX(0); } }
}
/* an unvisited door answers the hand: a carbon rule seeps under the tab on
   hover — the same pen, a lighter touch, so the rail feels alive before
   it is pressed. A transition, never an animation: it follows the hand. */
.nav-tabs button:not(.active)::after {
  content: ''; position: absolute; left: 9px; right: 9px; bottom: 2px;
  height: 2px; background: var(--carbon); opacity: .28;
  transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 200ms var(--e-std, ease);
}
.nav-tabs button:not(.active):hover::after { transform: scaleX(1); }


/* ============================================================================
   THE TITLE PAGE KNOWS THE HOUR. body[data-daypart] tints the masthead's
   paper like the room's own light — cool at morning, plain at day, lamplit
   at evening, deep at night. Paper neutrals only; the change is the slow
   kind a room actually makes.
   ============================================================================ */
.masthead { transition: background 1200ms var(--e-std, ease); }
/* the hours are told in the room's own materials — cool steel at morning,
   a breath of the signal ink at lamplight, deeper carbon at night — never
   a borrowed blue or amber. Strength the eye actually receives: the 7%
   version was measured against a still frame and read as nothing. */
body[data-daypart="morning"] .masthead { background: color-mix(in srgb, var(--steel) 11%, transparent); }
/* the masthead follows the ground: the room's ink deepening, not gold */
body[data-daypart="evening"] .masthead { background: color-mix(in srgb, var(--play, var(--lazur)) 12%, transparent); }
body[data-daypart="night"]  .masthead { background: color-mix(in srgb, var(--carbon) 13%, transparent); }

/* THE WHOLE ROOM KNOWS THE HOUR, not only the title page. The room's light
   is mixed from its own materials and arrives over more than a second —
   the slow kind of change a real room makes when the sun moves. This is
   the single largest "it knows me" a still frame can carry: morning cool,
   evening lamplit, night deep. Day stays the plain paper it should be.

   RECAST 2026-08-22 (cascade concordat, clause b). The hour and the standing
   patina are declared as TOKENS here — --daypart-ink/--daypart-mix and
   --patina/--lived — and the ground colour is composed from them in ONE
   background-color expression. The attribute rules below no longer declare
   `background` at all: when they did, their (0,1,1) specificity beat the
   arcade's plain `body` grid rule and wiped the grid paper off the page at
   morning, evening and night, while the arcade's later plain rule killed the
   patina at day. Now 90-chromatic's mends tail composes the same expression
   UNDER its grid background-image, so every hour keeps the grid and every
   hour keeps its tint. This sheet still paints the composed colour itself so
   the room works even where the arcade sheet is absent. */
body {
  --patina: 0%;
  /* THE PATINA STOPPED ERASING THE ROOM (2026-08-25, measured in OKLCH).
     It printed --signal, on the reasoning that gold is what a lived-in room
     acquires. The arithmetic says otherwise: --room sits at hue 271 and
     --signal at hue 87 -- 176 degrees apart, all but opposite. Mixing
     opposite hues does not warm a colour, it CANCELS it. Measured, mixing
     gold into this room by degrees:

         No0  0%    #f7f9ff   C 8.3   H 271   the room, blue
         No2  3%    #f7f7f9   C 2.6   H 286
         No3  4.5%  #f7f7f7   C 0.0   --      a dead neutral grey
         No5  7.5%  #f7f5f1   C 5.7   H  85   warm, wrong side of the wheel

     So the reward for using the product was that the room lost its colour and
     then changed families. At rank three the ground is literally achromatic.
     That is the "somehow not matching" a reader feels without being able to
     name it: the paper stops belonging to the same palette as the navy
     standing on it.

     The patina now prints --rule-room (hue 263), which is IN the room's own
     family, so ageing DEEPENS the paper instead of neutralising it. Gold is
     returned to what the law reserves it for -- celebration and illumination,
     never a surface this large. */
  --lived: color-mix(in srgb, var(--rule-room) var(--patina), var(--room));
  --daypart-ink: transparent;
  --daypart-mix: 0%;
  transition: background 1600ms var(--e-std, ease);
  background: color-mix(in srgb, var(--daypart-ink) var(--daypart-mix), var(--lived));
}
/* THE DAY IS A LIGHTNESS ARC, NOT A HUE ARC. Light through a window changes
   how much of a room you can see, not what colour its paper is; a page does
   not turn yellow at six o'clock. Morning took --steel and night --carbon --
   both in the room's family -- while evening took --signal, so one hour in
   three swung the largest surface in the product across the wheel. Evening
   now takes the room's own ink and simply sits deeper. Measured, the whole
   matrix stays at hue 264-271 with chroma 8-17 (paper's ceiling is --rule at
   22) and text never falls below 10.9:1. */
body[data-daypart="morning"] { --daypart-ink: var(--steel);  --daypart-mix: 5%; }
body[data-daypart="evening"] { --daypart-ink: var(--play, var(--lazur)); --daypart-mix: 8%; }
body[data-daypart="night"]   { --daypart-ink: var(--carbon); --daypart-mix: 14%; }

/* ============================================================================
   THE ROOM GROWS WITH THE READER — body[data-standing], set from the Table
   of Ranks. The finding this rests on (Duolingo's gold streak): a purely
   visual mark of standing, paying nothing, measurably moves behaviour.
   The shell takes gold patina by degrees; the title-page ornament gilds at
   the second rung; the running tab's rule at the third. Nothing unlocks —
   the room simply looks lived-in by exactly how much it has been.
   ============================================================================ */
body[data-standing="1"] { --patina: 1.5%; }
body[data-standing="2"] { --patina: 3%; }
body[data-standing="3"] { --patina: 4.5%; }
body[data-standing="4"] { --patina: 6%; }
/* the standing marks print --signal, not --gold-leaf: gold is rationed to
   seal / ceremony / finished-spine (concordat clause c), and signal is the
   identical hex these marks always rendered, so nothing moves on screen */
/* the ❦ on the title page's rule takes the reward ink from the second rung */
body[data-standing="2"] .masthead .rule span,
body[data-standing="3"] .masthead .rule span,
body[data-standing="4"] .masthead .rule span { color: var(--signal); }
/* from Titular Councillor, the active tab's underline is gilt */
body[data-standing="3"] .nav-tabs button.active::after,
body[data-standing="4"] .nav-tabs button.active::after { background: var(--signal); }
/* at the top of the table, hairlines throughout the shell warm to brass.
   The other half of that mix was #6e6b58 — an olive-brown from the retired
   green-grey family, matching no token in any room. Because it was a literal
   it did not move with the light, so the reader at the top rung got the SAME
   olive hairlines in the near-white room, the midnight board and the lamp
   room: the one rung where the shell is most itself was the one rung where
   its structure stopped agreeing with its ground. --steel is the metadata ink
   each room already mints for itself (#46536f · #c4cde2 · #554b3d), so the
   brass now takes a second term that moves with the light instead of standing
   still in one olive. It replaces the room's hairline rather than warming it:
   --rule-room's own value takes no part in the mix, so re-grinding that token
   for a room will not move what this rung draws. */
body[data-standing="4"] { --rule-room: color-mix(in srgb, var(--signal) 40%, var(--steel)); }
.almanac .al-more { display: block; opacity: .75; margin-top: 2px; }

/* ============================================================================
   THE READER'S MARGINS BELONG TO THE SCENE. Two static blooms of the
   instalment's own pigment stand at the paper's edges — laid once when the
   scene opens, then perfectly still, because nothing may move beside
   running text. Gone below 1200px, where there is no margin to colour.
   ============================================================================ */
#v-read { position: relative; }
/* A WATERMARK, NOT A STAIN. The container used to carry a soft radial wash
   under filter: blur(30px), which blurred the stamp inside it with it — the
   resident's mark rendered as a grey smudge that read as a printing accident.
   Now that the reading room stands on the house's grid paper (90-chromatic
   tail), the wash is gone entirely — gradient over grid read as a smear —
   and the mark is a pressed watermark: CRISP and faint, nothing else. */
#read-blooms .rb {
  position: fixed; top: 14vh; bottom: 14vh; width: min(26vw, 340px);
  pointer-events: none; z-index: 0;
  display: grid; place-items: center;
}
#read-blooms .rb i {
  font-style: normal; font-size: clamp(120px, 15vw, 210px);
  color: var(--ink, var(--carbon)); opacity: .07;
  transform: rotate(-8deg);
}
#read-blooms .rb-l { left: -6vw; }
#read-blooms .rb-r { right: -6vw; transform: scaleX(-1); }
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) #read-blooms .rb { animation: gx-bloom-in 1200ms var(--e-std, ease) both; }
}
@media (max-width: 1199px) { #read-blooms { display: none; } }

/* the scene's ornament and rule take its ink; the running text never does */
#scene-head .orn { color: var(--ink, var(--carbon)); }
#scene-head h2 { position: relative; z-index: 1; }

/* true marginalia: on a wide page the wisdom note steps into the margin
   beside the scene head, where a reader's pencil would put it */
@media (min-width: 1280px) {
  .margin-note {
    position: absolute; left: calc(50% + 18em + 24px); top: 120px;
    width: 230px; text-align: left; margin: 0;
    border-left: 3px solid var(--ink, var(--carbon)); padding-left: 12px;
  }
}

/* the take-all row: a quiet standing offer, not a banner */
.take-all { text-align: center; margin: 6px 0 14px; }


/* ============================================================================
   EVERY ROOM ITS OWN ATMOSPHERE — the boldness pass. The owner asked where
   the emotion lives; the answer must be visible in a still frame. Each view
   takes its own paper: the desk warm with the room's vermilion in the air,
   the Words case sepia like an opened drawer, Practice a lamplit night desk,
   Journey the dusk of an almanac. Washes, not themes — the text plane stays
   paper — but washes you can SEE.
   ============================================================================ */
/* one ink, five strengths: each room is a different pressure of the same
   signal or a neutral of the house — no blue dusk, no green study */
/* THE SHEET IS AN OBJECT. The prose used to sit on a flat cream rectangle
   flush with the room; a page a reader holds has an edge and a weight. One
   hairline, one deep soft shadow, and the book's own ink across its head —
   the same pigment that colours its drop cap, its shelf band, its bill line. */
#v-read #text {
  border: 1px solid var(--edge);
  border-top: 4px solid var(--book-ink, var(--carbon));
  box-shadow: 0 1px 0 rgba(255,255,255,.5) inset, 0 14px 44px -18px rgba(20,16,8,.4);
}

/* the desk carries NO wash of its own: its masthead plate is opaque from the
   fold down, so a top gradient could only ever paint the sliver above it — a
   pink seam between the nav and the plate, read as a printing error. The
   desk's atmosphere is the daypart light on the body, which is continuous. */
#v-words.show   { background: linear-gradient(180deg, color-mix(in srgb, var(--steel) 9%, transparent), transparent 340px); }
#v-review.show  { background: radial-gradient(ellipse 90% 62% at 50% 30%, transparent 30%, color-mix(in srgb, var(--carbon) 12%, transparent) 100%); }
#v-journey.show { background: linear-gradient(180deg, color-mix(in srgb, var(--signal) 4%, transparent), transparent 340px); }
/* the road under lamplight: warm gold falling from the top of the room —
   the one view that must feel like an invitation, not an archive */
#v-course.show  { background: linear-gradient(180deg,
  color-mix(in srgb, var(--signal, #f6c445) 8%, transparent),
  color-mix(in srgb, var(--rule) 30%, transparent) 220px,
  transparent 460px); }

/* THE LAMPLIT CARD. Practice is a night desk: the room recedes and the one
   sheet under your hands holds the light — always, not only in the weather's
   steady state. The single strongest change a still frame can carry. */
#v-review #rev-card, #rev-card {
  /* THE LAMP, AS A VARIABLE. This wrote box-shadow outright and lost every
     time to the acts and the heat, so the lamp was never once lit. It names
     its own layer now, and the assembling rule in 90-chromatic-game.css
     stacks it under the slab. */
  --rev-lamp: 0 1px 0 rgba(255,255,255,.5) inset,
              0 10px 40px -12px rgba(20, 16, 8, .35);
  /* the card wears its sentence's book: a band of that pigment along its
     head, changing card to card — the sitting's own variation, told in the
     shelf's own inks (set as --ink by nextCard) */
  border-top: 4px solid var(--ink, var(--carbon));
  transition: border-top-color 350ms linear;
}
#v-review .rev-head { opacity: .82; }

/* ENTRANCES YOU CAN FEEL. The sheet-lay was seven invisible pixels; a page
   being laid on a desk travels further and lands with more weight — still
   one deceleration, still nothing at friction. */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .view.show:not(#v-read) {
    animation: alive-sheet-2 380ms var(--e-std, ease) both;
  }
  @keyframes alive-sheet-2 {
    from { opacity: 0; transform: translateY(16px) scale(.995); }
    to   { opacity: 1; transform: none; }
  }
}

/* THE HAND FEELS THE PRESS. A primary button is a plate that inks: hover
   deepens it, press pushes it into the paper. Tactility is emotion. */
.btn.primary { box-shadow: 0 2px 0 color-mix(in srgb, var(--carbon) 60%, transparent); }
.btn.primary:not(:disabled):hover { box-shadow: 0 3px 0 color-mix(in srgb, var(--carbon) 60%, transparent); }
.btn.primary:not(:disabled):active { box-shadow: 0 0 0 transparent; transform: translateY(2px); }

/* THE ROAD'S BANDS ANSWER THE HAND WITH THEIR WHOLE BODY: a chapter band
   lifts a hair and its ornament tips like a label being read. */
.rd-ch { transition: transform 200ms var(--e-std, ease), box-shadow 200ms var(--e-std, ease); }
@media (hover: hover) {
  .rd-ch:hover { transform: translateY(-2px); box-shadow: 0 6px 18px -10px rgba(20,16,8,.4); }
  .rd-ch:hover .rd-orn { transform: rotate(-8deg); }
}
.rd-orn { transition: transform 220ms var(--e-std, ease); display: inline-block; }

/* dark theme: the atmospheres invert into deeper nights, never grey */
[data-theme="dark"] #v-review.show  { background: radial-gradient(ellipse 90% 62% at 50% 30%, transparent 25%, rgba(0,0,0,.5) 100%); }


/* ---- THE JOURNEY IS AN ALMANAC. A month is a spread: small caps, a rule
   running off the page, a printer's diamond at its station on the spine. ---- */
.tl .tl-month { display: flex; align-items: center; gap: 12px; position: relative;
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .22em;
  font-size: .8rem; color: var(--steel); margin: 30px 0 14px; }
.tl .tl-month::after { content: ''; height: 1px; background: var(--rule); flex: 1; }
.tl .tl-month::before { content: ''; position: absolute; left: -26.5px; top: .3em;
  width: 7px; height: 7px; transform: rotate(45deg);
  background: var(--room); border: 1px solid var(--carbon); }

/* THE RECORD SETTLES: each day laid down in turn — fill:backwards so a row
   owns its opacity again the moment the entrance is spent */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .tl > * {
    animation: tl-settle calc(420ms * var(--motion, 1)) var(--e-std, ease) backwards;
  }
  @keyframes tl-settle { from { opacity: 0; transform: translateY(7px); } }
}

/* ============================================================
   THE ROAD DOOR — the resident's call (scene-end, lesson-virgins only).
   The introduction to the whole learning half of the product, so it is
   dressed as a ceremony: the book's own figure beside the seal, its lines
   settling in one at a time on the brand breath, the card wearing the
   scene's mood. Everything here is ink, paper and steam; nothing bounces,
   and every animation is opacity/transform for the compositor. The mood
   washes are ink density and paper warmth only — palette law, no second
   hue; the one warm glint (happy/proud) is gold at 8%, celebration's own
   metal. Below 1400px the margin figure never mounts, so for a phone
   reader this card is the first meeting with the resident.
   ============================================================ */
.road-door { position: relative; display: flex; gap: 16px; align-items: flex-start;
  margin: 18px auto 6px; max-width: 640px; padding: 16px 18px 14px;
  background: var(--card); border: 1px solid var(--rule); border-radius: 10px;
  overflow: hidden; text-align: left; transition: box-shadow 1100ms var(--e-std, ease); }
.road-door .rdoor-fig { position: relative; flex: 0 0 84px; width: 84px; height: 84px; margin-top: 4px; }
.road-door .rdoor-fig svg { display: block; overflow: visible; }
.road-door .rdoor-body { flex: 1; min-width: 0; }
.road-door .rdoor-kicker { opacity: .6; margin-bottom: 6px; }
.road-door .rdoor-line { font-family: var(--serif); font-style: italic; margin: 0 0 7px; line-height: 1.45; }
.road-door .rdoor-ghost { margin: 10px 0 2px; font-size: .92rem; }
.road-door .rdoor-ghost .rdoor-q { font-family: var(--serif); }
.road-door .rdoor-ghost .gx-ghost { opacity: .4; letter-spacing: .04em; }
.road-door .rdoor-ghost .rdoor-shy { display: block; font-size: .78rem; opacity: .62; font-style: normal; margin-top: 3px; }
.road-door .rdoor-act { margin-top: 10px; }
@media (max-width: 560px) { .road-door { gap: 10px; padding: 14px; }
  .road-door .rdoor-fig { flex-basis: 64px; width: 64px; height: 64px; } }

/* the lines settle in — ink laid down a stroke at a time, on the breath */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .road-door .rdoor-line,
  body:not(.motion-low) .road-door .rdoor-ghost,
  body:not(.motion-low) .road-door .rdoor-act {
    opacity: 0; transform: translateY(5px);
    animation: rdoor-settle calc(700ms * var(--motion, 1)) var(--e-std, ease) forwards;
    animation-delay: calc(.4s + var(--i, 0) * .85s); }
  @keyframes rdoor-settle { to { opacity: 1; transform: none; } }
}

/* the card wears the scene's mood — depth of ink, warmth of paper, no new hue */
.road-door[data-mood="toska"], .road-door[data-mood="sad"] { box-shadow: inset 0 0 9vmax rgba(24, 19, 10, .14); }
.road-door[data-mood="wary"] { box-shadow: inset 0 0 8vmax rgba(24, 19, 10, .09); }
.road-door[data-mood="happy"], .road-door[data-mood="proud"] { box-shadow: inset 0 0 8vmax rgba(191, 146, 42, .08); }
[data-theme="dark"] .road-door[data-mood="toska"],
[data-theme="dark"] .road-door[data-mood="sad"] { box-shadow: inset 0 0 9vmax rgba(0, 0, 0, .5); }
[data-theme="dark"] .road-door[data-mood="wary"] { box-shadow: inset 0 0 8vmax rgba(0, 0, 0, .36); }

/* the Tooth's ache — the room's own vermilion breathing at the card's edge,
   once per four seconds: the book's fact, not the reader's */
.road-door::after { content: ''; position: absolute; inset: 0; pointer-events: none;
  border-radius: inherit; opacity: 0; }
.road-door[data-who="tooth"]::after { box-shadow: inset 0 0 46px rgba(165, 52, 42, .13); }
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .road-door[data-who="tooth"]::after { animation: rdoor-breath 4s ease-in-out infinite; }
  @keyframes rdoor-breath { 50% { opacity: 1; } }
}

/* the Little Pot simmers — two blurred wisps rising off the lid and thinning
   to nothing, offset so the steam never marches in step */
.road-door .rdoor-steam { display: none; }
.road-door[data-who="topf"] .rdoor-steam { display: block; position: absolute;
  left: 50%; top: -4px; width: 0; height: 0; pointer-events: none; }
.road-door[data-who="topf"] .rdoor-steam::before,
.road-door[data-who="topf"] .rdoor-steam::after { content: ''; position: absolute; bottom: 0;
  width: 9px; height: 26px; border-radius: 45% 55% 50% 50% / 60% 60% 40% 40%;
  background: currentColor; opacity: 0; filter: blur(3px); }
.road-door[data-who="topf"] .rdoor-steam::before { left: -12px; }
.road-door[data-who="topf"] .rdoor-steam::after { left: 4px; height: 20px; }
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .road-door[data-who="topf"] .rdoor-steam::before { animation: rdoor-steam 4s ease-in-out infinite; }
  body:not(.motion-low) .road-door[data-who="topf"] .rdoor-steam::after { animation: rdoor-steam 4s ease-in-out 1.6s infinite; }
  @keyframes rdoor-steam { 0% { transform: translateY(8px) scale(.7); opacity: 0; }
    30% { opacity: .22; } 100% { transform: translateY(-18px) scale(1.1); opacity: 0; } }
}

/* ---- THE PAGE WEARS THE SCENE'S WEATHER (see placeMate) — a static wash
   set once at the instalment's door, agreeing with the resident's arc:
   deeper ink where the author grows heavy, a whisper of gold where he is
   light. It cannot move; it can only be. ---- */
#v-read { transition: box-shadow 1100ms var(--e-std, ease); }
/* strengths re-measured against still frames: the .10/.07/.05 originals were
   invisible on every tested panel — a wash nobody receives is not weather */
#v-read[data-bookmood="toska"], #v-read[data-bookmood="sad"] { box-shadow: inset 0 0 22vmax rgba(26, 21, 12, .15); }
#v-read[data-bookmood="wary"] { box-shadow: inset 0 0 18vmax rgba(26, 21, 12, .10); }
#v-read[data-bookmood="happy"], #v-read[data-bookmood="proud"] { box-shadow: inset 0 0 16vmax rgba(191, 146, 42, .08); }
[data-theme="dark"] #v-read[data-bookmood="toska"],
[data-theme="dark"] #v-read[data-bookmood="sad"] { box-shadow: inset 0 0 22vmax rgba(0, 0, 0, .42); }
[data-theme="dark"] #v-read[data-bookmood="wary"] { box-shadow: inset 0 0 18vmax rgba(0, 0, 0, .3); }
[data-theme="dark"] #v-read[data-bookmood="happy"],
[data-theme="dark"] #v-read[data-bookmood="proud"] { box-shadow: inset 0 0 16vmax rgba(191, 146, 42, .04); }


/* ===== 30-choreo.css ===== */
/* ============================================================
   30-choreo.css — the shell's choreography.

   Everything in here is declarative. There is no scroll handler, no
   IntersectionObserver, no requestAnimationFrame and no library: the reveals
   and the reading-progress stick are native scroll timelines, which Chrome
   evaluates on the compositor, and the route moves are View Transitions, which
   the browser runs on snapshots. ui/choreo.js sets three attributes and
   creates one element; nothing else is asked of the main thread.

   THE FIREFOX PATH IS THE REASON THIS IS NOT ScrollTrigger. Firefox ships no
   scroll timelines, so every reveal below is wrapped in
   @supports (animation-timeline: view()). Outside that block the elements have
   no from-state at all — they are simply, permanently, in register. That is a
   finished state, not a blank one, and it costs 0 bytes. ScrollTrigger costs
   44KB to arrive at a worse answer.
   ============================================================ */


/* ============================================================
   1. SCROLL REVEALS — a sort set into the line
   ============================================================

   ONE gesture, used everywhere: the plane arrives from the left, along the
   composing stick, and locks against the line already set. Not fade-and-rise.
   Rise is what every other product does because rise is what a fade needs to
   stop looking like a bug; a compositor's hand moves along the line, so this
   one does too, and it always arrives from the same side so the eye learns it
   in about four seconds and then stops seeing it.

   Restraint is load-bearing here — this runs on every screen, every session,
   for as long as the product exists. So: 14px of travel, one direction, and an
   easing that spends most of the scroll range already finished. The reveal is
   over long before the element reaches reading height. */

@keyframes choreo-set {
  from { opacity: 0; transform: translate3d(var(--set-x, -14px), 0, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}
/* a rule drawing itself, from where it starts toward where it ends */
@keyframes choreo-draw {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}

/* The motion gate is an inherited custom property rather than a repeat of the
   selector list under `body.motion-low`. One declaration switches the whole
   system off, and any selector added later is governed without being
   remembered. `animation-name: none` leaves fill-mode with nothing to apply,
   so the element sits at its own, fully visible, styles. */
body.motion-low { --choreo-reveal: none; --choreo-rule: none; }
/* Law 8: the OS preference sets the DEFAULT, an explicit choice in Settings
   beats it. Motion.setTier() resolves that into body.motion-full / .motion-low,
   so the OS query must stand down whenever the reader has said otherwise. */
@media (prefers-reduced-motion: reduce) {
  body:not(.motion-full) { --choreo-reveal: none; --choreo-rule: none; }
}
/* Law 1, stated as a rule rather than trusted to the selector list: nothing
   inside the reading route may ever acquire a scroll reveal, whatever gets
   added to #v-read later. */
#v-read { --choreo-reveal: none; --choreo-rule: none; }

@supports (animation-timeline: view()) {

  /* WHAT IS REVEALED IS A BOUNDED SET, AND THE BOUND IS THE POINT.
     A view() timeline is not free. Measured here over a fixed 9,000px scroll of
     the desk (375 frames, whole-browser CPU including the compositor):

         0 timelines   2.077s
         9 timelines   2.212s   +0.36 ms/frame
       118 timelines   2.477s   +1.07 ms/frame

     118 was the honest first draft of this list, and 95 of them were the
     Curiosities grid — a wall of small tiles, most of them undiscovered
     placeholders, that grew as the reader read. It was four fifths of the cost
     and the least of the choreography.

     So the reveal set is law 3 applied to scroll: transform the ONE parent,
     not the N children. The panel HOLDING the curiosities arrives as one
     plane, and the tiles arrive with it for one timeline instead of ninety-five.
     Everything still on the list is bounded by something that cannot grow —
     twelve panels of layout, fourteen books, sixteen lessons, an instalment
     path. Nothing keyed to how long the reader has been coming back.

     Deliberately absent, and each for a stated reason:
       .finds-list .find-card — 95 tiles, measured above; its panel reveals
       .tl .tl-day            — one per day of use, unbounded over years. That
                                view gets its spine drawing instead: ONE
                                timeline that improves as the list gets longer
       .memlist li            — unbounded, same argument
       #wordlist li           — thousands of rows, and a data table whose rows
                                slide in one by one is exactly the default
                                gesture this is trying not to be
       .view.show > *         — head.html already owns the direct children of a
                                view (`viewin`). These all sit one level deeper,
                                so the two systems never touch one element. */
  .grid2 .card,
  #shelf .book,
  .path .path-row,
  .path .path-sect {
    animation-name: var(--choreo-reveal, choreo-set);
    animation-fill-mode: both;
    animation-duration: auto;        /* NEVER use the `animation` shorthand here:
                                        it resets duration to 0s, and a
                                        scroll-driven animation with a 0s
                                        duration never advances */
    animation-timing-function: var(--e-enter);
    animation-timeline: view();
    /* Rows and cells finish exactly when they are fully on screen. `entry`
       is the only range that means that, and it is the right one for anything
       that cannot outgrow the viewport. */
    animation-range: entry 0% entry 100%;
  }

  /* PANELS ARE RANGED IN PIXELS, NOT IN PERCENTAGES, AND THIS IS MEASURED.
     A desk card is whatever its contents are: the shelf card renders 1,971px
     tall here and one panel on this desk measured 16,698px. Under a
     percentage range both `entry` and `cover` scale with the subject, so that
     panel would have stayed part-transparent for 4,500px of scrolling — a
     reveal still running while its third screen is being read. A pixel offset
     is height-independent: 320px after the panel's top edge appears, it is in
     register, whether it is 100px tall or ten screens.

     The 320/380 split also stages the desk — the left stick is filled first
     and the right follows a beat behind. Geometric rather than nth-child, so
     it survives every column count and every re-render.

     Both are shorter than the range on the rows nested inside them, so a
     panel is always finished before its own contents start arriving and the
     two reveals never multiply into a double fade. */
  .grid2 .card               { animation-range: cover 0%    cover 320px; }
  .grid2 > :last-child .card { animation-range: cover 60px  cover 380px; }

  /* THE JOURNEY SPINE DRAWS ITSELF.
     .tl carries its timeline as a 2px border-left. A border has no length to
     animate, so the rule becomes a pseudo-element and the border stands down.
     Ranged over `contain`, it fills exactly as the column crosses the screen:
     the ink follows the reader down the page instead of arriving before them. */
  .tl { position: relative; border-left-color: transparent; }
  /* THE RESTING STATE IS THE FINISHED STATE. If scroll timelines are absent —
     every shipping Firefox — this rule is simply a full-height line joining the
     dots, which is what it was before and is a perfectly good state. The
     animation only ever takes it away from that and gives it back. */
  .tl::before {
    content: ''; position: absolute; left: -2px; top: 0; bottom: 0; width: 2px;
    background: var(--rule);
    transform-origin: 50% 0;
    animation-name: var(--choreo-rule, choreo-draw);
    animation-fill-mode: both;
    animation-duration: auto;
    animation-timing-function: linear;   /* a rule is a measurement; easing it
                                            would make it lie about position */
    animation-timeline: view();
    /* `contain` does not BEGIN until the whole element fits inside the
       viewport, so on any timeline taller than the screen the rule sat at
       scaleY(0) — no spine at all, permanently, at rest. Measured: 8 days of
       reading gave 0.000 at the top of the page, and 5 days gave 0.488 on a
       page that only scrolls 96px, so the reader could never finish drawing
       it. Before this it was a plain full-height border joining every dot.
       `entry`..`exit` starts as the column enters and completes as it leaves,
       which is what the eye expects and what works at every length. */
    animation-range: entry 0% exit 100%;
  }
}


/* ============================================================
   2. ROUTE MOVES — the room is one place, seen from different angles
   ============================================================ */

@supports (view-transition-name: none) {

  /* The bar is the same object in every room, so it is captured on its own and
     it does NOT move. Holding it perfectly still while the page slides under it
     is what proves it is the same bar; sliding it too would make it a picture
     of a bar in a cross-fade. */
  /* body > header, NOT bare header: the views' own room-heads are <header>
     elements too, and two visible elements sharing one view-transition-name
     abort EVERY route transition with an InvalidStateError — the whole
     choreography fell back to a cut on each nav until this was scoped. */
  body > header { view-transition-name: choreo-bar; }

  /* THE VIEWIN COLLISION, AND WHY THE FIX IS A NEGATIVE DELAY.
     head.html gives every direct child of a view `animation: viewin 420ms both`,
     whose first frame is opacity 0. A View Transition captures the new page at
     the first rendering opportunity after the update callback — which is the
     frame viewin starts on. The snapshot was therefore taken of a page at
     opacity 0, and every route change cross-faded to a blank rectangle and then
     cut to the content. (app.js:721 documents having hit exactly this with the
     home overture.)

     A negative delay parks viewin at its END for the length of the transition,
     so the snapshot is of the finished page. It is deliberately not
     `animation: none` — removing an animation and restoring it makes it RESTART
     when the marker clears — and deliberately not an !important opacity, which
     would fight sequences.js for the elements the overture legitimately holds
     at zero. viewin has no `to` block, so its final frame IS the element's own
     computed style: whatever gsap is holding stays held.

     Keyed on data-vt-to rather than data-vt: the marker that says "a transition
     is running" is the destination, not the direction. Turning to the next
     instalment has no direction and still needs its snapshot to contain a
     page. */
  :root[data-vt-to] .view.show > * { animation-delay: -1s !important; }

  /* Every named group — today that is the bar and the book being opened. Its own
     rule because `::view-transition-group(*)` is newer than the rest of this
     file, and a browser that drops the selector should lose only this
     refinement and keep everything else. Ordered BEFORE the (root) rule so that
     if an engine scores `*` and a name equally, the name still wins: the object
     travelling should land a beat AFTER the page it lands on, not with it. */
  ::view-transition-group(*) {
    animation-duration: calc(380ms * var(--motion));
    animation-timing-function: var(--e-std);
  }
  ::view-transition-group(root) {
    animation-duration: calc(360ms * var(--motion));
    animation-timing-function: var(--e-std);
  }

  :root[data-vt]::view-transition-old(root),
  :root[data-vt]::view-transition-new(root) {
    /* The UA cross-fade blends both halves with plus-lighter, which is right
       for a dissolve of one image into another and wrong for two pages passing
       each other — under it the overlap blows out. Timing for these two is set
       below, once, alongside the keyframes it belongs to. */
    mix-blend-mode: normal;
  }

  /* Going deeper: the new plane slides in from the right and locks; the one
     behind it gives a little ground. Coming back reverses it. Sideways moves
     between sections of equal rank get a short vertical set instead, because a
     horizontal slide would claim a depth that is not there.

     THE INK IS A SEPARATE, MUCH SHORTER ANIMATION THAN THE TRAVEL, AND THAT
     SPLIT CAME OUT OF A FROZEN FRAME. With one keyframe set carrying both, the
     150ms frame of desk→book was two whole pages printed over each other at
     roughly half strength — a dissolve with a drift in it, not a move.

     The obvious fix, sliding opaque planes with no fade at all, is not
     available: a root snapshot is TRANSPARENT wherever the document background
     shows, because the root background is propagated to the canvas and the
     canvas sits below the whole transition. There is no opaque plane to push.

     What that leaves is the right answer anyway. The room — the canvas — never
     moves. Only what is SET on it travels, and the two pages overlap for about
     110ms at the start instead of for the whole move, so two thirds of the
     transition is the new page alone, sliding into register on a ground that
     was never in question. */
  @keyframes choreo-page-in-fwd   { from { transform: translate3d(3.4%, 0, 0); } }
  @keyframes choreo-page-out-fwd  { to   { transform: translate3d(-2%, 0, 0); } }
  @keyframes choreo-page-in-back  { from { transform: translate3d(-3.4%, 0, 0); } }
  @keyframes choreo-page-out-back { to   { transform: translate3d(2%, 0, 0); } }
  @keyframes choreo-page-in-lat   { from { transform: translate3d(0, 1.4%, 0); } }
  @keyframes choreo-page-out-lat  { to   { transform: none; } }
  @keyframes choreo-ink-in        { from { opacity: 0; } }
  @keyframes choreo-ink-out       { to   { opacity: 0; } }

  :root[data-vt="in"]::view-transition-new(root)       { animation-name: choreo-page-in-fwd, choreo-ink-in; }
  :root[data-vt="in"]::view-transition-old(root)       { animation-name: choreo-page-out-fwd, choreo-ink-out; }
  :root[data-vt="out"]::view-transition-new(root)      { animation-name: choreo-page-in-back, choreo-ink-in; }
  :root[data-vt="out"]::view-transition-old(root)      { animation-name: choreo-page-out-back, choreo-ink-out; }
  :root[data-vt="lateral"]::view-transition-new(root)  { animation-name: choreo-page-in-lat, choreo-ink-in; }
  :root[data-vt="lateral"]::view-transition-old(root)  { animation-name: choreo-page-out-lat, choreo-ink-out; }

  :root[data-vt]::view-transition-new(root),
  :root[data-vt]::view-transition-old(root) {
    animation-duration: calc(360ms * var(--motion)), calc(130ms * var(--motion));
    animation-timing-function: var(--e-std), linear;
    animation-fill-mode: both, both;
  }
  /* the arriving page starts taking ink only once the leaving one is mostly
     off — 30ms of delay is the whole difference between a move and a smear */
  :root[data-vt]::view-transition-new(root) { animation-delay: 0s, calc(30ms * var(--motion)); }

  /* ENTERING THE READING ROUTE, THE SHELL GETS OUT OF THE WAY.
     openScene() builds the page, calls show(), and then hands it to
     playSceneOpen() — which sets the scene head and the paragraphs to opacity 0
     synchronously, i.e. before the snapshot. Any transition here would
     therefore be a 360ms slide of an empty sheet, played over the top of the
     real entrance. The route move collapses to nothing and the book's own
     ceremony is the only thing the reader sees. */
  :root[data-vt-to="read"]::view-transition-group(root),
  :root[data-vt-to="read"]::view-transition-old(root),
  :root[data-vt-to="read"]::view-transition-new(root) { animation-duration: 1ms; }
}


/* ============================================================
   3. LIGHTS DOWN — the room settles for the session
   ============================================================

   A STATE, NOT AN AMBIENCE. It changes once on the way in and once on the way
   out, and while the text is on screen nothing in here is running: these are
   token values, and the transition that carries them is the one already
   declared on <body> — 29-alive's 1600ms room-light ease, the winner of the
   body transition cascade.

   RE-POINTED 2026-08-22 (cascade concordat, clause a). This rule used to drop
   the room through the ALIASES (--paper, --canvas, --paper-1), which was right
   when the ground READ those aliases. It no longer does: 90-chromatic seized
   --paper as a literal (#fffdf5, its proof-sheet furniture token) and paints
   the ground itself — `body { background: var(--room) …grid }` at day, and the
   composed daypart/patina ground in its mends tail, which is
   color-mix(daypart-ink, --lived) where 29-alive defines
   --lived = color-mix(gold patina, var(--room)). Every path terminates in
   --ROOM: that is the live ground token, so that is what lights-down rewrites.
   --canvas/--paper-1/--paper-2 still alias --room/--case in head.html and
   follow the dim for free. --paper is deliberately NOT dimmed any more: it is
   arcade furniture (small warm cards), not the ground — cooling it would grey
   the furniture without darkening the room.

   The old rule dodged --room because `--paper: var(--room)` sat on the same
   element and a self-referencing override is a cycle. The mix below references
   only --carbon and --case — no self-reference, no cycle. */

:root[data-lights="down"] {
  --room: color-mix(in srgb, var(--carbon) 6%, var(--case));
}
/* THE STEP IS RE-MEASURED AGAINST THE GROUND REALLY BEHIND THE TEXT.
   The old table was struck on the retired green-grey room (rgb 173,180,171),
   where mix(case 55%, room) took −21.4% of the ground's luminance. The room
   is now near-white #f7f9ff with --case only 10.5% below it, so the same
   recipe now takes −5.8% — a wash, not a step; dimming had died of token
   drift twice over. Re-measured (APCA, srgb mix arithmetic, tests keep the
   invariant) with the scene head's real pairings — carbon title, steel
   byline — straight on the ground:

       step                    ground     Y drop    title Lc   byline Lc
       lights up               #f7f9ff       —        99.3       83.1
       old 55% recipe          #eff3fd     −5.8%      95.6       79.5
       full step (case)        #e8eefc    −10.5%      92.6       76.5
       case + 6% carbon        #dbe2f0    −21.1%      85.5       69.4  ← chosen
       case + 10% carbon       #d3dae9    −27.6%      81.0       64.9

   Case + 6% carbon restores the house figure — about a fifth of the room's
   luminance, a step rather than a wash — and both pairings stay far above
   the palette's floors (Lc 45 headline, 60 non-body). The blue game rail
   above the sheet is navy (--play #244177) in every daypart, so the dimmed
   ground also stops arguing with it: the room steps toward the rail's night
   side instead of floating near-white behind it.

   SEPIA pays more per unit: its case already sits 19.4% below its room, so
   the whole step alone is the fifth (−19.4%, title 72.8, byline 59.3);
   adding carbon there would push the byline under the non-body floor.
   DARK takes the whole step too and it is free there — steel is a LIGHT
   value on a dark ground, so dropping the ground RAISES its contrast
   (title −101.2 → −102.4, byline −74.2 → −75.4). */
:root[data-theme="sepia"][data-lights="down"],
:root[data-theme="dark"][data-lights="down"] {
  --room: var(--case);
}

/* СУРИК COLLAPSES TOWARD INK — in the chrome, and only where red is INK.

   --chrome-ink is mixed from --signal-ink rather than from --signal, and that
   is a polarity decision, not a shade preference. --signal-ink is the red that
   is already legal to read in the current room, so it always sits on the same
   side of the ground as --carbon does: in daylight both are dark, at night both
   are light. Mixing them can never produce a value that crosses the ground.

   FILL ROLES ARE LEFT ALONE, and that is the whole reason this is a short list
   of selectors instead of one blanket --verm override. --verm is a fill in one
   place and ink in another; collapsing it everywhere would have dragged a red
   BUTTON toward carbon while --on-signal kept printing a pale label on it,
   which at night is an unreadable pair. Only the four text uses of red the bar
   actually has are collapsed.

   The first draft of this WAS the blanket override, and it was worse than
   wrong, it was accidentally right: `--verm: color-mix(..., var(--signal), ...)`
   next to `--signal: var(--verm)` is a custom-property CYCLE. Every property in
   a cycle is invalid at computed-value time, so `color` fell back to inherit,
   the wordmark inherited carbon from the body, and the measurement said the red
   had collapsed. It had not. It had been deleted, by an error, and anything
   else in the bar that happened to be coloured from those tokens would have
   been deleted with it. --chrome-ink is declared on :root, where --signal-ink
   still means what it says, so nothing refers to itself.

   Nothing here touches the sheet, and nothing touches #readbar's fill. Red is a
   currency in this product — it marks what changed — and the two places it is
   still doing that job during a reading session are the marks under the words
   and the line reporting how far the reader has got. Draining those would be
   taking the meaning out to get the mood. */
:root[data-lights="down"] {
  --chrome-ink: color-mix(in srgb, var(--signal-ink) 22%, var(--carbon));
}
:root[data-lights="down"] header {
  border-bottom-color: var(--rule-room);
  transition: border-color var(--d-m) var(--e-std), background var(--d-m) var(--e-std);
}
:root[data-lights="down"] header .wm-t,
:root[data-lights="down"] header .brand b,
:root[data-lights="down"] header .nav-tabs button:hover,
:root[data-lights="down"] header .nav-tabs button.active {
  color: var(--chrome-ink);
  transition: color var(--d-m) var(--e-std);
}


/* ============================================================
   4. THE COMPOSING STICK — how far this page has run
   ============================================================

   Not a bar across the top. A rule standing in the room's left gutter, outside
   the sheet, filling downward as the page runs — a stick taking a column of
   type, with a knee at each end.

   #readbar's own fill is NOT replaced and is NOT duplicated here: it reports
   SENTENCES READ, which is a claim about the reader, and it only ever moves
   when they mark one. This reports SCROLL, which is a claim about the page.
   Two different facts; the readbar was measured before anything was touched.

   Legal beside running text because it makes no frames of its own. Its only
   input is scroll offset, so it can only move while the reader is already
   moving the page — the same contract as the scrollbar the OS draws — and it
   stands in the room, off the sheet, where there is nothing to read. */

#choreo-stick {
  position: fixed;
  left: max(14px, calc(50% - 306px));   /* clear of the 34em sheet and its shadow */
  top: 104px; bottom: 104px;
  width: 2px;
  background: var(--rule-room);
  z-index: 20;                          /* under the header (40) and readbar (30) */
  pointer-events: none;
  display: none;                        /* opted INTO below, never out of */
}
/* the knees: a composing stick is a tray with a stop at each end */
#choreo-stick::before,
#choreo-stick::after {
  content: ''; position: absolute; left: -3px; width: 8px; height: 2px;
  background: var(--rule-room);
}
#choreo-stick::before { top: -2px; }
#choreo-stick::after  { bottom: -2px; }

#choreo-stick .fill {
  position: absolute; inset: 0; display: block;
  /* the stick takes the BOOK'S ink — the same pigment as the drop cap and
     the sheet's head, so the rule in the gutter reads as this book's
     instrument rather than a stray line (which is what a tester called it) */
  background: var(--book-ink, var(--carbon));
  transform-origin: 50% 0;
  transform: scaleY(0);
}

@supports (animation-timeline: scroll()) {
  /* Below 1000px there is no gutter to stand it in — the sheet reaches the
     edges — and a rule pressed against the frame of a phone is not part of the
     room, it is a bar stuck to the side. There the readbar and the platform's
     own scrollbar already answer the question. */
  @media (min-width: 1000px) {
    #choreo-stick { display: block; }
  }
  #choreo-stick .fill {
    animation-name: choreo-draw;
    animation-fill-mode: both;
    animation-duration: auto;
    animation-timing-function: linear;
    animation-timeline: scroll(root block);
  }
}

/* Section navigation already animates the live view as one short fade.
   Do not restart staggered child entrances or leave content invisible. */
:root[data-navigation="instant"] .view.show > * {
  animation: none !important;
}


/* ===== 31-stages.css ===== */
/* ============================================================================
   31-stages.css — THE PLATE AND THE ARC'S FACE.

   The ten word shapes carry their own silhouettes (26-games.css: the
   specimen sheet, the type case, the galley strip, the composing stick, the
   manuscript, the sorting board, the dusk). The GRAMMAR SITTING did not: an
   article on aspect and an article on diminutives rendered on the same
   plain sheet as a vocabulary question, and the owner named the result —
   boring. A grammar sitting is a different kind of hour and now looks like
   one: THE PLATE, the engraved figure of an old book, framed twice, its
   quoted line set as a captioned specimen rather than a question floating
   on paper.

   And an arc — a spiral of stations teaching one large thing across many
   chapters (Учение, item 3) — stamps its name on the stage, so each spiral
   wears its own face: the aspect arc's band is a strip of film sprockets
   (the two cameras), the gender arc's band is cut in three (the three
   figures of the tale), the word-order arc's band holds the king's gap at
   seat two. One glyph and one band per arc; no new hue anywhere — every
   color below is the chapter's ink, the paper, or a mix of the two.
   ============================================================================ */

/* ---- THE PLATE: the sitting's sheet framed as an engraving ---- */
.gx-stage[data-gram] .gx {
  position: relative;
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 62%, var(--carbon));
  padding: clamp(26px, 4.5vw, 54px);
}
.gx-stage[data-gram] .gx::before {
  content: ''; position: absolute; inset: 8px; pointer-events: none;
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 30%, transparent);
}
/* the plate's corner marks — the engraver's register, not decoration enough
   to shout: four hairline ticks where the inner frame meets the work */
.gx-stage[data-gram] .gx::after {
  content: '§'; position: absolute; top: -1px; left: 50%;
  transform: translate(-50%, -50%);
  padding: 0 10px; background: var(--galley);
  font-family: var(--serif); font-size: 15px;
  color: color-mix(in srgb, var(--ink, var(--carbon)) 80%, var(--carbon));
}

/* the quoted line inside a sitting's check is a FIGURE with rules above and
   below, centred like a caption under an engraving — not a strip of galley */
.gx-stage[data-gram] .gx-mid[data-kind="gram"] .gx-line {
  text-align: center; padding: 18px 10px;
  background: transparent;
  border-top: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
  font-size: clamp(21px, 2.8vw, 27px); line-height: 1.6;
}
.gx-stage[data-gram] .gx-mid[data-kind="gram"] .gx-ask {
  text-align: center; letter-spacing: .02em;
}

/* ---- THE ARC PLATE: the spiral seen whole, one mark per station ---- */
.gx-arcplate {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .14em; color: var(--steel);
}
.gx-arcplate i {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  border: 1.5px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
}
.gx-arcplate i.gx-a-held {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 75%, var(--carbon));
  border-color: transparent;
}
.gx-arcplate i.gx-a-now {
  border-color: var(--ink, var(--carbon));
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ink, var(--carbon)) 16%, transparent);
}
.gx-arcplate em { font-style: normal; margin-left: 7px; }

/* ---- EACH ARC'S BAND: the door-sweep bar carries the arc's own mark ---- */
/* the two cameras: a film strip — the band perforated like sprocket holes */
.gx-stage[data-arc="aspect"]::before {
  background: repeating-linear-gradient(90deg,
    var(--ink, var(--carbon)) 0 22px,
    color-mix(in srgb, var(--ink, var(--carbon)) 22%, var(--galley)) 22px 28px);
}
/* the three figures of the tale: the band cut in three weights — der, die, das */
.gx-stage[data-arc="gender"]::before {
  background: linear-gradient(90deg,
    var(--ink, var(--carbon)) 0 33.4%,
    color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--galley)) 33.4% 66.7%,
    color-mix(in srgb, var(--ink, var(--carbon)) 78%, var(--carbon)) 66.7%);
}
/* the king on seat two: the band pauses once — the gap the verb never yields */
.gx-stage[data-arc="word-order"]::before {
  background: linear-gradient(90deg,
    var(--ink, var(--carbon)) 0 18%,
    color-mix(in srgb, var(--ink, var(--carbon)) 18%, var(--galley)) 18% 24%,
    var(--ink, var(--carbon)) 24%);
}
/* six jobs, six uniforms: the band cut in six steps of deepening ink */
.gx-stage[data-arc="case"]::before {
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--ink, var(--carbon)) 30%, var(--galley)) 0 16.6%,
    color-mix(in srgb, var(--ink, var(--carbon)) 44%, var(--galley)) 16.6% 33.3%,
    color-mix(in srgb, var(--ink, var(--carbon)) 58%, var(--galley)) 33.3% 50%,
    color-mix(in srgb, var(--ink, var(--carbon)) 72%, var(--galley)) 50% 66.6%,
    color-mix(in srgb, var(--ink, var(--carbon)) 86%, var(--galley)) 66.6% 83.3%,
    var(--ink, var(--carbon)) 83.3%);
}
/* the storyteller's fabric: long scene-paint pierced by event-strikes */
.gx-stage[data-arc="narrative"]::before {
  background: repeating-linear-gradient(90deg,
    color-mix(in srgb, var(--ink, var(--carbon)) 30%, var(--galley)) 0 64px,
    var(--ink, var(--carbon)) 64px 70px);
}
/* the folded clause: the band creases like a pocket knife */
.gx-stage[data-arc="participle"]::before,
.gx-stage[data-arc="partizip"]::before {
  background: repeating-linear-gradient(45deg,
    var(--ink, var(--carbon)) 0 12px,
    color-mix(in srgb, var(--ink, var(--carbon)) 34%, var(--galley)) 12px 24px);
}
/* two actions in one sentence: the doubled rail */
.gx-stage[data-arc="gerund"]::before {
  background:
    linear-gradient(0deg, var(--ink, var(--carbon)) 0 35%, transparent 35% 65%,
      var(--ink, var(--carbon)) 65%),
    color-mix(in srgb, var(--ink, var(--carbon)) 18%, var(--galley));
}
/* the untranslatable dust: fine sprinkle over a quiet wash */
.gx-stage[data-arc="particles"]::before {
  background:
    repeating-linear-gradient(90deg,
      var(--ink, var(--carbon)) 0 3px, transparent 3px 14px),
    color-mix(in srgb, var(--ink, var(--carbon)) 26%, var(--galley));
}
/* the -te machine: the regular tick of the weak past, every notch alike */
.gx-stage[data-arc="lesen"]::before {
  background: repeating-linear-gradient(90deg,
    var(--ink, var(--carbon)) 0 26px,
    color-mix(in srgb, var(--ink, var(--carbon)) 40%, var(--galley)) 26px 32px,
    var(--ink, var(--carbon)) 32px 34px,
    color-mix(in srgb, var(--ink, var(--carbon)) 40%, var(--galley)) 34px 40px);
}
/* the curtain-raiser: es holds seat one, the sentence follows entire */
.gx-stage[data-arc="es"]::before {
  background: linear-gradient(90deg,
    var(--ink, var(--carbon)) 0 9%,
    color-mix(in srgb, var(--ink, var(--carbon)) 24%, var(--galley)) 9% 12%,
    color-mix(in srgb, var(--ink, var(--carbon)) 62%, var(--galley)) 12%);
}
/* the reported word: the line beneath the line — speech carried at one remove */
.gx-stage[data-arc="rede"]::before {
  background:
    linear-gradient(0deg, color-mix(in srgb, var(--ink, var(--carbon)) 45%, var(--galley)) 0 40%,
      transparent 40% 60%, var(--ink, var(--carbon)) 60%),
    var(--galley);
}

/* ---- THE LETTERS' DOOR on the reader's doorstep ---- */
.rd-sprint-door {
  display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap;
  margin: 10px 0 4px; padding: 12px 16px;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--galley));
  border-left: 3px solid var(--ink, var(--carbon));
  font-size: var(--t-0);
}
.rd-sprint-door span { flex: 1 1 260px; }

/* ---- THE SPRINT'S LETTER CASE, on the stage. The road's letter styles
   are scoped to #v-course, and the stage is appended to body — so the
   sprint's letters rendered as bare buttons in a row. The case is ruled
   here for the stage: same compartments, plate-appropriate. ---- */
.gx-stage .cs-letters {
  display: grid; gap: 1px;
  grid-template-columns: repeat(auto-fill, minmax(172px, 1fr));
  background: color-mix(in srgb, var(--carbon) 25%, transparent);
  border: 1px solid var(--carbon);
}
.gx-stage .cs-letter {
  background: var(--galley); padding: 10px 12px; border: 0; border-radius: 0;
  display: grid; grid-template-columns: auto 1fr; gap: 2px 12px;
  align-items: baseline; text-align: left; cursor: pointer;
  font: inherit; color: inherit;
}
.gx-stage .cs-letter:hover { background: color-mix(in srgb, var(--carbon) 6%, var(--galley)); }
.gx-stage .cs-c {
  grid-row: 1 / 3; font-family: var(--serif); font-size: clamp(28px, 4vw, 34px);
  line-height: 1; color: var(--carbon);
}
.gx-stage .cs-say { font-weight: 600; font-size: var(--t-0); }
.gx-stage .cs-lnote { font-size: var(--t-00); color: var(--steel); }
@media (max-width: 620px) {
  .gx-stage .cs-letters { grid-template-columns: 1fr 1fr; }
  .gx-stage .cs-lnote { display: none; }
}

/* ---- THE SPRINT: the decoded word held up to the light ---- */
.gx-stage .sp-word {
  text-align: center; font-family: var(--serif);
  font-size: clamp(38px, 7vw, 64px); letter-spacing: .04em;
  padding: 14px 0 6px; color: var(--carbon);
}
.ab-playrow { margin-top: 10px; }
.ab-held { color: var(--steel); font-size: .8em; letter-spacing: .08em; }

/* ---- THE DIAL: the reader's hand on the sitting's weight ---- */
.rev-dial {
  display: flex; justify-content: center; width: fit-content;
  margin: 10px auto 2px;
  border: 1.5px solid var(--carbon); background: var(--galley);
}
.rev-dial button {
  font: inherit; font-size: var(--t-00); letter-spacing: .12em;
  text-transform: uppercase; padding: 7px 14px; cursor: pointer;
  background: none; border: 0; color: var(--steel);
  border-right: 1px solid color-mix(in srgb, var(--carbon) 35%, transparent);
}
.rev-dial button:last-child { border-right: 0; }
.rev-dial button.on { background: var(--carbon); color: var(--galley); }

/* ---- THE RECAP'S TWO CROWNS (23 Aug): the crossing and the cliffhanger ---- */
/* a crossing is the night's headline — a plate, not a sidebar sentence */
.gx-stage .gx-done .gx-drive-gold {
  text-align: center; padding: 14px 12px; margin: 8px 0;
  border-top: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
  border-bottom: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
  font-family: var(--serif); font-size: clamp(16px, 1.9vw, 19px);
}
.gx-stage .gx-done .gx-drive-gold > b {
  display: block; font-size: 2em; line-height: 1.15;
  color: var(--ink, var(--carbon));
}
/* the ghost line closes the page the way a serial closes a number */
.gx-stage .gx-done .gx-ghostline {
  margin-top: 14px; padding-top: 12px; text-align: center;
  border-top: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 35%, transparent);
  font-size: var(--t-0);
}
.gx-stage .gx-done .gx-ghost-ru {
  display: block; font-family: var(--serif); line-height: 1.5;
  font-size: clamp(22px, 2.6vw, 28px); margin: 8px 0 2px;
}
.gx-stage .gx-done .gx-ghost-shy { display: block; font-style: italic; color: var(--steel); }

/* ---- THE SHELF KNOWS ITS ARCS: a station's card names its spiral ---- */
.glesson-card .g-arc {
  font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .12em; color: var(--steel); margin-top: 4px;
}
.glesson-card[data-arc] {
  border-left: 3px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent);
}

/* ---- THE RANK'S PAPERS: the lore panel behind the patent ---- */
.rl-door { cursor: pointer; }
.rl-door:hover { outline: 2px solid color-mix(in srgb, var(--carbon) 30%, transparent); }
.rl-quote {
  margin: 12px 0; padding: 10px 14px;
  border-left: 3px solid var(--carbon);
  font-family: var(--serif); font-size: var(--t-1);
}
.rl-quote .rl-en { display: block; font-style: italic; color: var(--steel); font-size: var(--t-0); margin-top: 4px; }
.rl-quote .rl-qsrc { display: block; font-style: normal; font-size: var(--t-00); letter-spacing: .08em; color: var(--steel); margin-top: 6px; }
.rl-cast p { font-style: italic; margin: 4px 0; }
.rl-src { font-size: var(--t-00); letter-spacing: .04em; color: var(--steel); margin-top: 10px; }
.rl-ladder summary { cursor: pointer; font-size: var(--t-00); letter-spacing: .1em; text-transform: uppercase; color: var(--steel); margin-top: 8px; }
.rl-row { display: flex; gap: 10px; padding: 2px 0; font-size: var(--t-0); }
.rl-row i { font-style: normal; width: 22px; text-align: right; color: var(--steel); font-variant-numeric: tabular-nums; }
.rl-row.now { font-weight: 700; color: var(--ink, var(--carbon)); }

/* ============================================================
   THE EYE — the door from a difficulty to the article that explains it.

   Quiet by default: a hairline rule, the room's steel, and a single
   glyph. It is offered after a MISS, so it must read as help and never
   as reproach — no red, no alarm, no motion of its own. Open, it unfolds
   the article's first paragraph in place, because a reader mid-sitting
   who is teleported into another room has lost their place.
   ============================================================ */
.gx-eye-wrap { margin: 10px 0 2px; }
.gx-eye {
  display: inline-flex; align-items: baseline; gap: 7px;
  background: none; border: 0; border-bottom: 1px solid var(--rule, rgba(0,0,0,.18));
  padding: 3px 2px; cursor: pointer; color: var(--steel);
  font-size: var(--t-00); letter-spacing: .06em; text-transform: uppercase;
}
.gx-eye i { font-style: normal; font-size: .95em; opacity: .75; }
.gx-eye span { text-transform: none; letter-spacing: .01em; font-size: var(--t-0); }
.gx-eye:hover, .gx-eye:focus-visible { color: var(--ink, var(--carbon)); border-bottom-color: currentColor; }
.gx-eye[aria-expanded="true"] i { opacity: 1; }
.gx-eyed {
  margin-top: 8px; padding: 10px 12px;
  border-left: 2px solid var(--rule, rgba(0,0,0,.18));
  background: rgba(0,0,0,.02);
}
.gx-eyed[hidden] { display: none; }
.gx-eyed-ti {
  font-size: var(--t-00); letter-spacing: .1em; text-transform: uppercase;
  color: var(--steel); margin-bottom: 5px;
}
.gx-eyed p { margin: 0 0 9px; font-size: var(--t-0); line-height: 1.5; }
.gx-eyed-go { font-size: var(--t-00); padding: 4px 11px; }

/* the resident's hint wears the same slip as its other lines, marked only
   so a hint can be told from a greeting when both are on screen */
.gx-mate-hint { font-style: italic; }

/* ============================================================
   THE AUTHORITIES — printed under an article, as a reference book does.
   Names and links only; nothing here is reproduced from a source.
   ============================================================ */
.g-src {
  margin-top: 26px; padding-top: 12px;
  border-top: 1px solid var(--rule, rgba(0,0,0,.18));
}
.g-src-cap {
  font-size: var(--t-00); letter-spacing: .12em; text-transform: uppercase;
  color: var(--steel); margin-bottom: 8px;
}
.g-src-row { margin: 0 0 7px; line-height: 1.45; }
.g-src-t { font-size: var(--t-0); }
.g-src-t a { color: var(--ink, var(--carbon)); text-underline-offset: 2px; }
.g-src-w, .g-src-n { display: block; font-size: var(--t-00); color: var(--steel); }
.g-src-n { font-style: italic; }

/* ============================================================
   THE ARTICLE AS A PAGE — two columns where there is room for two.

   Measured before this existed: at 1440px the prose was 575px wide inside
   a 1274px page, flush left, with ~600px of empty grid beside it, and the
   six corpus cards stacked underneath in the same narrow measure. The page
   read as one undifferentiated ribbon of text down the left edge.

   The prose keeps its measure. A line much over 70 characters is measurably
   harder to read, so «use the empty half» must never mean «set the text
   wider» — it means put something there. The corpus lines go there: the
   rule on the left, the evidence for it on the right, both in view at once.

   Below 1000px the rail is `display: contents`, its children rejoin the
   single stack, and the narrow layout is exactly what it was.
   ============================================================ */
.g-rail { display: contents; }
.g-rail-cap { display: none; }

@media (min-width: 1000px) {
  #lesson-body {
    max-width: none;                 /* the 34em measure moves to .g-prose */
    display: grid;
    grid-template-columns: minmax(0, 34em) minmax(17rem, 1fr);
    column-gap: clamp(2rem, 4vw, 4.5rem);
    align-items: start;
  }
  .g-prose { grid-column: 1; min-width: 0; }
  .g-rail {
    display: block;                  /* becomes a real box again */
    grid-column: 2; grid-row: 1;
    min-width: 0;
    border-left: 1px solid var(--rule, rgba(0,0,0,.14));
    padding-left: clamp(1rem, 2vw, 2rem);
  }
  .g-rail-cap {
    display: block;
    font-size: var(--t-00); letter-spacing: .12em; text-transform: uppercase;
    color: var(--steel); margin: 4px 0 14px;
  }
  /* the cards are the rail's whole job — they carry their own ink already */
  .g-rail .gex { margin: 0 0 14px; }
  /* the colophon sits under the prose, not under the evidence */
  .g-prose .g-src { margin-top: 30px; }
}

/* THE BLOCKS STOP LOOKING ALIKE. Five paragraphs set identically is the
   other half of the complaint, and the opening one is the thesis in every
   article on this shelf — so it is set as a lead. Nothing else changes:
   hierarchy by size and colour, no rules, no boxes, no ornament. */
.g-lead {
  font-size: 1.14em; line-height: 1.5;
  color: var(--ink, var(--carbon));
}
.g-lead::first-line { letter-spacing: .005em; }
#lesson-body .g-prose p + p { margin-top: var(--s4, 14px); }

/* the word taken apart, above the rule — the particular before the general */
.g-wc {
  margin: 0 0 12px; padding: 0 0 11px;
  border-bottom: 1px solid var(--rule, rgba(0,0,0,.16));
}
.g-wc-form {
  font-family: var(--serif, Georgia, serif);
  font-size: var(--t-2); line-height: 1.15; margin-bottom: 3px;
  color: var(--ink, var(--carbon));
}
.g-wc-gloss { font-size: var(--t-0); line-height: 1.4; }
.g-wc-lem, .g-wc-root, .g-wc-kin {
  font-size: var(--t-00); color: var(--steel); line-height: 1.5; margin-top: 2px;
}
.g-wc-kin { font-family: var(--serif, Georgia, serif); letter-spacing: .01em; }

/* ============================================================
   THE ROOM WITH A MARGIN — the sitting, at the width it actually has.

   A 760px sheet in a 1400px room left half the screen doing nothing, the
   resident shrunk into the bottom-right corner, and the arc's stations
   buried inside the sheet. The cure is not decoration: it is putting the
   furniture the room already owns where it can be seen. No score, no
   streak, no prize — the mint stays closed. A figure at proper size, a
   ladder you can see yourself on, and the name of the book you are in.

   Below 1180px every rule here is off and the sitting is the sheet it was.
   ============================================================ */
.gx-margin { display: none; }

@media (min-width: 1180px) {
  .gx-stage[data-gram="on"] {
    --gx-marg: 208px;
    --gx-gap: clamp(28px, 4vw, 68px);
    --gx-sheet: min(760px, 100%);
    grid-template-columns: var(--gx-marg) var(--gx-sheet);
    column-gap: var(--gx-gap);
    justify-content: center; justify-items: stretch;
  }
  .gx-stage[data-gram="on"] > .gx { grid-column: 2; grid-row: 1; }

  .gx-margin {
    display: block; grid-column: 1; grid-row: 1;
    /* THE OFFSET IS DERIVED, NOT GUESSED. The medallion is fixed to the
       viewport at --gx-top and is --gx-marg tall, and the resident's line
       hangs beneath it; a hand-picked padding put the arc's ladder behind
       the figure. Same arithmetic, so they cannot collide again. */
    --gx-top: clamp(96px, 13vh, 150px);
    padding-top: calc(var(--gx-top) + var(--gx-marg) + 74px);
  }

  /* THE RESIDENT LEAVES THE CORNER. It is `position: fixed`, so it is placed
     with the same arithmetic the grid uses — the margin's left edge is half
     the composition back from centre — rather than by guesswork. */
  .gx-stage[data-gram="on"] .gx-mate {
    left: calc(50% - (var(--gx-marg) + var(--gx-gap) + var(--gx-sheet)) / 2);
    right: auto;
    top: clamp(96px, 13vh, 150px); bottom: auto;
    z-index: 3;
    width: var(--gx-marg); height: var(--gx-marg);
  }
  /* its line now has room to sit under the figure rather than beside it */
  .gx-stage[data-gram="on"] .gx-mate-line {
    right: auto; left: 0; bottom: auto; top: 100%;
    margin: 8px 0 0; text-align: left; max-width: var(--gx-marg);
  }

  /* THE SPINE — the arc as a ladder. The station you are on is filled in
     the book's ink; the ones you hold are outlined; the rest are waiting. */
  .gx-sp { display: flex; flex-direction: column; gap: 7px; align-items: flex-start; }
  .gx-sp i {
    width: 26px; height: 3px; background: var(--rule, rgba(0,0,0,.22));
    display: block; transition: width 240ms var(--e-std, ease);
  }
  .gx-sp i.gx-sp-held { background: color-mix(in srgb, var(--ink, var(--carbon)) 55%, transparent); width: 34px; }
  .gx-sp i.gx-sp-now  { background: var(--ink, var(--carbon)); width: 52px; height: 4px; }
  .gx-sp-name {
    margin-top: 12px; font-size: var(--t-00); letter-spacing: .1em;
    text-transform: uppercase; color: var(--steel); line-height: 1.35;
  }
  .gx-margin-book {
    margin-top: 18px; padding-top: 12px;
    border-top: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 30%, transparent);
    font-family: var(--serif, Georgia, serif); font-size: var(--t-0);
    color: var(--ink, var(--carbon)); opacity: .8; line-height: 1.3;
  }

  /* NO INK WASH HERE. One was written and it never painted a pixel: the
     stage's ::before computed correctly — 532px wide, the book's crimson
     at 0.2 alpha — and an opaque-red test at the same selector rendered
     nothing either, so the pseudo is structurally blocked on this stage
     rather than merely too faint. Dead decoration is worse than none, so
     it is gone; the book's pigment reaches the room through the medallion,
     the lit station and the book's name instead, all of which were
     measured and do paint. */
}

@media (min-width: 1180px) {
  /* THE FIGURE MUST GROW WITH ITS MEDALLION. The svg carries no width of
     its own, so at 208px it sat as a thumbnail in the middle of a large
     gold disc — the medallion grew and the resident did not. */
  .gx-stage[data-gram="on"] .gx-mate svg { width: 72%; height: 72%; }
}

/* ============================================================
   THE DOOR HAS TO LOOK LIKE A DOOR.

   Reported: «there never comes the information thing». Two causes. It was
   gated behind a wrong answer (fixed in course.js), and what remained was
   a line of small caps the size of a footnote — quiet past the point of
   being findable. It keeps the house's restraint but it now reads as a
   control: a ruled plate, the mark in the book's ink, and a cue saying
   what it will do.
   ============================================================ */
.gx-eye {
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 34%, transparent);
  border-radius: 2px;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, transparent);
  padding: 7px 12px; gap: 9px;
  align-items: center;
}
.gx-eye i { color: var(--ink, var(--carbon)); opacity: .9; font-size: 1em; }
.gx-eye span { font-size: var(--t-0); color: var(--ink, var(--carbon)); }
.gx-eye-cue {
  font-style: normal; font-size: var(--t-00); letter-spacing: .1em;
  text-transform: uppercase; color: var(--steel); margin-left: 2px;
}
.gx-eye:hover, .gx-eye:focus-visible {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 11%, transparent);
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 60%, transparent);
}
/* the mark turns as it opens — one small rotation, tier-1, no bounce */
.gx-eye i { transition: transform 260ms var(--e-std, ease), opacity 200ms linear; }
.gx-eye[aria-expanded="true"] i { transform: rotate(90deg); }

/* THE DRAWER UNFOLDS rather than appearing. A height animation on content
   of unknown size is a lie, so it is a clip-path wipe with a slight rise:
   cheap, honest, and it reads as a sheet being drawn out. */
@keyframes gx-eyed-open {
  from { opacity: 0; transform: translateY(-5px); clip-path: inset(0 0 100% 0); }
  to   { opacity: 1; transform: none;             clip-path: inset(0 0 0 0); }
}
.gx-eyed:not([hidden]) { animation: gx-eyed-open 300ms var(--e-std, ease) both; }
@media (prefers-reduced-motion: reduce) {
  .gx-eyed:not([hidden]) { animation: none; }
  .gx-eye i { transition: none; }
}

/* THE LADDER WAS INVISIBLE. Measured off the rendered page, the unlit
   stations were 26×3 of a near-white rule on a near-white ground — they
   read as nothing at all. Darker, taller, and the lit one keeps its ink. */
@media (min-width: 1180px) {
  .gx-sp i { height: 4px; background: color-mix(in srgb, var(--carbon) 26%, transparent); }
  .gx-sp i.gx-sp-held { background: color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent); }
  .gx-sp i.gx-sp-now  { height: 6px; box-shadow: 0 1px 0 color-mix(in srgb, var(--ink, var(--carbon)) 30%, transparent); }
}

/* the door as it stands ON the card — set apart from the answers so it
   never reads as one of them, and quieter than the options it sits under */
.gx-ask-eye { margin-top: 14px; }
.gx-ask-eye:empty { display: none; }
.gx-ask-eye .gx-eye { opacity: .85; }
.gx-ask-eye .gx-eye:hover { opacity: 1; }

/* ============================================================
   THE DOOR, ENLARGED AND SPECIFIC.

   «Rather small, and doesn't really analyze the specifics well.» Both
   fixed together: it now carries the FORM'S analysis rather than the
   article's name — «genitive singular», not «The two cameras» for the
   tenth time — and it is set at reading size instead of footnote size.
   ============================================================ */
.gx-eye { padding: 10px 14px; gap: 10px; }
.gx-eye span { font-size: var(--t-1); letter-spacing: .005em; }
.gx-eye i { font-size: 1.05em; }
.gx-eye-cue { display: block; margin-left: auto; }

/* the analysis is the first thing in the drawer and the largest */
.g-wc-an {
  font-size: var(--t-1); line-height: 1.25; margin: 2px 0 4px;
  color: var(--ink, var(--carbon));
  font-variant-caps: all-small-caps; letter-spacing: .06em;
}
/* and the one sentence that is actually useful about it */
.g-wc-rule {
  margin-top: 9px; padding-top: 8px;
  border-top: 1px dotted var(--rule, rgba(0,0,0,.22));
  font-size: var(--t-0); line-height: 1.45; color: var(--carbon);
}

/* the ending, set apart inside the headword — the two letters that carry
   the whole case, which the drawer had described and never pointed at */
.g-wc-end {
  color: var(--ink, var(--carbon));
  border-bottom: 2px solid currentColor;
  padding-bottom: 1px; font-weight: 700;
}
/* why this case is here, in this sentence — the part that transfers */
.g-wc-why {
  margin-top: 9px; padding: 7px 9px;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 7%, transparent);
  border-left: 2px solid color-mix(in srgb, var(--ink, var(--carbon)) 45%, transparent);
  font-size: var(--t-0); line-height: 1.45;
}
.g-wc-why i {
  display: block; font-style: normal; font-size: var(--t-00);
  letter-spacing: .1em; text-transform: uppercase; color: var(--steel);
  margin-bottom: 2px;
}

/* ============================================================
   THE PIGMENTS LIFT IN THIS ROOM TOO — §11's law, applied at its site.

   The book's masstone arrives INLINE: course.js writes --ink on the lesson
   host, app.js writes it on the review card, and an inline custom property
   is the one colour in the product a stylesheet cannot re-declare. So this
   sheet spent it as TEXT colour in eleven places and carried no dark block at
   all. The nine lifted below stand on two grounds, and neither is the galley.
   Seven are on var(--sheet) — #253454 after dark (head.html:275): #rev-card
   is --sheet at 10-components.css:1211-1212, #ov-key .panel at
   66-rooms.css:598-602, and the exercise sheet, which 26-games.css:259 prints
   on var(--galley), is re-declared `background: var(--sheet)` by
   90-chromatic-game.css:1049 at the same specificity and later in the build's
   sorted sheet order (build/build.py:141). There the six pigments come out
   grafit 1.16:1, lazur 1.20:1, bakan 1.29:1, sepia 1.50:1, zelen 1.63:1,
   okhra 2.65:1. The other two, .g-lead and .g-src-t a, are the grammar
   article's prose in #lesson-body, which carries no plate of its own and so
   stands on the room itself, #121d38: 1.57 – 3.57:1. (Of the eleven, the
   margin note has the veil paragraph below to itself, and the eye's hover
   state at line 229 is left raw.) Ink the colour of its paper,
   and not on decoration: the eye's label is the door out of a miss, and
   .g-wc-end is the two letters the whole drawer exists to point at.
   95-coherence §11 lifted the shelf; the room the reader sits in was left.

   Same remedy and the same measured constant, so there is one law and not
   two: mix toward the room's warm off-white in oklab, 45% masstone, so
   madder still reads as madder. Re-measured on the two grounds this sheet
   actually gets: 4.68 – 6.40:1 on --sheet #253454, 6.31 – 8.64:1 on the
   room's #121d38, all clear of the 4.5:1 floor. --sheet is the tighter of
   the two, and grafit at 4.68:1 is where the margin now lives.
   A HIGHER percentage keeps MORE masstone and reads darker — §11
   tried 70% and made it worse. Borders, washes and shadows drawn from
   --ink are left alone: they are tint, not ink.
   ============================================================ */
[data-theme="dark"] .gx-eye i,
[data-theme="dark"] .gx-eye span,
[data-theme="dark"] .g-wc-form,
[data-theme="dark"] .g-wc-an,
[data-theme="dark"] .g-wc-end,
[data-theme="dark"] .g-lead,
[data-theme="dark"] .g-src-t a,
[data-theme="dark"] .rl-row.now,
[data-theme="dark"] .gx-stage .gx-done .gx-drive-gold > b {
  color: color-mix(in oklab, var(--ink, var(--carbon)) 45%, var(--carbon));
}
/* .g-wc-end wants no second rule: its underline is 2px solid currentColor,
   so the ending's mark follows the lifted ink of its own accord. The eye's
   mark keeps its opacity .9, and on --sheet #253454 that veil spends the
   margin: 4.09:1 at grafit, 5.50:1 at okhra, with the four darkest masstones
   under 4.5. It is the ◉ bullet and it is aria-hidden (app.js:8414,
   course.js:5061) — a decoration, not text; the label beside it,
   .gx-eye span, is opaque and carries the reading. */

/* THE MARGIN NOTE IS THE ONE SITE WITH A VEIL OVER IT — AND THE ONE NOT ON
   THE SHEET. .gx-margin is grid-column 1 (line 365), beside the sheet and
   not on it, so its plate is the stage. What the stage is after dark is NOT
   the book's ink wash: 26-games.css:535-537 writes that as a normal
   `background:` shorthand, and 90-chromatic-game.css:1038 overrides it with
   `background-color: #f4f7fb !important`; while place.js holds data-place on
   the stage (written at place.js:421, deleted at :491) 98-place.css:135-139
   paints --pl-floor over that in turn — the SCENE's --pl-ink mixed into
   --room, a different ground in every room. So this one site has no single
   ground and no single measured constant. 35% is chosen, not derived: it is
   the one site whose ink is veiled (opacity .8, line 407), set lighter than
   the other nine so the veil does not eat the lift. On a --pl-floor ground
   that holds. On the bare #f4f7fb it does not — there the lift runs the
   wrong way, 1.4 – 1.7:1 against 3.1 – 5.6:1 for the unlifted ink at the
   same opacity — and which of the two the reader gets is the cascade's
   question, not this comment's. The veil stays; it is the point. */
[data-theme="dark"] .gx-margin-book {
  color: color-mix(in oklab, var(--ink, var(--carbon)) 35%, var(--carbon));
}

/* ============================================================
   THE THREE DESKS (30 Aug): the type tray shared by dictation and
   the anatomy table; the misprint's galley line. Sorts are pills of
   the house's letterpress; a placed sort settles into the line with
   the sheet's own emphasis; the lit sort is remediation, never a
   prize.
   ============================================================ */
.gx-stage .gx-dict-horn { display: flex; align-items: center; gap: 12px; margin: 10px 0 4px; }
.gx-stage .gx-dict-horn em { font-style: italic; font-size: .82rem; opacity: .6; }
.gx-stage .gx-horn-big { width: 56px; height: 56px; border-radius: 50%; display: grid; place-items: center; }
.gx-stage .gx-horn-big svg { width: 26px; height: 26px; }
.gx-stage .gx-setline {
  min-height: 2.2em; margin: 14px 0 4px; padding: 8px 10px 6px;
  display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: baseline;
  border-bottom: 2px solid color-mix(in srgb, var(--carbon) 55%, transparent);
  background: color-mix(in srgb, var(--carbon) 3%, transparent);
}
.gx-stage .gx-dt-set {
  font-style: normal; font-family: var(--reading, var(--serif));
  font-size: 1.12rem; line-height: 1.3;
  animation: gx-dt-settle .28s var(--e-out, ease) both;
}
@keyframes gx-dt-settle {
  from { opacity: 0; transform: translateY(-7px); }
  to { opacity: 1; transform: none; }
}
.gx-stage .gx-tray { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 0; }
.gx-stage .gx-dt {
  font-family: var(--reading, var(--serif)); font-size: 1.02rem;
  padding: 7px 13px; cursor: pointer; color: inherit;
  background: var(--galley); border: 1.5px solid var(--carbon);
  border-radius: 3px; box-shadow: 0 2px 0 var(--press, rgba(0,0,0,.16));
}
.gx-stage .gx-dt:hover { transform: translateY(-1px); }
.gx-stage .gx-dt:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--press, rgba(0,0,0,.16)); }
.gx-stage .gx-dt-gone { visibility: hidden; }
.gx-stage .gx-dt-no { animation: gx-hl-shake .3s ease; }
.gx-stage .gx-dt-lit { outline: 2px solid var(--accent, var(--carbon)); outline-offset: 2px; }
@keyframes gx-hl-shake {
  25% { transform: translateX(-4px); } 75% { transform: translateX(4px); }
}
@media (prefers-reduced-motion: reduce) {
  .gx-stage .gx-dt-set { animation: none; }
  .gx-stage .gx-dt-no { animation: none; outline: 2px solid color-mix(in srgb, var(--carbon) 45%, transparent); }
}
.gx-stage .gx-galley { line-height: 2.1; }
.gx-stage .gx-mis-was { text-decoration: line-through; opacity: .55; }
.gx-stage .gx-an-ph {
  background: color-mix(in srgb, var(--accent, var(--carbon)) 22%, transparent);
  color: inherit; padding: 1px 3px; border-bottom: 2px solid var(--accent, var(--carbon));
}
.gx-stage .gx-an-sent { line-height: 1.7; }


/* ===== 32-atmos.css ===== */
/* ============================================================================
   32-atmos.css — THE WEATHER IN THE MARGINS.

   THE LAW THIS SHEET EXISTS TO KEEP: the column is the reader's, the margins
   are the room's. Nothing here may ever cross onto running text, answer the
   pointer, or move while the reader is deciding something. The physics, the
   scene table and the reasoning all live in ui/atmos.js; this sheet is only
   what each substance LOOKS like once it is standing still.

   ---- HOW A PARTICLE IS BUILT ----------------------------------------------

   Two or three nested elements, and the nesting is the physics:

       <i class="at-snow">            the fall   — vertical, linear
         <b class="at-v">             the swing  — its own period and phase
       <i class="at-leaf">            the fall
         <b>                          the swing
           <b class="at-v">           the tumble — a third, unrelated clock

   Transforms compose by nesting, so no track ever has to know about another,
   and no two clocks are ever in step. `.at-v` — the innermost — is the only
   element that carries a size, a colour or a blur; everything above it is a
   pure transform carrier with no paint of its own. That is also why nothing
   here animates a filter: the blur is applied once, statically, to a thing
   that is thereafter only moved.

   ---- COLOUR ---------------------------------------------------------------

   Everything is drawn in `--carbon`, which is midnight blue on paper and warm
   cream after dark, so a single declaration is correct in both themes and no
   second hue enters the palette. The exceptions are the materials the motion
   charter already names — flame (ember), light (haze, fog, sun, lamp, gloom)
   — and exactly one gold, rationed to the one story whose miracle is falling
   gold.
   ========================================================================== */

/* ---- THE TWO SKIES. The house sky is fixed to the body BELOW every piece of
   fixed furniture the house owns (companion 25, readbar 30, header 40), so
   weather can never stand in front of a control. The stage sky lives inside
   the lesson room, which is itself an opaque overlay at 240. ---- */
#atmos {
  position: fixed; inset: 0; z-index: 2;
  pointer-events: none; overflow: hidden;
  contain: strict;                      /* nothing in here may reflow the page */
  color: var(--carbon);
}
.atmos-stage {
  position: absolute; inset: 0; z-index: 0;
  pointer-events: none; overflow: hidden;
  contain: strict;
  color: var(--carbon);
}
/* The lesson's own FURNITURE keeps the front of the room — and `.pl`, the
   ground the weather falls past (ui/place.js, 98-place.css), is not
   furniture. It is the other half of this layer: the sky is what is IN the
   room and the ground is the room. Left inside this rule it was forced
   `position: relative` at specificity (1,1,0) — which beats the (0,2,0) that
   declares it fixed — and, being in flow with nothing in it and `contain:
   strict` on top, it computed to 0×0. Every gradient, every light and every
   signature the place layer draws was rendering into a box with no area, and
   the only thing reaching the screen was the stage's own background colour.
   The exclusion is the fix, and it is the honest one: both layers named here
   are weather, and neither is ever in the flow. */
#gx-stage > *:not(.atmos-stage):not(.pl):not(.gx-mate) { position: relative; z-index: 1; }
/* The RESIDENT is the third thing here that is not furniture. It is pinned to
   the room's own bottom corner with `position: fixed` (27-route.css: «present,
   never presiding»), and 98-place.css lifts it out of the sheet on a question
   card because the sheet leans and a transform would make it the medallion's
   containing block. Landing here as a direct child, it was caught by this rule
   and forced `position: relative` — which put a 112px figure in the flow at
   the stage's bottom edge, half of it below the fold. Measured at 1280x820:
   x 526, y 776, `bottom: -164px`. It keeps its own position and its own
   z-index (10), both of which it already declares. */

body.motion-low #atmos, body.motion-low .atmos-stage,
body.no-atmos #atmos, body.no-atmos .atmos-stage { display: none; }
@media (prefers-reduced-motion: reduce) { #atmos, .atmos-stage { display: none; } }

/* ---- THE TWO GUTTERS, and the softest edge in the product ----------------

   overflow:hidden is the guarantee in the cascade rather than in arithmetic —
   even a particle with a bad sway amplitude cannot reach the column. But a
   clip alone is a HARD edge: a flake drifting toward the page hit an invisible
   vertical line and was gone, mid-fall, which is the one thing that tells a
   reader they are looking at a rectangle rather than at a room.

   So the clip stays and a mask sits in front of it. Weather thins out as it
   nears the text and is gone before it arrives — the reader never sees a
   boundary because there is nothing at the boundary to see. The outer edge
   fades too, more shallowly, so the window frame is not an edge either, and
   the top and bottom fade because the gutters stand 70px proud of the viewport
   (the BLEED in atmos.js) for the parallax to slide inside.

   Left gutter fades to the RIGHT, right gutter to the LEFT: the page-facing
   side is the one that has to disappear. ---- */
#atmos .at-g, .atmos-stage .at-g {
  position: absolute; top: -70px; bottom: -70px; overflow: hidden;
}
/* The HORIZONTAL fade lives on the gutter — it is what keeps weather off the
   text, and it travels with the gutter harmlessly because a horizontal
   gradient is uniform down its whole height. */
#atmos .at-l, .atmos-stage .at-l {
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 46%, transparent 92%);
          mask-image: linear-gradient(to right, transparent 0, #000 14%, #000 46%, transparent 92%);
}
#atmos .at-r, .atmos-stage .at-r {
  -webkit-mask-image: linear-gradient(to left, transparent 0, #000 14%, #000 46%, transparent 92%);
          mask-image: linear-gradient(to left, transparent 0, #000 14%, #000 46%, transparent 92%);
}
/* The VERTICAL fade belongs to the LAYER, which stays with the window. It used
   to sit on the gutters, and once the gutters started travelling with the
   document the top and bottom fades travelled off the screen with them and the
   sky got a hard horizon again. The fade has to be where the horizon is. */
#atmos, .atmos-stage {
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
          mask-image: linear-gradient(to bottom, transparent 0, #000 7%, #000 93%, transparent 100%);
}

/* ---- THE WEATHER IS IN THE ROOM, NOT ON THE WINDOW ----------------------

   The first version of this translated the gutters a fixed 96px across the
   whole document, which is a parallax garnish rather than weather: the snow
   was still pinned to the window, and a reader scrolling a long instalment
   watched the same flakes hang at the same height for two thousand pixels.
   Weather does not do that. Look up from a page, move down the room, and the
   snow you were looking at stays where it was — you leave it behind.

   So the sky travels the WHOLE scroll range (`--at-run`, published by
   atmos.js, which is the only side that knows how tall the document turned
   out) at 82% of it. The missing 18% is the entire parallax: it is behind the
   page, and things behind you move a little slower than you do.

   Still a scroll-driven animation, so the browser runs it on the compositor
   from the scroll position itself — no scroll listener, no
   requestAnimationFrame, no main-thread work on a surface whose whole job is
   to cost nothing. Where scroll timelines are unsupported the sky simply stays
   with the window, which is what it did before, so nothing depends on it. ---- */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    body:not(.motion-low) #atmos .at-g {
      animation: at-travel linear both;
      animation-timeline: scroll(root block);
    }
    @keyframes at-travel {
      from { transform: translateY(0); }
      to   { transform: translateY(calc(var(--at-run, 0px) * -1)); }
    }
  }
}

/* every particle: absolutely placed, never in flow, never hit-tested */
#atmos i, .atmos-stage i { position: absolute; display: block; opacity: 0; }
#atmos i b, .atmos-stage i b { display: block; }
#atmos i .at-v, .atmos-stage i .at-v { display: block; will-change: transform, opacity; }

/* ---- SNOW. Not a white flake — white is invisible on near-white paper, and
   this room has no pure white anyway. A flake here is a halftone dot: the
   house's ink at low density, softened. It reads as snow because of how it
   falls, not because of what colour it is. ---- */
.at-snow .at-v { width: 6px; height: 6px; border-radius: 50%; background: currentColor; filter: blur(.7px); }
.at-snow.at-far .at-v { width: 3.5px; height: 3.5px; filter: blur(1.4px); }

/* ---- BLIZZARD. The same crystal with six metres a second behind it. It is
   drawn as a short bar rather than a dot because the streak is what the wind
   does to it — the scaleX and the wind angle both come from atmos.js, which
   holds the angle for the whole field so a storm is a storm and not a
   scatter. ---- */
.at-blizzard .at-v { width: 9px; height: 3px; border-radius: 2px; background: currentColor; filter: blur(.5px); }
.at-blizzard.at-far .at-v { width: 6.5px; height: 2.2px; filter: blur(1px); }

/* ---- MOTE. Dust in a shaft of light — the office, the courtroom, the market
   square at noon. ---- */
.at-mote .at-v { width: 3px; height: 3px; border-radius: 50%; background: currentColor; filter: blur(.5px); }
/* under the reading lamp the dust is IN the lamplight, so it warms and there
   is more of it — the one visible reward the lamp has ever paid out */
body.read-lamp #atmos .at-mote .at-v {
  background: color-mix(in srgb, #c8a15e 70%, currentColor); filter: blur(.6px);
}

/* ---- DOWN. Тополиный пух — the poplar fluff that fills a Russian June and
   never quite lands. ---- */
.at-down .at-v {
  width: 7px; height: 7px; border-radius: 50%; filter: blur(1.1px);
  background: radial-gradient(circle at 40% 40%, currentColor, transparent 72%);
}

/* ---- RAIN. Not a drop — a rivulet on a pane you cannot see, which is what a
   window actually looks like from a desk beside it. ---- */
.at-rain .at-v {
  width: 1.5px; height: 26px; border-radius: 1px; filter: blur(.6px);
  background: linear-gradient(180deg, transparent, currentColor 55%, transparent);
}

/* ---- ASH. A fleck of soot off a stove, off an oven, off a fire that has
   already been. Irregular, so a tumble reads as a tumble. ---- */
.at-ash .at-v {
  width: 5px; height: 3.5px; background: currentColor; filter: blur(.3px);
  border-radius: 46% 54% 60% 40% / 55% 45% 55% 45%;
}

/* ---- STEAM. A wisp off something hot. ---- */
.at-steam .at-v {
  width: 12px; height: 30px; background: currentColor; filter: blur(4px);
  border-radius: 45% 55% 50% 50% / 60% 60% 40% 40%;
}

/* ---- EMBER. Flame — the charter's own material, and the one warm exception
   to drawing everything in house ink. A spark carries its own light because a
   spark IS a light source. ---- */
.at-ember .at-v {
  width: 4px; height: 4px; border-radius: 50%; background: #f6b26b;
  box-shadow: 0 0 10px 2px rgba(240, 150, 70, .6);
}

/* ---- STAR. Gold, and therefore rationed: one story, whose miracle is
   falling gold. ---- */
.at-star .at-v {
  width: 3px; height: 3px; border-radius: 50%; background: var(--signal, #f6c445);
  box-shadow: 0 0 9px 2px color-mix(in srgb, var(--signal, #f6c445) 45%, transparent);
}

/* ---- LEAF. Cut in atmos.js as an SVG so it can turn edge-on as it falls,
   which is the entire difference between a leaf and a brown dot going
   downwards. Inked in the BOOK's own pigment — the same ink as that volume's
   drop cap and its band on the shelf. ---- */
.at-leaf .at-v { width: 17px; height: 17px; }
.at-leaf .at-v svg { display: block; width: 100%; height: 100%; }
.at-leaf .at-v svg path.lf { fill: var(--book-ink, currentColor); opacity: .62; }
.at-leaf .at-v svg path.vn { fill: none; stroke: var(--book-ink, currentColor); stroke-width: 1.1; opacity: .34; }

/* ---- BLOOM. Petals. A leaf is autumn and an ending; a petal is a garden in
   June and somebody sixteen years old. «Первая любовь» needed a weather that
   was not a leaf, because nothing in that story has fallen yet. ---- */
.at-bloom .at-v {
  width: 11px; height: 8px; filter: blur(.35px);
  background: var(--book-ink, currentColor);
  border-radius: 60% 40% 55% 45% / 70% 65% 35% 30%;
  opacity: .78;
}

/* ---- HAZE. Sea light at Yalta; the pallor of a white night in June. The one
   member of the set drawn in LIGHT rather than ink, so it lifts the paper
   instead of marking it — and after dark it lifts a great deal less. ---- */
.at-haze .at-v {
  width: 220px; height: 54px; border-radius: 50%; filter: blur(20px);
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, var(--sheet, #fff) 92%, transparent), transparent 70%);
}
[data-theme="dark"] .at-haze .at-v {
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, #8fa6d8 34%, transparent), transparent 70%);
}

/* ---- FOG. Haze's opposite, and deliberately its twin: same air, same
   breeze, same speed — where haze lifts the margin, fog dims it, and a scene
   can turn from one into the other without a word being said about it. ---- */
.at-fog .at-v {
  width: 260px; height: 90px; border-radius: 50%; filter: blur(26px);
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, var(--carbon) 31%, transparent), transparent 68%);
}
[data-theme="dark"] .at-fog .at-v {
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, #7f8ba8 30%, transparent), transparent 68%);
}

/* ---- SUN. Standing weather: one warm shaft laid across the margin for as
   long as the scene lasts, breathing on the tempo of a cloud going over. Warm
   PAPER, not a new hue, which is what keeps a sunlit margin inside the
   palette law. ---- */
.at-sun .at-v {
  width: 240%; height: 150px; margin-left: -70%; filter: blur(30px);
  transform-origin: 50% 50%;
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, #e3bb78 58%, transparent) 42%,
              color-mix(in srgb, #e3bb78 58%, transparent) 58%, transparent);
}
[data-theme="dark"] .at-sun .at-v {
  background: linear-gradient(90deg, transparent,
              color-mix(in srgb, #f0c98a 26%, transparent) 42%,
              color-mix(in srgb, #f0c98a 26%, transparent) 58%, transparent);
}

/* ---- LAMP. The interior answer to the sun: not a shaft through a window but
   a pool of light on a table at night, low in the margin. Warm, because every
   lamp in 1886 was a flame. ---- */
.at-lamp .at-v {
  width: 190px; height: 130px; border-radius: 50%; filter: blur(28px);
  margin: -65px 0 0 -95px;
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, #e8b465 62%, transparent), transparent 68%);
}
[data-theme="dark"] .at-lamp .at-v {
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, #f2c579 34%, transparent), transparent 68%);
}

/* ---- GLOOM. The darkest thing the house owns: the light going OUT of the
   room. It creeps in from the WALL side of the margin and never from the page
   side, because the page must stay the brightest thing on the screen. This is
   what «sadness» is in a vocabulary made of paper and ink — not a symbol of
   grief, just less light — and it is on five instalments out of ninety-seven.
   The heat may lift it; nothing else may. ---- */
.at-gloom { right: -90px; }
.at-gloom.at-from-l { left: -90px; right: auto; }
.at-gloom .at-v {
  width: 300px; height: 430px; border-radius: 50%; filter: blur(38px);
  background: radial-gradient(ellipse at center,
              color-mix(in srgb, var(--carbon) 46%, transparent), transparent 66%);
}
[data-theme="dark"] .at-gloom .at-v {
  background: radial-gradient(ellipse at center, rgba(0, 0, 0, .8), transparent 66%);
}

/* ---- THE FLEURON. The hidden one. Perhaps one fall in a few minutes is not
   weather at all: it is the book's own printer's ornament — Ванька's
   envelope, the Nose's pointing hand, Шинель's snowflake — come loose from
   the page and drifting down through the margin. Nobody is told this happens.
   A reader who is at the desk often enough will see one, twice, and
   eventually work out that it is always THIS book's mark. ---- */
.at-fleur .at-v {
  font-family: var(--serif, Georgia, serif); font-size: 19px; line-height: 1;
  color: var(--book-ink, currentColor); opacity: .5;
}

/* ============================================================================
   THE WATERMARK — the margin's one motionless surprise.

   At an instalment's door, now and then, the book's ornament is printed very
   large and very faint into the margin, the way a mark is pressed into paper
   rather than laid on top of it. It does not move, does not fade in, does not
   answer anything: it is simply the paper this instalment happens to be
   printed on, at an ink density measured to be legible only once you have
   stopped reading and looked up — which is precisely the moment it is for.
   ========================================================================== */
#atmos .at-mark, .atmos-stage .at-mark {
  position: absolute; font-family: var(--serif, Georgia, serif); line-height: 1;
  color: var(--book-ink, var(--carbon)); opacity: .052; user-select: none;
}
[data-theme="dark"] #atmos .at-mark,
[data-theme="dark"] .atmos-stage .at-mark { opacity: .085; }

/* Below the width where the gutters are real, none of this exists at all —
   atmos.js measures the live column and refuses to mount, and this is the
   belt to that pair of braces. A phone reader gets the page, unmolested,
   which is the correct answer: there is no margin to put a room in. */
@media (max-width: 899px) { #atmos, .atmos-stage { display: none; } }


/* ===== 33-cast-stage.css ===== */
/* ============================================================================
   33-cast-stage.css — THE CAST'S STAGE (the emotional-design charter, Act I).

   The residents become teachers: they fly when a run is earned, speak from the
   reader's own record, ponder before a hint, and grow with the acquaintance.
   Laws in force (research/emotional-design-2026-08-30.md): verdict before
   theater; one hero motion per moment; flight is deterministic and rare;
   nothing bounces — a bird banks and settles, ink glides; every displacement
   is born inside the reduced-motion gate, and reduced motion never reduces
   feedback — the words and the gold remain as fades.
   ========================================================================== */

/* ---- the spring tokens: paper compliance, not rubber. The overshoot is
   ≤4% — the charter's seal-tier settle, shared by the slips and chips.
   linear() where the browser has it; a close bezier where it does not. ---- */
:root {
  --spring-slip: cubic-bezier(.3, 1.04, .55, 1);
  --spring-slip-t: 380ms;
}
@supports (animation-timing-function: linear(0, 1)) {
  :root { --spring-slip: linear(0, 0.62, 0.96, 1.035, 1.01, 0.998, 1); }
}

/* ---- the sky: one fixed, contained, pointer-transparent overlay that hosts
   every flier. Layers are created and destroyed in this one known place.
   Below the dialogs (word cards, the counter) — a flight never covers work. */
#cast-sky {
  position: fixed; inset: 0; z-index: 60;
  pointer-events: none; contain: strict;
}
#cast-sky .cs-flier {
  position: absolute; left: 0; top: 0;
  width: clamp(72px, 10vw, 116px); height: clamp(72px, 10vw, 116px);
  offset-rotate: auto 0deg;
  will-change: transform;
}
#cast-sky .cs-flier svg { width: 100%; height: 100%; }
/* THE ONE LAYERING RULE (§19.1). A lesson takes the whole screen — .gx-stage
   is fixed, opaque and at 240 — and the sky stood at 60, so the run of six and
   the sealed chapter sent their resident across the sheet UNDER the lesson,
   where nobody could see it. While the lesson room is up the sky stands just
   over it: above the lesson it crosses, still below the toast (250), the close
   ritual (260), the coach (265) and the dialogs (290), so a flight still never
   covers work, and it never takes a pointer. */
body.gx-on #cast-sky { z-index: 245; }

/* the reduced-motion / calm fallback: the medallion takes one pass of gold —
   an opacity fade, no displacement. The feedback survives; the travel does not. */
.gx-mate::after {
  content: ''; position: absolute; inset: 4%; border-radius: 50%;
  background: radial-gradient(circle at 50% 40%,
    color-mix(in srgb, var(--g-gold, #c9a227) 55%, transparent), transparent 70%);
  opacity: 0; pointer-events: none;
}
.gx-mate.gx-mate-flash::after { animation: cs-flash 1.1s ease-out both; }
@keyframes cs-flash { 0% { opacity: 0; } 25% { opacity: .85; } 100% { opacity: 0; } }

/* the medallion's BLOOM — a ring of the sitting's ink on every clear
   (--pulse-c set by the room: the ink, the accent when swift, gold at a
   milestone). Opacity only; the same fade under reduced motion. */
.gx-mate.gx-mate-pulse::after {
  background: radial-gradient(circle at 50% 42%,
    color-mix(in srgb, var(--pulse-c, var(--ink, #888)) 60%, transparent), transparent 72%);
  animation: cs-flash .7s ease-out both;
}

/* THE RESIDENT'S MARGIN NOTE — its words set inline at the answer,
   readable: the quill glyph, a rule of the room's ink, the line. */
.gx-mate-note {
  display: flex; align-items: baseline; gap: 9px;
  margin: 8px 0 6px; padding: 8px 12px;
  border-left: 3px solid color-mix(in srgb, var(--ink, var(--carbon)) 65%, transparent);
  background: color-mix(in srgb, var(--ink, var(--carbon)) 5%, var(--sheet));
  font-family: var(--serif); font-size: .98rem; line-height: 1.5;
  position: relative; z-index: 1;
}
.gx-mate-note i { font-style: normal; color: var(--ink, var(--carbon)); flex: 0 0 auto; }
.gx-mn-teach { border-left-color: color-mix(in srgb, var(--g-gold, #c9a227) 70%, transparent); }
@media (prefers-reduced-motion: no-preference) {
  .gx-mate-note { animation: cs-rise .3s ease-out both; }
}

/* ---- the slip: the resident's spoken line, a printed strip beside the
   medallion. Text is the payload — it appears even where all motion is off. */
.gx-mate .gx-slip {
  position: absolute; right: 96%; bottom: 52%;
  max-width: min(40ch, 52vw);
  background: var(--sheet); color: var(--carbon);
  border: 1px solid color-mix(in srgb, var(--ink, var(--carbon)) 55%, var(--carbon));
  box-shadow: 0 2px 8px color-mix(in srgb, var(--carbon) 18%, transparent);
  padding: 9px 13px; font-size: 1rem; line-height: 1.5; text-align: left;
  transform-origin: 100% 90%;
}
.gx-mate .gx-slip::after {
  content: ''; position: absolute; right: -7px; bottom: 12px;
  width: 12px; height: 12px; background: inherit;
  border: inherit; border-left: 0; border-bottom: 0;
  transform: rotate(45deg);
}
.gx-mate .gx-slip-gone { opacity: 0; transition: opacity .45s ease-out; }
@media (max-width: 560px) {
  .gx-mate .gx-slip { font-size: .78rem; max-width: 44vw; bottom: 70%; }
}

/* ---- the hint chip: the teacher's offer on a retry card. A quiet tertiary
   control in the card itself (the medallion is pointer-transparent). ---- */
.gx-hintchip {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 10px; padding: 5px 12px;
  font-family: var(--sans); font-size: .78rem; letter-spacing: .04em;
  background: transparent; color: color-mix(in srgb, var(--carbon) 78%, var(--galley));
  border: 1px dashed color-mix(in srgb, var(--ink, var(--carbon)) 50%, transparent);
  border-radius: 0; cursor: pointer;
}
.gx-hintchip:hover { background: var(--sheet); }
.gx-hintchip[disabled] { opacity: .45; cursor: default; }

/* ---- growth: the acquaintance made visible. Friend.depth() (1..5) scales
   the figure from its feet, slowly — the protégé grows because the reader
   reads. It may lean a little out of its medallion by depth five. ---- */
.gx-mate svg {
  scale: calc(1 + (var(--mate-depth, 1) - 1) * .035);
  transform-origin: 50% 100%;
  transition: scale 1.2s ease-out;
}

/* ---- the keeper of the book (Act III): the quill at the memory page,
   presiding over the open notebook ---- */
#mem-keeper {
  display: flex; gap: 14px; align-items: center;
  margin: 10px 0 14px; padding: 12px 14px;
  border: 1px solid var(--rule, #ccc);
  background: color-mix(in srgb, var(--accent, #888) 4%, transparent);
}
#mem-keeper .memk-fig { width: 64px; height: 64px; flex: 0 0 auto; }
#mem-keeper .memk-fig svg { width: 100%; height: 100%; }
#mem-keeper .memk-fact { margin: 3px 0 0; font-size: .95rem; line-height: 1.5; }

/* ---- every displacement below is born gated. ---- */
@media (prefers-reduced-motion: no-preference) {
  /* the slip arrives on the spring — scale from its tail, text riding late */
  .gx-mate .gx-slip { animation: cs-slip var(--spring-slip-t) var(--spring-slip) both; }
  @keyframes cs-slip { from { scale: .6; opacity: 0; } }

  /* SEEDED ENTRANCES — four ways a card can be laid on the table, stable per
     question today, different tomorrow. All decelerating arrivals under
     340ms; the variation is the point, the calm is the law. */
  .gx-mid[data-enter="rise"]   { animation: cs-rise .3s ease-out both; }
  .gx-mid[data-enter="settle"] { animation: cs-settle .28s ease-out both; }
  .gx-mid[data-enter="tip"]    { animation: cs-tip .34s ease-out both; }
  .gx-mid[data-enter="draw"]   { animation: cs-draw .3s ease-out both; }
  @keyframes cs-rise   { from { opacity: 0; translate: 0 10px; } }
  @keyframes cs-settle { from { opacity: 0; scale: 1.012; } }
  @keyframes cs-tip    { from { opacity: 0; rotate: .35deg; translate: 0 6px; } }
  @keyframes cs-draw   { from { opacity: 0; translate: -8px 0; } }
}


/* ===== 40-press.css ===== */
/* ============================================================
   40-press.css — ТИСК, the press. The room the ceremony happens in.

   Owns #press and nothing else. Every plane here is a token: the ground is
   --room and carries display type only, the bed is --case and carries no text
   at all, the sheet is --sheet and is the only warm surface on screen. The
   one saturated thing in the whole file is four register marks, and they are
   fills, never text.

   Hard edges, flat planes, one offset shadow with no blur. A blurred shadow
   implies a light source, and a composing room lit from nowhere in particular
   is exactly the point: the sheet is bright because it is paper, not because
   something is shining on it.
   ============================================================ */

#press {
  position: fixed; inset: 0; z-index: 400;      /* over snow (300) and the xp float (320) */
  display: none;
  background: var(--room);
  /* ONE number for the bed's packing. The forme has to sit exactly on the
     sheet, and a percentage would not: padding percentages resolve against
     the PARENT's inline size and inset percentages against this element's own
     box, so the two agree at one viewport width and nowhere else. */
  --pack: clamp(16px, 4vw, 34px);
  /* the ceremony is a moment, not a screen: no scroll, no selection, no idle */
  overflow: hidden; user-select: none; -webkit-user-select: none;
  touch-action: none;
}
#press.on {
  display: grid;
  grid-template-rows: auto auto auto;
  place-content: center; justify-items: center;
  gap: clamp(14px, 3.4vh, 30px);
  animation: press-in calc(200ms * var(--motion)) var(--e-enter) both;
}
#press.is-out { animation: press-out calc(260ms * var(--motion)) var(--e-exit) both; }
@keyframes press-in  { from { opacity: 0 } to { opacity: 1 } }
@keyframes press-out { from { opacity: 1 } to { opacity: 0 } }

/* THE NAME OF THE MACHINE. Display type on --room is the one thing that
   ground is allowed to carry (carbon on room measures Lc 62.1 — past the
   non-body floor of 60 and nowhere near the body floor of 75, which is what
   makes "display only" an enforceable law rather than a preference). */
#press .press-name {
  font-family: var(--bill); font-weight: 700;
  font-size: var(--t-5); line-height: 1;
  letter-spacing: .34em; margin-right: -.34em;   /* cancel the trailing track: really centred */
  text-transform: uppercase; color: var(--carbon);
}

/* THE BED. Recessed metal, carrying no text — only the sheet sits on it. */
#press .press-bed {
  position: relative;
  width: min(92vw, 660px, 84vh);
  aspect-ratio: 3 / 2;
  padding: var(--pack);
  background: var(--case);
  border: 1px solid var(--rule-room);
  border-top: 2px solid var(--carbon);            /* the platen edge — a structural bar */
  box-shadow: var(--shadow);
  /* clips the forme exactly where the GL canvas clips its plate: both paths
     lose the forme off the top of the press, neither flies over the room */
  overflow: hidden;
}
#press.on .press-bed { will-change: transform; }   /* the recoil moves this ONE parent */

/* Register marks: the only saturated hue in the product, used as fill and as
   a mark, never as a word. They belong to setting up, so they go out the
   moment the forme lifts — that is the room stilling. */
#press .press-reg {
  position: absolute; width: 11px; height: 11px; opacity: .9;
  background:
    linear-gradient(var(--signal), var(--signal)) 50% 0 / 1px 100% no-repeat,
    linear-gradient(var(--signal), var(--signal)) 0 50% / 100% 1px no-repeat;
  transition: opacity calc(420ms * var(--motion)) var(--e-exit);
}
#press .press-reg.tl { left: 5px;  top: 5px; }
#press .press-reg.tr { right: 5px; top: 5px; }
#press .press-reg.bl { left: 5px;  bottom: 5px; }
#press .press-reg.br { right: 5px; bottom: 5px; }
#press.is-gl .press-reg, #press.is-forme .press-reg, #press.is-done .press-reg { opacity: 0; }

/* THE SHEET. The only warm plane on screen and the brightest in the mode.
   NO hairline on it, and that is structural rather than aesthetic: the GL
   quad is placed from this element's border box, so a 1px border would be a
   1px ring the shader paints in paper and the DOM paints in --rule, visible
   at exactly the moment the two are supposed to be the same picture. Paper on
   type metal needs no rule anyway — #fbf7ee against #8e958c is the edge. */
#press .press-sheet {
  position: relative; width: 100%; height: 100%;
  background: var(--sheet);
  /* minmax(0, 1fr), not the implicit auto track. An auto track is sized by its
     item's MIN-content, and the title is white-space: nowrap — so a long book
     title made the track wider than the sheet and pushed the whole impression
     off the right edge. minmax(0, …) is what stops content from sizing the
     paper instead of the paper sizing the content. */
  display: grid; grid-template-columns: minmax(0, 1fr); place-items: center;
  overflow: hidden;
}

/* THE IMPRESSION — the finished state, and the frame all four paths land on.
   Nothing here is decorated: it is the DOM the GL texture is painted from, so
   any rule added below changes the plate as well. They cannot drift. */
#press .press-imp {
  text-align: center; color: var(--carbon);
  padding: 0 6%;
  width: 100%; min-width: 0;
}
#press .press-eyebrow {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 500;
  letter-spacing: .28em; text-transform: uppercase;
  /* one ink, screened back — a lighter line on a press is less ink, not grey */
  color: color-mix(in srgb, var(--carbon) 52%, var(--sheet));
  line-height: 2;
}
#press .press-title {
  font-family: var(--serif); font-weight: 600;
  font-size: clamp(var(--t-5), 6.2vw, var(--t-7));
  line-height: 1.5; white-space: nowrap;          /* one line: the plate measures this box */
  margin: 2px 0;
}
#press .press-rule {
  display: block; width: 58px; height: 2px;
  margin: clamp(8px, 2%, 14px) auto;
  background: var(--carbon);
}
#press .press-foot {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 400;
  letter-spacing: .24em; text-transform: uppercase;
  color: color-mix(in srgb, var(--carbon) 52%, var(--sheet));
  line-height: 2;
}

/* THE GL SURFACE. Covers the bed's padding box exactly, draws the sheet's own
   rect and the forme above it, and is transparent everywhere else so the DOM
   bed shows through. Removed and the context destroyed the moment it ends. */
#press .press-gl {
  position: absolute; left: 0; top: 0; width: 100%; height: 100%;
  display: block; pointer-events: none;
}
/* The DOM impression is hidden while GL owns the sheet — otherwise the
   finished text would be legible under the blank paper from the first frame. */
#press.is-gl .press-imp { opacity: 0; }
#press.is-gl.is-done .press-imp { opacity: 1; }

/* THE FORME, for the paths without GL: a plate of type metal with its chase
   and furniture, sitting exactly over the sheet.
   NOT `inset: var(--pack)` with auto width. An <svg> is a REPLACED element:
   with width auto and both insets set, CSS 2.1 10.3.8 shrink-to-fits it to its
   INTRINSIC size and drops the far inset, so the plate came out 300x200px
   pinned to the top-left corner and flew over the wordmark. Explicit sizes
   against the padding box are the only thing that puts it on the sheet. */
#press .press-forme {
  position: absolute; left: var(--pack); top: var(--pack);
  width: calc(100% - var(--pack) * 2);
  height: calc(100% - var(--pack) * 2);
  opacity: 0; pointer-events: none;
  fill: none; stroke: var(--carbon);
}
/* ONE parent moves; the four paths inside it never do — measured at 0.37s of
   task time against 0.51s for moving the children, 38% cheaper. */
#press.is-forme .press-forme { opacity: 1; will-change: transform, opacity; }
/* the plate itself: --case inked down 12%, the same mix the shader makes,
   because the bed under it is --case too and same-on-same is not a plate */
#press .pf-plate { fill: color-mix(in srgb, var(--case) 88%, var(--carbon)); stroke: none; }
#press .pf-chase { stroke-width: 3; opacity: .42; }
#press .pf-rule  { stroke-width: 2; opacity: .3; }
#press.is-done .press-forme { opacity: 0; }

/* Display type again, and it appears only once there is something to leave. */
#press .press-hint {
  font-family: var(--bill); font-size: var(--t-0); font-weight: 400;
  letter-spacing: .22em; margin-right: -.22em;
  text-transform: uppercase; color: var(--carbon);
  opacity: 0; transition: opacity calc(400ms * var(--motion)) var(--e-enter);
}
#press.is-done .press-hint { opacity: .72; }

/* A ceremony must not be the reason a phone in landscape shows nothing. */
@media (max-height: 460px) {
  #press.on { gap: 10px; }
  #press .press-name { font-size: var(--t-3); }
  #press .press-bed { width: min(92vw, 660px, 128vh); }
}

/* The three.js ceremony sits above everything and covers the route completely.
   Covering it is not decoration: it is what keeps the law — nothing animates
   BESIDE running text, and the surest way to guarantee that is for there to be
   no text beside it. */
.press3d {
  position: fixed; inset: 0; z-index: 300;
  background: var(--room);
  animation: press3d-in 260ms var(--e-std, ease) both;
}
.press3d canvas { display: block; width: 100%; height: 100%; }
@keyframes press3d-in { from { opacity: 0 } to { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .press3d { animation: none } }


/* ===== 50-legal-foot.css ===== */
/* The legal links in the colophon. Quiet — they are an obligation and a
   courtesy, not a call to action, and nothing here should compete with the
   page it sits under. */
.legal-foot {
  margin: 30px 0 4px; padding-top: 14px;
  border-top: 1px solid var(--rule);
  display: flex; flex-wrap: wrap; gap: 4px 20px;
}
.legal-foot a {
  font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .12em; text-transform: uppercase;
  color: var(--steel); text-decoration: none;
  padding: 6px 0;                      /* a 44px row on touch, without a box */
  border-bottom: 1px solid transparent;
}
.legal-foot a:hover { color: var(--carbon); border-bottom-color: var(--signal); }
.legal-foot a:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

/* The static footer. It exists for crawlers and for JavaScript-off readers, so
   it must be in the document and reachable — but the application has its own
   footer in the colophon, so this one sits quietly at the very bottom rather
   than competing with it. Never display:none: a link that is not rendered is a
   link that does not count. */
#static-legal {
  display: flex; flex-wrap: wrap; gap: 6px 22px;
  padding: 22px 16px 30px; margin-top: 40px;
  border-top: 1px solid var(--rule);
  font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .1em; text-transform: uppercase;
  /* the reading bar is fixed over the foot of the page; the room it takes is
     kept free under these links (app.js measures it; 0 off the reading view) */
  margin-bottom: var(--readbar-h, 0px);
}
#static-legal a { color: var(--steel); text-decoration: none; padding: 4px 0; }
#static-legal a:hover { color: var(--carbon); }
#static-legal a:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }


/* ===== 60-desk.css ===== */
/* ============================================================================
   THE DESK, CORRECTED.

   Four things were wrong when the type case first landed, and all four were
   visible in one screenshot rather than findable in the source. They are fixed
   here rather than in 20-case.css so that the sheet which owns the case stays
   the case's own, and so the reasoning for each correction sits next to it.
   ============================================================================ */

/* The corrections that were about КАССА's compartments, its ghost letters and
   its ledger are gone with it — see build.py's MODULE_ORDER. What is left here
   applies to the desk itself and outlives the scene that prompted it. */

/* --- 3. THE ORNAMENT WAS BUILT FOR A CENTRED MASTHEAD -------------------
   The home masthead is left-aligned against the type-metal band; a centred
   rule with a knocked-out ornament floats in the middle of that band and
   crosses the diagonal, which is the one line in the composition that has to
   stay clean. The ornament belongs to the story masthead, which IS centred.
   Kept there, dropped here. */
#v-home .masthead .rule { display: none; }

/* --- 4. THE THEME SWATCHES WERE ONE SOLID BLOCK -------------------------
   Three colour chips with hard 1px borders and no gap read as a single
   three-striped object rather than as three choices, and the selected one had
   nothing to say so. Separated, and the chosen one now carries the same red
   underline the active navigation tab does — one language for "this is where
   you are", used in both places. */
.theme-swatches, .theme-pick, .bar .themes { gap: 6px; }
.bar [data-theme-set], .theme-swatches button {
  border: 1px solid var(--carbon);
  outline-offset: 2px;
  position: relative;
}
.bar [data-theme-set] + [data-theme-set],
.theme-swatches button + button { margin-left: 6px; }
.bar [data-theme-set][aria-pressed="true"]::after,
.theme-swatches button.on::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -5px;
  height: 3px; background: var(--signal);
}



/* The bill-and-ladder side-by-side grid that used to live here is gone with
   the panels themselves: the desk is recomposed as a spread, a bill, a bench
   and a record — see 65-home.css, which owns all of the new layout. Only the
   measure rule below outlives it. */
#frontier-card .fr-lab, #frontier-card .un-note {
  max-width: 68ch;
}


/* ===== 65-home.css ===== */
/* ============================================================================
   65-home.css — THE DESK, RECOMPOSED.

   The desk used to be twenty stacked cards: three of them were doors into the
   same FSRS deck, and the rest were a dashboard. It is now four acts down one
   page, one left edge, one primary action per act:

     I    THE SPREAD  — the sentence you stopped at (held by the volume itself,
                        a CSS-3D book) beside tonight's single composed sitting.
     II   THE BILL    — the one number that is a fact about your Russian,
                        full width, with the playbill and the ladder folded in.
     III  THE SHELF   — fourteen volumes standing on a bench line.
     IV   THE RECORD  — memory, calendar, rank: one band, three planes.

   3D is CSS only. A book is box geometry, which is CSS-3D's home ground: the
   type on the boards is real DOM type (crisp, themeable, zero GL bytes), the
   pose is a static transform (zero per-frame cost), and the only motion is a
   hover straightening and a ±5° tilt under a fine pointer. WebGL stays where
   the law put it: in the seal ceremony, one context, seconds long.

   Scroll choreography reuses the house verb — choreo-set, the plane arriving
   from the left along the composing stick (30-choreo.css owns the keyframes
   and the motion gates; everything here honours the same custom properties).
   Firefox, which ships no scroll timelines, simply sees every element in its
   finished state — a complete picture, not a blank one.
   ========================================================================== */

/* ---- ONE MEASURE. Every band on the desk — arena, ledger, dateline,
   spread, road, bill, shelf, record — hangs on this single width; nothing
   floats at its own size any more. 1280 up from 1040: the page breathes,
   the grid holds. ---- */
#v-home, #v-library { --desk-w: min(94vw, 1280px); }
#v-home > .desk-spread,
#v-home > .desk-sect,
#v-library > .desk-spread,
#v-library > .desk-sect {
  max-width: var(--desk-w);
  margin-inline: auto;
}
/* the spread holds only Tonight now — the hero and the shelf keep the
   reading room; the card stands centred at a hand's width */
#v-home > .desk-spread { grid-template-columns: min(640px, 100%); justify-content: center; }
/* the reading room's hero takes the whole spread */
.lib-spread { grid-template-columns: 1fr; }
/* the shelf's PLANK runs full-bleed — the books stay on the shared measure
   with everything else (a full-bleed content band left them 20px off every
   other section's edge); only the rule itself escapes to the viewport,
   which html/body's own overflow clip absorbs without a scrollbar */

/* ---- section heads: a bar of ink standing on a rule, not a prose H3 ---- */
.sect-head { display: flex; align-items: center; gap: 0; margin: 52px 0 20px; }
.sect-head span {
  background: var(--pine, var(--carbon)); color: var(--on-pine, var(--galley));
  font-family: var(--bill); font-weight: 600; text-transform: uppercase;
  letter-spacing: .16em; font-size: var(--t-0);
  padding: 6px 14px;
  flex: none;
}
.sect-head::after {
  content: ''; flex: 1; height: 2px; background: var(--pine, var(--carbon));
  transform-origin: 0 50%;
}

/* ============================================================
   ACT I · THE SPREAD
   ============================================================ */
.desk-spread {
  display: grid;
  grid-template-columns: 1.12fr .88fr;
  gap: 26px;
  align-items: stretch;
  margin-top: 30px;
}
@media (max-width: 980px) { .desk-spread { grid-template-columns: 1fr; } }

/* -- left page: the hero volume and the sentence it is holding -- */
.spread-hero {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 28px;
  align-items: center;
  background: var(--galley);
  border: 2px solid var(--carbon);
  padding: 26px 28px;
}
@media (max-width: 560px) {
  .spread-hero { grid-template-columns: 1fr; justify-items: center; text-align: center; }
  .spread-hero .hero-copy { text-align: left; }
}

.hero-vol-wrap { perspective: 900px; width: 178px; }
.vol {
  position: relative;
  width: 150px; height: 224px;
  margin-left: 16px;
  transform-style: preserve-3d;
  /* base pose + pointer tilt compose in one place; the tilt vars default to 0
     so the resting pose is authored here and nowhere else */
  transform: rotateX(calc(4deg + var(--tx, 0deg))) rotateY(calc(-26deg + var(--ty, 0deg)));
  transition: transform calc(.45s * var(--motion)) var(--e-std);
}
/* the six faces a book actually shows: cover, spine, page block, back board */
.vol-cover {
  position: absolute; inset: 0;
  background: var(--sheet);
  border: 1.5px solid var(--carbon);
  transform: translateZ(15px);
  display: flex; flex-direction: column;
  padding: 18px 16px 14px;
  backface-visibility: hidden;
}
.vol-back {
  position: absolute; inset: 0;
  background: var(--carbon);
  transform: translateZ(-15px);
}
/* side faces use the standard centred recipe: a face of width D sits at the
   box's centre, is rotated onto the yz plane, then travels W/2 along its own
   (rotated) z axis to the box's edge. Default transform-origin — the centre —
   is load-bearing here. */
.vol-spine {
  position: absolute; left: calc(50% - 15px); top: 0;
  width: 30px; height: 100%;
  background: var(--carbon); color: var(--galley);
  transform: rotateY(-90deg) translateZ(75px);
  display: flex; align-items: center; justify-content: center;
  writing-mode: vertical-rl;
  font-family: var(--bill); font-weight: 600; text-transform: uppercase;
  letter-spacing: .18em; font-size: var(--t-0);
  backface-visibility: hidden;
}
.vol-pages {
  position: absolute; left: calc(50% - 15px); top: 3px;
  width: 30px; height: calc(100% - 6px);
  transform: rotateY(90deg) translateZ(75px);
  /* a page block is a texture of edges, drawn with the two greys the room owns */
  background: repeating-linear-gradient(to right, var(--galley) 0 2px, var(--rule) 2px 3px);
  backface-visibility: hidden;
}
.vol-cover .vc-author {
  font-family: var(--sans); font-size: var(--t-00);
  letter-spacing: .14em; text-transform: uppercase; color: var(--steel);
}
.vol-cover .vc-title {
  font-family: var(--bill); font-weight: 700; text-transform: uppercase;
  font-size: var(--t-4); line-height: .95; letter-spacing: .02em;
  color: var(--carbon); margin-top: 10px;
  overflow-wrap: anywhere;
}
.vol-cover .vc-rule { width: 34px; height: 3px; background: var(--signal); margin-top: 10px; }
.vol-cover .vc-foot {
  margin-top: auto;
  display: flex; justify-content: space-between;
  font-family: var(--mono); font-size: var(--t-00); color: var(--steel);
}
/* the red mark of the current book: a bookmark over the board, not a badge */
.vol-mark {
  position: absolute; top: -2px; right: 16px;
  width: 14px;
  /* the ribbon hangs as deep as the reader is: a fact, not a meter */
  height: calc(30px + var(--depth, 0) * 150px);
  background: var(--signal);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% calc(100% - 9px), 0 100%);
}

.hero-copy .smallcaps { font-size: var(--t-00); letter-spacing: .16em; color: var(--steel); }
.hero-copy .gutter {
  font-family: var(--serif); font-size: 1.18rem; line-height: 1.6;
  font-style: italic; margin: 10px 0 14px;
  max-width: 44ch;
}
.hero-copy .gutter .dim { opacity: .38; }
/* the hero speaks in written verbs — the masthead holds the desk's only
   primary button, so nothing here may shout */
.hero-verbs { display: flex; flex-wrap: wrap; gap: 8px 22px; margin-top: 4px; }
.hero-verbs .linklike:first-child { color: var(--carbon); font-weight: 600; }

/* -- right page: tonight -- */
.tonight-card {
  background: var(--galley);
  border-width: 2px;
  padding: 22px 24px 20px;
  display: flex; flex-direction: column;
}
.tonight-card .bill-head {
  background: var(--carbon); color: var(--galley);
  margin: -22px -24px 16px; padding: 7px 24px;
}
.tn-row {
  display: flex; align-items: baseline; gap: 14px;
  padding: 9px 0;
  border-bottom: 1px dotted var(--rule);
}
.tn-row:last-of-type { border-bottom: none; }
.tn-n {
  font-family: var(--mono); font-weight: 400;
  font-size: var(--t-5); line-height: .9; letter-spacing: -.02em;
  min-width: 54px; text-align: right;
  flex: none;
}
.tn-l { font-size: var(--t-0); color: var(--steel); }
.tn-l b { color: var(--carbon); }
/* THE DUE COUNT IS A SENTENCE, NOT A METER (kill list). Set display-size in
   the count column it read as a debt owed — the one number the charter
   refuses to make primary, struck once from the hearth and crept back here.
   It keeps its figure, at the weight of a fact stated in passing. */
.tn-due { padding-left: 2px; }
.tn-due .tn-l b { font-family: var(--mono); font-weight: 700; }
.tn-keys { justify-content: flex-start; margin-top: 12px; }
/* each key wears its book: a tick of the volume's pigment along the top,
   deepening on hover — five words from three books read as three places */
.tn-keys button { border-top: 3px solid var(--ink, var(--edge)); }
.tn-keys button:hover {
  border-color: var(--ink, var(--carbon));
  box-shadow: inset 0 2px 0 color-mix(in srgb, var(--ink, var(--carbon)) 35%, transparent), var(--lift);
}
.tn-act { margin-top: auto; padding-top: 18px; }
/* the evening lamp: the run of evenings, spoken where the evening begins.
   Unlit it sits in steel; lit, the flame alone takes the gold — celebration
   ink is spent on the mark, never on the sentence. */
.tn-lamp {
  display: flex; gap: 9px; align-items: baseline;
  font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--steel);
  margin: 14px 2px 0; padding-top: 10px;
  border-top: 1px dotted var(--rule);
}
.tn-lamp b { color: var(--carbon); font-style: normal; }
.tn-lamp .tn-flame { font-style: normal; color: var(--steel); flex: none; }
.tn-lamp.lit .tn-flame { color: var(--reward, var(--gold-leaf, #b8860b)); }
.tn-note { font-size: var(--t-00); margin-top: 10px; max-width: 52ch; }
.tn-empty {
  font-family: var(--serif); font-style: italic; color: var(--steel);
  margin: 8px 0 0; max-width: 40ch;
}

/* ============================================================
   ACT II · THE BILL — full width, number against playbill
   ============================================================ */
#bill-sect { margin-top: 40px; }
#bill-sect .frontier-card {
  max-width: none; width: auto; margin: 0;
  text-align: left;
}
.frontier-card .fr-grid {
  display: grid;
  grid-template-columns: minmax(240px, .8fr) 1.2fr;
  gap: 10px 48px;
  align-items: start;
}
@media (max-width: 900px) { .frontier-card .fr-grid { grid-template-columns: 1fr; } }
.frontier-card .fr-grid .bill { margin-top: 4px; }

/* the goal gradient under the bill's number: one line, a fact about distance
   to the next rung. The signal ink appears only on the last inch — when one
   evening could close it — per the palette budget. */
.frontier-card .fr-next { font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--ink-2); margin-top: 6px; }
.frontier-card .fr-next .fr-inch { color: var(--signal-ink, var(--signal));
  font-style: normal; font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* the movement notice: a number that just went up, marked the way every state
   change is marked — a rule of signal, never a banner */
.un-moved {
  border-left: 3px solid var(--signal);
  padding-left: 12px;
  margin: 10px 0;
  font-size: var(--t-0);
  color: var(--carbon);
  max-width: 60ch;
}

/* the ladder, folded to one honest line under the bill */
.lad-fold {
  border-top: 2px solid var(--carbon);
  margin-top: 20px; padding-top: 16px;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 4px 24px;
  align-items: center;
}
@media (max-width: 700px) { .lad-fold { grid-template-columns: 1fr; } }
.lad-fold .lad-line { white-space: nowrap; }
.lad-fold .lad-line .lf-d {
  font-family: var(--sans); font-size: var(--t-1); letter-spacing: 0; color: var(--steel);
}
.lad-fold .lf-body .faint { max-width: 72ch; }

/* ============================================================
   ACT III · THE SHELF — volumes on a bench line
   ============================================================ */
#bench-sect .shelf {
  display: flex; flex-wrap: wrap;
  gap: 26px 20px;
  align-items: flex-end;
  padding: 10px 8px 0;
}
/* the bench itself, edge to edge of the viewport — the books keep the
   measure, the plank they stand on does not */
#bench-sect::after {
  content: '';
  display: block;
  height: 3px;
  background: var(--carbon);
  margin-inline: calc(50% - 50vw);
}
/* reset the card look the shelf books inherit from head.html — a volume is a
   solid, not a sheet of stock */
#shelf .book {
  border: none; background: transparent; box-shadow: none;
  padding: 0; width: 136px;
  perspective: 700px;
  cursor: pointer;
  position: relative;
}
/* the flat-card hover vocabulary (lift, tilt, press) belongs to sheets of
   stock; a standing volume answers with its own turn instead */
#shelf .book:hover, #shelf .book:active {
  transform: none; box-shadow: none;
}
#shelf .book:focus-visible { outline: 2px solid var(--signal); outline-offset: 4px; }
:is(#shelf, #locked-panel) .b-vol {
  position: relative;
  width: 112px; height: 176px;
  margin-left: 16px;
  transform-style: preserve-3d;
  transform: rotateY(-22deg);
  transition: transform calc(.38s * var(--motion)) var(--e-std);
}
/* the hover is the browse gesture: the volume turns to face you, exactly as a
   hand would tip it off the shelf. Coarse pointers never see it; tap opens. */
@media (hover: hover) {
  #shelf .book:hover .b-vol, #shelf .book:focus-visible .b-vol { transform: rotateY(-5deg); }
}
:is(#shelf, #locked-panel) .b-cover {
  position: absolute; inset: 0;
  background: var(--sheet);
  border: 1.5px solid var(--carbon);
  transform: translateZ(10px);
  display: flex; flex-direction: column;
  padding: 12px 11px 10px;
  backface-visibility: hidden;
}
:is(#shelf, #locked-panel) .b-back { position: absolute; inset: 0; background: var(--carbon); transform: translateZ(-10px); }
:is(#shelf, #locked-panel) .b-spine {
  position: absolute; left: calc(50% - 10px); top: 0;
  width: 20px; height: 100%;
  background: var(--carbon); color: var(--galley);
  transform: rotateY(-90deg) translateZ(56px);
  display: flex; align-items: center; justify-content: center;
  writing-mode: vertical-rl;
  font-family: var(--bill); font-weight: 600; text-transform: uppercase;
  letter-spacing: .14em; font-size: 10px;   /* scale-exempt: see the book-object note below */
  overflow: hidden;
  backface-visibility: hidden;
}
:is(#shelf, #locked-panel) .b-spine span { max-height: 100%; overflow: hidden; }
:is(#shelf, #locked-panel) .b-pages {
  position: absolute; left: calc(50% - 10px); top: 2px;
  width: 20px; height: calc(100% - 4px);
  transform: rotateY(90deg) translateZ(56px);
  background: repeating-linear-gradient(to right, var(--galley) 0 2px, var(--rule) 2px 3px);
  backface-visibility: hidden;
}
/* SCALE-EXEMPT — the shelf's books are drawn objects, not labels.
   Each is a real 3D solid: translateZ(±10px) covers, a 20px page block, a
   spine set in vertical-rl, and a title whose size is pinned by a stated
   measurement — «Злоумышленник», fourteen condensed capitals that must sit
   on ONE line of a 90px cover. Lifting the type without rebuilding the
   geometry in rem would push the author off the cover and break the spine's
   vertical run, and rebuilding the geometry means rebuilding the transforms
   that give the shelf its depth. A miniature is allowed to be a miniature:
   it is internally proportionate, and it is an object the reader looks AT
   rather than a label the reader reads. The desk's real labels around it —
   the rank ladder, the record sheet, the diary — all take the lift.
   Deliberately below the floor; §96.7 and tests/scale.test.js both know. */
:is(#shelf, #locked-panel) .b-cover .b-author {
  font-size: 8.5px; letter-spacing: .12em; text-transform: uppercase;   /* scale-exempt */
  color: var(--steel); font-family: var(--sans);
}
:is(#shelf, #locked-panel) .b-cover .b-title {
  font-family: var(--bill); font-weight: 700; text-transform: uppercase;
  font-size: var(--t-1); line-height: .95; color: var(--carbon);
  margin-top: 7px;
  /* «Злоумышленник» is the measure: 14 condensed caps must sit on one line
     of a 90px column, which pins the size at 12.5px — no mid-word breaks */
  overflow-wrap: normal; hyphens: manual;
  font-size: 12.5px;
  letter-spacing: .01em;
}
:is(#shelf, #locked-panel) .b-cover .b-year {
  margin-top: auto;
  font-family: var(--mono); font-size: 9px; color: var(--steel);   /* scale-exempt */
}
#shelf .b-fin { position: absolute; right: 8px; bottom: 8px; width: 22px; height: 22px; }
#shelf .b-fin .tick { width: 100%; height: 100%; stroke: var(--signal); }
/* memory state as geometry: a ≤2px mark at the foot of the board, never a fade */
:is(#shelf, #locked-panel) .b-cov {
  position: absolute; left: 10px; right: 10px; bottom: 5px;
  height: 2px; background: var(--rule);
}
:is(#shelf, #locked-panel) .b-cov i { display: block; height: 100%; width: calc(var(--v) * 100%); background: var(--signal); }
/* EVERY VOLUME WEARS ITS PIGMENT — the same ink that marks this book's words
   in practice, its spine on the road, its line on the bill. A publisher's
   series band across the head of the board and the memory mark in the same
   ink: provenance, never decoration. */
:is(#shelf, #locked-panel) .b-cover { border-top: 6px solid var(--ink, var(--carbon)); }
:is(#shelf, #locked-panel) .b-cov i { background: var(--ink, var(--signal)); }
/* the case's headline number counts in the same pigment — but only when there
   IS a number. At zero cold lines the rider «N lines» stands alone in the .fr-n
   (app.js:1511), becomes :first-child, and this id-weighted rule paints the
   metadata in the book's masstone over .fr-d's --steel. The rider is not the
   figure; it keeps the metadata grey. */
#locked-panel .fr-n > span:first-child:not(.fr-d) { color: var(--ink, var(--carbon)); }
#shelf .book:hover .b-spine,
#shelf .book:focus-visible .b-spine {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 45%, var(--carbon));
}
#shelf .b-here {
  position: absolute; top: -2px; right: 10px;
  width: 10px; height: calc(22px + var(--depth, 0) * 110px);
  background: var(--signal);
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% calc(100% - 7px), 0 100%);
}
#shelf .b-under {
  text-align: center;
  margin-top: 10px;
  font-family: var(--mono); font-size: var(--t-00); color: var(--steel);
}

/* ============================================================
   ACT IV · THE RECORD — one sheet, the Послужной список
   ============================================================
   The record band (three planes) and the legacy cabinet grid (six more) are
   struck: nine ink caps below the shelf read as a dashboard, and the owner
   said so. What stands instead is one document on the galley plane — one
   border, one hard shadow, no cards inside. Spine: the Table of Ranks.
   Main leaf: the reader's figures. Margin: marginalia. Labels are smallcaps
   runs on continuous paper, exactly as a clerk would rule them. */
.record-sheet {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr) 240px;
  gap: 0;
  background: var(--galley);
  border: 2px solid var(--carbon);
  box-shadow: var(--shadow);
  align-items: stretch;
}
.record-sheet .rs-spine  { padding: 22px 18px; border-right: 1px solid var(--rule); }
.record-sheet .rs-main   { padding: 24px 28px 20px; min-width: 0; }
.record-sheet .rs-margin { padding: 22px 20px; border-left: 1px solid var(--rule); }
/* spine struck (calm mode, or the ladder still counting) — the sheet closes
   ranks so the main matter never falls into the spine's narrow slot */
.record-sheet.no-spine { grid-template-columns: minmax(0, 1fr) 240px; }
.record-sheet.no-spine .rs-spine { display: none; }
@media (max-width: 980px) {
  /* .record-sheet is (0,1,0) and .record-sheet.no-spine is (0,2,0); a media
     query contributes no specificity at all, so the desktop two-column rule
     went on winning inside here and a phone reader's own figures set one or
     two words to a line down a screen and a half. */
  .record-sheet,
  .record-sheet.no-spine { grid-template-columns: 1fr; }
  .record-sheet .rs-spine  { border-right: none; border-bottom: 1px solid var(--rule); }
  .record-sheet .rs-margin { border-left: none; border-top: 1px solid var(--rule); }
}

/* a text action on the sheet: ink on paper, never a stamp — the record is a
   document, and a document's verbs are written in its own hand */
.linklike {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: var(--t-0); color: var(--steel);
  letter-spacing: .02em; text-decoration: underline;
  text-decoration-thickness: 1px; text-underline-offset: 3px;
}
.linklike:hover { color: var(--signal-ink); }

/* -- the spine: all fourteen rungs, climbed by lines read cold -- */
.rank-ladder { list-style: none; margin: 12px 0 0; padding: 0; }
.rank-ladder li { position: relative; padding: 7px 0 7px 30px; }
/* the rail every rung stands on; the climbed length is signal — geometry,
   not a meter: the red is exactly as tall as the reading is */
.rank-ladder li::before {
  content: ''; position: absolute; left: 9px; top: -1px; bottom: -1px;
  width: 2px; background: var(--rule);
}
.rank-ladder li:first-child::before { top: 50%; }
.rank-ladder li:last-child::before  { bottom: 50%; }
.rank-ladder li.rk-passed::before { background: var(--signal); }
/* the current rung: the grey rail stays whole, and the signal climbs over it
   from below — through the rung, then --rise of the way toward the next */
.rank-ladder li.rk-cur::after {
  content: ''; position: absolute; left: 9px; width: 2px;
  bottom: -1px; height: calc(50% + var(--rise, 0) * 50%);
  background: var(--signal);
}
/* at the foot of the ladder there is nothing below the rung to have climbed */
.rank-ladder li.rk-cur:last-child::after {
  bottom: 50%; height: calc(var(--rise, 0) * 50%);
}
/* the class number is the rung itself, set on the rail in the counting face */
.rank-ladder .rk-cls {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  width: 1.25rem; text-align: center; z-index: 1;
  background: var(--galley); padding: 2px 0;
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: var(--t-00); color: var(--steel);
}
.rank-ladder .rk-en { font-family: var(--sans); font-size: var(--t-00); letter-spacing: .02em; }
.rank-ladder li.rk-ahead  .rk-en { color: var(--ink-3); }
.rank-ladder li.rk-passed .rk-en { color: var(--ink-2); }
.rank-ladder li.rk-cur { padding-top: 10px; padding-bottom: 10px; }
.rank-ladder li.rk-cur .rk-cls { color: var(--signal-ink); font-weight: 600; }
.rank-ladder li.rk-cur .rk-en {
  font-family: var(--bill); font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; font-size: var(--t-3); color: var(--carbon);
  display: block; line-height: 1.05;
}
.rank-ladder .rk-ru { display: block; font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--ink-2); margin-top: 2px; }
.rank-ladder .rk-note { display: block; font-size: var(--t-00); color: var(--signal-ink); margin-top: 2px; }
.rank-ladder .rk-date { display: block; font-family: var(--mono); font-size: var(--t-00); color: var(--steel); }
.rs-spine .rk-foot { font-size: var(--t-00); margin-top: 14px; line-height: 1.5; }

/* -- the main leaf -- */
/* the hero numeral: the one figure that is a fact about the reader's memory */
.rs-hero .rs-n {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: clamp(44px, 7vw, 68px); font-weight: 400;
  line-height: .95; letter-spacing: -.02em; margin-top: 6px;
}
.rs-hero .rs-sub { font-family: var(--sans); font-size: var(--t-0); color: var(--steel); margin-top: 4px; }
.rs-hero .rs-delta {
  font-family: var(--serif); font-style: italic; font-size: var(--t-0);
  color: var(--ink-2); line-height: 1.55; max-width: 56ch; margin: 10px 0 0;
}
.rs-spark { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.rs-spark svg { display: block; flex: none; }
.rs-spark span { font-size: var(--t-00); }

/* the diary: the calendar month as the reader's own register — one square a
   day, ink that only accumulates. A blank day is a blank square and nothing
   else: no red anywhere on this sheet, ever, and nothing here ever resets.
   Carbon ink only; the diary spends none of the screen's vermilion. */
.rs-month { margin-top: 26px; }
.rs-month .dy-cap { font-family: var(--serif); font-style: italic;
  font-size: var(--t-1); color: var(--ink); margin-top: 8px; line-height: 1.5; }
.dy-grid { display: grid; gap: 3px; margin-top: 10px; width: max-content; max-width: 100%; }
.dy-week { display: grid; grid-template-columns: repeat(7, 14px); gap: 3px; position: relative; }
.dy-week i { display: block; width: 14px; height: 14px; border: 1px solid var(--rule); }
.dy-week i.dy-x { border-color: transparent; }                    /* outside the month */
.dy-week i.dy-fu { border-color: color-mix(in srgb, var(--rule) 45%, transparent); } /* not yet lived */
.dy-week i.dy-on { border-color: var(--carbon);
  background: color-mix(in srgb, var(--carbon) 30%, transparent); }
.dy-week i.dy-on.dy-d2 { background: color-mix(in srgb, var(--carbon) 55%, transparent); }
.dy-week i.dy-on.dy-d3 { background: var(--carbon); }
/* today: a quiet carbon ring, never the signal ink — the diary judges nothing */
.dy-week i.dy-today { box-shadow: 0 0 0 1px var(--carbon); }
/* a kept week closes with a short rule — the week's own full stop */
.dy-week.dy-kept::after { content: ''; position: absolute; left: calc(100% + 6px);
  top: calc(50% - 1px); width: 10px; height: 2px; background: var(--carbon); }
.dy-foot { display: flex; flex-wrap: wrap; gap: 4px 16px; margin-top: 10px; align-items: baseline; }
.dy-foot span { font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--ink-2); }
.dy-foot .dy-frame { color: var(--ink-3); font-size: var(--t-00); }
/* the still page: calm mode renders the diary with no motion at all, and the
   OS preference is honored the same way — the sheet simply IS there */
.dy-grid.dy-static, .dy-grid.dy-static .dy-week { animation: none !important; }
@media (prefers-reduced-motion: reduce) {
  #month-register .dy-grid, #month-register .dy-week { animation: none !important; }
}

/* one ruled line each: the agreement, then the service record */
.rs-agreement {
  margin-top: 24px; padding-top: 14px; border-top: 1px solid var(--rule);
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
}
.rs-agreement .rs-agree { font-family: var(--serif); font-style: italic; color: var(--ink); }
/* the agreement opened for writing, in place (planEdit in app.js) — on the
   desk's record and on the ticket alike; it sets its own type, because the
   ticket's quote around it is italic and, when empty, sized to nothing */
.plan-edit {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  width: 100%; font: normal .95rem/1.4 var(--sans); color: var(--carbon);
}
.plan-edit label { flex: 1 0 100%; }
.plan-edit input {
  flex: 1 1 16em; min-width: 0; min-height: 44px; box-sizing: border-box;
  padding: 8px 12px; border: 1px solid var(--carbon); border-radius: 0;
  background: var(--sheet); color: var(--carbon);
  font: normal 1rem/1.4 var(--serif);
}
.plan-edit input:focus { outline: 2px solid var(--carbon); outline-offset: 1px; }
.plan-edit-btns { display: flex; gap: 8px; flex: none; }
.plan-edit-btns .btn { min-height: 44px; }
.rs-service { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--rule); }
.rs-service .rs-line { font-size: var(--t-0); color: var(--ink-2); padding: 3px 0; }
.rs-service .linklike { font-size: var(--t-0); }

/* the wax: impressions actually taken, pressed overlapping as a hand would
   leave them — each disc a hair off true */
#seals.rs-wax { margin-top: 22px; gap: 0; padding-right: 12px; }
/* on the sheet a seal is WAX again — the radial gradient head.html casts —
   not the flat square sort the component sheet later set it to: a signature
   block is pressed into a document, not composed in a case */
#seals.rs-wax .seal.ach {
  border-radius: 50%; border: none;
  background: radial-gradient(circle at 35% 32%, #c4544a, var(--wax) 68%);
  box-shadow: inset 0 2px 5px rgba(255,255,255,.28), inset 0 -3px 6px rgba(0,0,0,.38),
              0 3px 8px rgba(0,0,0,.3);
  color: #f0d9c0; font-family: var(--serif);
}
/* sin(--i) keeps every disc within ±9° of true however many are pressed —
   a hand's scatter, not a fan. Engines without sin() leave them square. */
#seals.rs-wax .seal { margin-right: -12px; transform: rotate(calc(sin(var(--i, 0)) * 9deg)); }
#seals.rs-wax .seal:hover { z-index: 2; position: relative; }

/* the colophon foot: mono figures and written verbs under one heavy rule */
.rs-foot {
  margin-top: 24px; padding-top: 12px; border-top: 2px solid var(--carbon);
  display: flex; flex-wrap: wrap; gap: 8px 18px;
  align-items: baseline; justify-content: space-between;
  font-family: var(--sans); font-size: var(--t-0); color: var(--steel);
}
.rs-foot b { font-family: var(--mono); font-weight: 400; font-variant-numeric: tabular-nums;
  font-size: var(--t-2); color: var(--carbon); margin-right: 3px; }
.rs-foot .rf-actions { display: flex; gap: 16px; flex-wrap: wrap; align-items: baseline; }

/* -- the margin: marginalia, unboxed, one left rule for the whole column -- */
/* the ex-libris slip, pasted in at a clerk's slight angle — seated on the
   record, never floating over the desk */
.rs-margin #ticket-card {
  margin: 0 0 20px;
  transform: rotate(-1.4deg);
}
/* an empty record folds its sheet away but keeps the door: the once-bound
   Back up / Restore controls and the ticket slip must stay reachable — a
   fresh install is exactly when Restore is needed */
#record-sect.sheet-empty .sect-head,
#record-sect.sheet-empty .rs-spine,
#record-sect.sheet-empty .rs-main > :not(.rs-foot),
#record-sect.sheet-empty #marginalia { display: none; }
#record-sect.sheet-empty .record-sheet { box-shadow: none; }
/* …and the fold is a LINE, not a crate (30 Aug audit): the empty sheet kept
   its frame, its heavy foot rule, a zeros stat line, a «Back up» for a record
   that does not exist and a journey door into an empty diary — an abandoned
   box on a stranger's desk. One quiet Restore line is the whole furniture. */
#record-sect.sheet-empty .record-sheet {
  display: block; border: 0; background: none; padding: 0; min-height: 0;
}
#record-sect.sheet-empty .rs-main { display: block; }
#record-sect.sheet-empty .rs-foot {
  border-top: 0; margin-top: 0; padding-top: 0; justify-content: flex-start;
}
#record-sect.sheet-empty #rf-stats,
#record-sect.sheet-empty #btn-export,
#record-sect.sheet-empty #btn-journey,
#record-sect.sheet-empty .rs-margin { display: none; }
.rs-margin .mg-run { border-left: 2px solid var(--rule); padding-left: 14px; margin-top: 10px; }
.rs-margin .mg-item { margin: 0 0 16px; }
.rs-margin .mg-cat {
  font-family: var(--bill); font-weight: 600; text-transform: uppercase;
  letter-spacing: .14em; font-size: var(--t-00); color: var(--signal-ink);
}
.rs-margin .mg-t { font-family: var(--serif); font-weight: 600; font-size: var(--t-1);
  line-height: 1.35; margin: 2px 0; }
.rs-margin .mg-b { font-size: var(--t-0); color: var(--ink-2); line-height: 1.5; }
.rs-margin .mg-plain { font-family: var(--serif); font-size: var(--t-0); color: var(--ink-3); padding: 2px 0; }
.rs-margin .mg-empty { font-family: var(--serif); font-style: italic;
  font-size: var(--t-0); color: var(--ink-2); line-height: 1.55; }
.rs-margin .linklike { margin-top: 6px; }

/* ============================================================
   SCROLL CHOREOGRAPHY — the same verb, the new members
   ============================================================
   The spread is deliberately ABSENT from this list: it sits above the fold and
   is owned by the GSAP overture (sequences.js). Two systems on one element is
   the viewin/VT collision all over again — each element has exactly one owner.

   Everything below the fold gets the house gesture, gated by the same
   custom properties 30-choreo.css declares (body.motion-low and the OS
   preference already set them to `none`; Firefox never enters @supports and
   sees finished states). */
@supports (animation-timeline: view()) {
  /* the ticket slip is excluded: choreo-set's end frame (translate3d(0,0,0),
     fill both) would permanently flatten its −1.4° pasted angle — the margin
     column it sits in already arrives as one plane */
  .desk-sect .card:not(#ticket-card),
  .record-sheet .rs-spine,
  .record-sheet .rs-main,
  .record-sheet .rs-margin {
    animation-name: var(--choreo-reveal, choreo-set);
    animation-fill-mode: both;
    animation-duration: auto;      /* shorthand would zero it; see 30-choreo */
    animation-timing-function: var(--e-enter);
    animation-timeline: view();
    animation-range: cover 0% cover 320px;
  }
  /* the sheet's three columns stage left-to-right, geometrically */
  .record-sheet .rs-main   { animation-range: cover 40px cover 360px; }
  .record-sheet .rs-margin { animation-range: cover 80px cover 400px; }

  /* the section rules draw themselves outward, the same stroke the masthead
     rule makes on arrival — a rule being drawn, not an object fading */
  .sect-head::after {
    animation-name: var(--choreo-rule, sect-rule);
    animation-fill-mode: both;
    animation-duration: auto;
    animation-timing-function: var(--e-std);
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
  }

  /* the bench planes itself across the whole viewport as the shelf arrives —
     the bench is as long as the library, and the stroke is ink, not a fade */
  #bench-sect::after {
    transform-origin: 0 50%;
    animation-name: var(--choreo-rule, sect-rule);
    animation-fill-mode: both;
    animation-duration: auto;
    animation-timing-function: var(--e-std);
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
  }

  /* the diary unrolls left to right as the record enters — a reveal of what
     already stands, never a count-up; calm mode and the OS preference keep
     it still via .dy-static and the reduced-motion guard above */
  #month-register .dy-grid {
    animation-name: var(--choreo-reveal, month-unroll);
    animation-fill-mode: both;
    animation-duration: auto;
    animation-timing-function: var(--e-std);
    animation-timeline: view();
    animation-range: cover 0% cover 260px;
  }

  /* the current rung's rise inks itself upward — how far the cold reading
     has climbed this rank, drawn as the climb it is */
  .rank-ladder li.rk-cur::after {
    transform-origin: bottom;
    animation-name: var(--choreo-rule, rise-climb);
    animation-fill-mode: both;
    animation-duration: auto;
    animation-timing-function: var(--e-std);
    animation-timeline: view();
    animation-range: cover 0% cover 320px;
  }
}
@keyframes sect-rule {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}
@keyframes month-unroll {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}
@keyframes rise-climb {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}


/* ============================================================================
   BEHIND GLASS — desire drawn with light on a pane, never a padlock. The
   locked volume stays at full strength, exactly as handsome as the owned
   ones, only untouchable; the browse hover moves the room's light across
   the glass. Paper neutrals only: nothing here is wrong, nothing here is a
   celebration.
   ============================================================================ */
:is(#shelf .book.glassed, #locked-panel) .b-glass {
  position: absolute; inset: -2px;
  transform: translateZ(14px);
  border: 1.5px solid var(--carbon);
  background: linear-gradient(115deg, transparent 32%,
    rgba(255,255,255,.16) 40%, rgba(255,255,255,.05) 47%, transparent 56%);
  background-size: 220% 100%;
  background-position: 82% 0;
  transition: background-position calc(.38s * var(--motion, 1)) var(--e-std, ease);
  pointer-events: none;
  backface-visibility: hidden;
}
@media (hover: hover) {
  #shelf .book.glassed:hover .b-glass,
  #shelf .book.glassed:focus-visible .b-glass { background-position: 12% 0; }
}
#shelf .book.glassed .b-under { color: var(--steel); }
#shelf .book.glassed .b-cov i { background: var(--steel); }

/* THE CASE — a 402 answered as an object, not an apology. Quiet by charter:
   an invitation is neither an error nor a celebration. */
#locked-panel .lc-volwrap {
  width: 112px; height: 176px; margin: 44px auto 26px;
  perspective: 900px;
}
#locked-panel .b-vol { margin-left: 0; transform: rotateY(-20deg); }
@media (hover: hover) {
  #locked-panel .lc-volwrap:hover .b-vol { transform: rotateY(-5deg); }
}
body:not(.motion-low) #locked-panel .lc-in {
  animation: lc-settle calc(260ms * var(--motion, 1)) var(--e-std, ease) both;
}
@keyframes lc-settle { from { opacity: 0; transform: translateY(10px); } }

/* the appointment bears its date, in the room's own orthography */
.tonight-card .bill-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.tonight-card .bill-head .bh-date { font-size: .92em; letter-spacing: .18em; opacity: .85; white-space: nowrap; }


/* the ribbon settles to its true depth once, on arrival */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .vol-mark { animation: ribbon-settle .9s var(--e-std, ease) both; }
}
@keyframes ribbon-settle { from { height: 30px; } }

/* a finished volume is gilded on the top edge — t.e.g., the binder's mark of
   a book read to the end. Celebration only: it gates nothing, counts nothing. */
#shelf .book.fin .b-pages::before,
.vol.fin .vol-pages::before {
  content: ''; position: absolute; top: 0; left: 0; right: 0; height: 5px;
  background: linear-gradient(to bottom, var(--gold-leaf, #b8860b),
    color-mix(in srgb, var(--gold-leaf, #b8860b) 55%, var(--galley)));
}
#shelf .book.fin .b-spine span {
  color: var(--gold-leaf, #b8860b);
  text-shadow: 0 0 1px rgba(220,180,80,.4);
  font-weight: 700;
}
.vol.fin .vol-pages::before { height: 7px; }

/* ============================================================
   БИЛЕТ — a room, like Journey and Words.

   Same title page (room-head). The Russian rank is the headline until
   a name is written. The house figure stands in the margin. No second
   navy band, no oval, no matching cards.
   ============================================================ */
.profile-dossier {
  max-width: min(94%, 1040px);
  margin: 0 auto 72px;
}

.px-spread {
  display: grid;
  grid-template-columns: 1.4fr .6fr;
  gap: 0 36px;
  align-items: start;
}
.px-main { padding: 28px 8px 8px 0; min-width: 0; }
.px-margin {
  padding: 20px 0 8px;
  border-left: 1px solid var(--carbon);
  text-align: center;
}

@media (max-width: 820px) {
  .px-spread { grid-template-columns: 1fr; }
  .px-main { padding: 22px 0 8px; }
  .px-margin { border-left: none; border-top: 1px solid var(--carbon); padding: 22px 0; }
}

input.px-name {
  display: block;
  width: 100%;
  margin: 0 0 14px;
  padding: 0;
  border: 0;
  background: none;
  appearance: none;
  -webkit-appearance: none;
  font-family: var(--bill);
  font-size: clamp(56px, 9vw, 120px);
  font-weight: 900;
  text-transform: uppercase;
  letter-spacing: .01em;
  line-height: .82;
  color: var(--carbon);
  text-align: left;
  overflow-wrap: anywhere;
  cursor: text;
  border-radius: 0;
  box-shadow: none;
  min-height: 0;
}
input.px-name::placeholder { color: var(--rule-room); opacity: 1; }
input.px-name:hover { border: 0; box-shadow: none; }
input.px-name:focus {
  outline: none;
  border: 0;
  box-shadow: inset 0 -4px 0 var(--pine);
}

.px-ru {
  font-family: var(--serif);
  font-size: clamp(1.25rem, 2.4vw, 1.7rem);
  font-weight: 600;
  line-height: 1.25;
  margin: 0 0 4px;
  color: var(--carbon);
}
.px-ru.px-hero {
  font-size: clamp(34px, 5.5vw, 56px);
  line-height: 1.05;
  margin-bottom: 10px;
}
.px-name-invite { margin: 14px 0 0; }
.px-en {
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.05rem;
  line-height: 1.4;
  margin: 0;
  color: var(--steel);
}
.px-note, .px-climb {
  font-family: var(--serif);
  font-style: italic;
  font-size: .95rem;
  margin: 6px 0 0;
  color: var(--steel);
}

.px-tally {
  display: grid;
  grid-template-columns: auto auto 1fr;
  gap: 8px 28px;
  align-items: end;
  margin: 28px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--carbon);
}
.px-tal b {
  display: block;
  font-family: var(--mono);
  font-weight: 700;
  font-size: clamp(40px, 5.5vw, 58px);
  line-height: .85;
  font-variant-numeric: tabular-nums;
  color: var(--pine);
}
.px-tal span {
  display: block;
  margin-top: 4px;
  font-family: var(--bill);
  font-weight: 700;
  font-size: .78rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--steel);
}
.px-tal-since { align-self: center; }
.px-tal-since span {
  font-family: var(--serif);
  font-style: italic;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  font-size: .95rem;
}

.px-if {
  margin: 28px 0 10px;
  padding: 0 0 0 16px;
  border-left: 6px solid var(--pine);
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1.2rem, 2.2vw, 1.55rem);
  line-height: 1.35;
  color: var(--carbon);
}
.px-if.is-empty { color: var(--steel); border-left-color: var(--rule); }

.px-verb {
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.02rem;
  color: var(--pine);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  border-radius: 0;
  box-shadow: none;
}
.px-verb:hover { color: var(--carbon); text-decoration-thickness: 2px; }
/* an italic verb is 21px tall; the padding gives the thumb a 27px target
   and the negative margin gives the layout back what the padding took */
.px-verb { padding: 3px 0; margin: -3px 0; }

.px-acts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin-top: 22px;
}

/* --- the margin figure: a resident, not an icon in a box -------------- */
.px-guide-host {
  width: 168px;
  height: 200px;
  margin: 0 auto;
  color: var(--carbon);
  cursor: pointer;
  overflow: visible;
}
.px-guide-host svg { width: 100%; height: 100%; display: block; overflow: visible; }
.px-guide-plinth {
  width: 128px;
  height: 16px;
  margin: -22px auto 18px;
  background: var(--signal);
  border: 1px solid var(--carbon);
}
.px-guide-ru {
  font-family: var(--bill);
  font-weight: 800;
  font-size: clamp(28px, 4vw, 40px);
  line-height: .9;
  text-transform: uppercase;
  letter-spacing: .03em;
  color: var(--carbon);
}
.px-guide-en {
  font-family: var(--serif);
  font-style: italic;
  font-size: .95rem;
  color: var(--steel);
  margin: 6px 0 10px;
}

.px-case {
  display: none;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 4px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--carbon);
}
.px-case.open { display: flex; }
/* the guide picker's label stands still; the caret alone says open or shut */
#prof-btn-bench .px-caret { display: inline-block; transition: transform .2s ease; }
#prof-btn-bench[aria-expanded="true"] .px-caret { transform: rotate(180deg); }
.px-case button {
  background: none;
  border: 0;
  padding: 2px 7px;
  font-family: var(--bill);
  font-weight: 700;
  font-size: 1.15rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--steel);
  cursor: pointer;
  border-radius: 0;
}
.px-case button:nth-child(1) { font-size: 1.45rem; }
.px-case button:nth-child(5) { font-size: 1.05rem; }
.px-case button:hover { color: var(--carbon); }
.px-case button.active {
  background: var(--pine);
  color: var(--on-pine);
}

/* --- colophon rule at the foot of the ticket -------------------------- */
.px-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 28px;
  margin-top: 36px;
  padding: 14px 0 0;
  border-top: 2px solid var(--carbon);
}
.px-foot .px-verb { font-size: 1.05rem; }

/* the nav profile button is composed in 72-masthead.css — the cameo */

.journey-sheet {
  background: var(--galley);
  border: 2px solid var(--carbon);
  box-shadow: var(--shadow);
  padding: 24px 28px;
  margin-top: 16px;
}
.journey-sheet .chartbox { margin-bottom: 22px; }
.journey-sheet .chartbox:empty { display: none; }
/* the timeline keeps its own spine (30-choreo draws it); only the paper
   underneath has changed */
.journey-sheet .tl { margin-left: 8px; }

/* THE ROAD PLATE — the walked reader's map: last chapter struck, the open
   one, the next by their true names. It hangs on the shared measure like
   every other band; the stranger never sees it (the arena holds their one
   door). */
.road-plate { margin: 0 auto; }
.road-plate .rp-ti { font-family: var(--serif); font-size: 1.18rem; font-weight: 600;
  margin: 10px 0 2px; }
.road-plate .rp-line { margin-top: 6px; }
.road-plate .rp-act { margin-top: 14px; }

/* THE DAY'S GALLEY AND THE LEDGER (Букварь, slate 5-6). The job ticket
   states the day's three lines of work; the strip beneath it is fourteen
   days of fact — lit evenings, the run's leave-slips finally visible, a
   gilded cell for a day that carried its whole galley. Gold stays the
   seals' pigment, which a gilded day IS; ordinary evenings burn vermilion.
   Calm mode never renders any of this (the markup is gated in app.js). */
.tn-galley {
  margin: 14px 0 4px; padding-top: 10px;
  border-top: 1px dotted var(--rule);
}
.gly-head { font-size: var(--t-0); color: var(--steel); letter-spacing: .14em; }
.gly-gild { color: var(--reward, var(--gold-leaf, #b8860b)); letter-spacing: .08em; }
.gly-row {
  margin: 5px 0 0; font-size: var(--t-0); color: var(--carbon);
  padding-left: 14px; position: relative;
}
.gly-row::before {
  content: '○'; position: absolute; left: 0; top: 0;
  color: var(--steel); font-size: .82em; line-height: 1.9;
}
.gly-row.struck { color: var(--steel); text-decoration: line-through; text-decoration-thickness: 1px; }
.gly-row.struck::before { content: '●'; color: var(--verm, #a5342a); }
.tn-ledger {
  display: flex; gap: 4px; margin-top: 10px; align-items: center;
}
.tn-ledger .ldg {
  width: 12px; height: 12px; display: inline-block;
  border: 1px solid var(--rule); border-radius: 2px;
  background: transparent;
}
.tn-ledger .ldg-lit  { background: var(--verm, #a5342a); border-color: var(--verm, #a5342a); }
.tn-ledger .ldg-gild { background: var(--reward, var(--gold-leaf, #b8860b)); border-color: var(--reward, var(--gold-leaf, #b8860b)); }
.tn-ledger .ldg-leave {
  border-style: dashed; border-color: var(--verm, #a5342a);
  background: transparent;
}
.tn-ledger .ldg-wait { border-color: var(--carbon); }

/* THE RETURN-VISIT ASK rides the ledger it speaks about (signup research,
   30 Aug). Quiet furniture: a dotted rule, the reader's own numbers, and a
   decline that stands down on the cooldown ladder. */
.tn-keep { margin-top: 10px; padding-top: 8px; border-top: 1px dotted var(--rule); }
.tn-keep .wk-row { display: flex; gap: 10px; align-items: center; margin: 6px 0 0; }

/* ============================================================
   THE DAY'S LINE — one sentence a day, per room, as a puzzle (5 Sep 2026)
   ============================================================ */
.number-card { margin-top: var(--s3, 14px); }
.number-card .nb-date { color: var(--ink-2); margin: 2px 0 6px; }
.number-card .nb-line { font-family: var(--serif); font-size: var(--t-2); line-height: 1.45; margin: 4px 0 6px; }
.number-card .nb-blank { display: inline-block; min-width: 4.5em; border-bottom: 2px solid var(--carbon); color: transparent; letter-spacing: .1em; }
.number-card .nb-set { background: color-mix(in srgb, var(--signal) 40%, transparent); color: inherit; padding: 0 .12em; }
.number-card .nb-en { font-family: var(--serif); font-style: italic; color: var(--ink-2); margin: 2px 0 8px; }
.number-card .nb-opts { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin: 8px 0 10px; }
.number-card .nb-opts .quiz-opt { margin: 0; }
.number-card .nb-hint { margin: 0 0 6px; font-size: var(--t-0); }
.number-card .nb-row { justify-content: flex-start; }
@media (max-width: 480px) { .number-card .nb-opts { grid-template-columns: 1fr; } }

/* «В прошлом номере» — the last line read, quoted above the ribbon on return */
.recap-slip { margin: 0 0 var(--s3, 14px); padding: 10px 14px; border-left: 3px solid var(--book-ink, var(--carbon));
  background: color-mix(in srgb, var(--book-ink, var(--carbon)) 6%, transparent); }
.recap-slip .recap-line { font-family: var(--serif); font-style: italic; margin: 4px 0 2px; font-size: var(--t-0); }
.recap-slip .recap-tr { font-family: var(--serif); color: var(--ink-2); margin: 4px 0 0; font-size: var(--t-0); }
/* the closing slip's own teaser: where the ribbon lies */
.tn-next { font-family: var(--serif); font-style: italic; color: var(--ink-2); font-size: var(--t-0); margin: 10px 0 0; }

/* A deliberately kept line is already a personal collection. Show it even
   before there is enough study history to justify the full record sheet. */
#record-sect.sheet-empty.sheet-has-lines .rs-margin,
#record-sect.sheet-empty.sheet-has-lines #marginalia { display: block; }
#record-sect.sheet-empty.sheet-has-lines .rs-margin { margin-top: var(--breath, 24px); padding: 0; border: 0; }
.mg-kept-actions { display: flex; flex-wrap: wrap; gap: var(--seam, 12px); }
.mg-kept-actions button { min-height: 44px; text-align: left; }
.mg-kept-actions .q-del { color: var(--ink-2); }


/* ===== 66-rooms.css ===== */
/* ============================================================================
   66-rooms.css — THE OTHER ROOMS, BROUGHT UP TO THE HOUSE.

   The desk, the road and the lesson room learned to be objects. Words,
   Journey, Grammar, the reading page, the sign-in door and Settings were
   still generic h2 + list. Same constitution, same inks, same physics:
   decelerations only, vermilion as currency, gold for celebration, no
   second hue, no rounded cards. Each room now has a title page of its
   own, and a still frame of each is distinguishable from the others.
   ============================================================================ */

/* ---- A TITLE PAGE FOR EVERY ROOM that is not the desk. The Russian line
   LEADS, set in tracked serif capitals — this is the Russian room, and its
   rooms name themselves in Russian first. The English follows as the
   translator's line, italic and small. Thin-over-thick double rule. ---- */
.room-head {
  margin: 6px 0 22px;
  padding: 4px 0 16px;
  border-bottom: 2px solid var(--carbon);
  position: relative;
}
.room-head::before {
  content: ''; position: absolute; left: 0; right: 0; bottom: 3px;
  border-bottom: 1px solid var(--rule-room);
}
.room-head .rh-ru {
  font-family: var(--serif); font-weight: 600;
  letter-spacing: .16em; text-transform: uppercase;
  font-size: clamp(1.5rem, 3.4vw, 2.1rem); line-height: 1.1;
  color: var(--carbon);
}
.room-head h1, .room-head h2 {
  font-family: var(--serif); font-style: italic; font-weight: 500;
  text-transform: none; letter-spacing: .02em;
  font-size: 1.02rem; color: var(--steel);
  margin: 6px 0 4px; line-height: 1.3;
}
.room-head .rh-sub, .room-head #journey-sub, .room-head #words-census {
  font-family: var(--serif); font-style: italic;
  font-size: var(--type-body); line-height:1.6; color: var(--steel); margin: 0; max-width: 58ch;
}
/* THE ROOM'S SORT — not a giant watermark (a 92px flat glyph at 7% reads
   as clip-art), but the compositor's own sort standing in its case: small,
   boxed, crisp, top right, where a printer parks the signature mark. */
.room-head::after {
  content: attr(data-orn);
  /* left/bottom are RESET, not omitted: the course head is .masthead AND
     .room-head, and .masthead::after (the closing double rule, head.html)
     sets left:8% / bottom:0 — with left, right and width all resolved,
     left wins and the sort box gets dragged into the middle of the title. */
  position: absolute; right: 2px; top: 8px; left: auto; bottom: auto;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  font-size: 21px; line-height: 1;
  color: var(--steel);
  border: 1px solid var(--rule-room);
  background: var(--galley);
  box-shadow: 2.5px 2.5px 0 color-mix(in srgb, var(--carbon) 22%, transparent);
  pointer-events: none; user-select: none;
}
[data-theme="dark"] .room-head::after {
  background: var(--case); color: var(--steel);
  box-shadow: 2.5px 2.5px 0 rgba(0,0,0,.5);
}
@media (max-width: 560px) { .room-head::after { display: none; } }


/* ============================================================================
   WORDS — a type case, not a spreadsheet.
   ============================================================================ */
#v-words.show {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--steel) 10%, transparent), transparent 280px);
}

/* drawers: the four filters are compartments of one case. The active one
   is the open drawer — carbon face, galley type — not a filled pill. */
#word-filters {
  display: flex; flex-wrap: wrap; gap: 0; margin: 0;
  border: 2px solid var(--carbon); border-bottom: none;
  background: var(--galley);
}
#word-filters button {
  flex: 1 1 auto; min-width: 7.5em;
  border: none; border-right: 1px solid var(--carbon);
  border-radius: 0; background: transparent;
  padding: 10px 12px 9px;
  font-family: var(--bill); font-weight: 600;
  letter-spacing: .03em; text-transform: none; font-size: var(--type-control); line-height:1.5; white-space:normal; overflow-wrap:anywhere;
  color: var(--steel);
  box-shadow: none;
}
#word-filters button:last-of-type { border-right: none; }
#word-filters button:hover { background: var(--sheet); color: var(--carbon); }
#word-filters button.active {
  background: var(--carbon); color: var(--galley);
  box-shadow: none;
}
#word-filters button i {
  font-style: normal; font-family: var(--mono);
  font-size: .85em; letter-spacing: 0; margin-left: 7px;
  opacity: .7; font-variant-numeric: tabular-nums;
}
#word-filters button.active i { opacity: 1; color: var(--signal); }
#word-filters input {
  flex: 1 1 160px; margin: 0; min-width: 140px;
  border: none; border-left: 1px solid var(--carbon);
  background: var(--sheet); border-radius: 0;
  padding: 10px 14px; font-family: var(--serif); font-size: var(--type-control);
}
/* THE RING MUST SURVIVE. This line used to say `outline: none; background:
   var(--sheet)` — the background byte-identical to the resting rule right above
   it, so the whole of :focus was "delete the ring". The house ring at
   10-components.css:427 is written with :where(), which carries no specificity,
   so this selector beat it and the room's only text field had no keyboard
   indicator at all. The ring is inset because the field is flush inside the
   filter bar's carbon frame and an outer ring would climb over its neighbours. */
#word-filters input:focus-visible { outline: 2px solid var(--mark); outline-offset: -3px; }

/* the case itself: one sheet, compartments by book */
#wordlist {
  background: var(--galley);
  border: 2px solid var(--carbon);
  box-shadow: var(--shadow);
  padding: 0; margin: 0;
}
#wordlist li {
  padding: 9px 14px 9px 12px;
  border-bottom: 1px solid var(--rule);
  border-left: 4px solid var(--ink, transparent);
  gap: 12px;
  transition: background 160ms linear;
}
#wordlist li:last-child { border-bottom: none; }
#wordlist li:hover { background: var(--sheet); }
#wordlist .wf {
  font-family: var(--serif); font-size: 1.12rem;
  min-width: 0; overflow-wrap:anywhere; cursor: pointer;
  transition: color 140ms linear;
}
#wordlist .wf:hover { color: var(--signal-ink, var(--signal)); }
#wordlist .wl-cap {
  margin-top: 0;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 11%, var(--sheet));
  border-left-width: 4px;
  padding: 8px 14px;
  letter-spacing: .08em; text-transform: uppercase;
  font-size: var(--type-meta); line-height:1.5; overflow-wrap:anywhere;
}
#wordlist .wl-cap + li { border-top: none; }
#wordlist .warmbar {
  display: block; height: 4px; background: var(--rule); overflow: hidden;
}
#wordlist .warmbar i {
  display: block; height: 100%; width: calc(var(--v, 0) * 100%);
  background: var(--ink, var(--carbon));
}
#wordlist .warmbar.cold i { background: color-mix(in srgb, var(--ink, var(--carbon)) 45%, var(--rule)); }

@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) #wordlist .wl-cap {
    animation: wl-cap-in 280ms var(--e-std, ease) both;
  }
  @keyframes wl-cap-in {
    from { opacity: 0; transform: translateX(-6px); }
    to   { opacity: 1; transform: none; }
  }
}


/* ============================================================================
   JOURNEY — a patent, a cabinet, a chronicle. One almanac.
   ============================================================================ */
#v-journey.show {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--signal) 6%, transparent), transparent 300px);
}
#journey-patent .jp-doc {
  position: relative; overflow: hidden;
  background: var(--sheet);
  border: 2px solid var(--carbon);
  box-shadow: var(--lift);
  padding: 22px 24px;
}
#journey-patent .jp-doc::before {
  content: '';
  position: absolute; inset: 7px;
  border: 1px solid color-mix(in srgb, var(--gold-leaf, #b8860b) 45%, var(--rule));
  pointer-events: none;
}
#journey-patent .jp-cls {
  width: 86px; height: 86px;
  font-size: 2.2rem;
  box-shadow: inset 0 0 0 6px var(--sheet),
              inset 0 0 0 8px color-mix(in srgb, var(--gold-leaf) 55%, transparent),
              0 2px 0 color-mix(in srgb, var(--carbon) 25%, transparent);
}
#journey-patent .jp-ru { font-size: 1.5rem; }
#journey-patent .jp-rise {
  height: 4px; background: var(--rule); margin-top: 12px;
}
#journey-patent .jp-rise i { background: var(--signal); }

/* seals: a cabinet of impressions, not a grid of badges */
.jseals { gap: 8px; }
.jseal {
  background: var(--galley); border: 1px solid var(--rule);
  padding: 10px 12px; min-height: 62px;
  transition: border-color 160ms linear, background 160ms linear,
              box-shadow 180ms var(--e-std, ease);
}
.jseal:hover { border-color: var(--carbon); background: var(--sheet); }
.jseal.got {
  border-color: var(--carbon);
  background: var(--sheet);
  box-shadow: var(--lift-sm);
}
.jseal.got .jseal-wax {
  width: 42px; height: 42px; font-size: 1.1rem;
}
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .jseal.got .jseal-wax {
    animation: jseal-press 520ms var(--e-std, ease) both;
  }
  @keyframes jseal-press {
    from { transform: scale(1.18) rotate(-8deg); opacity: .4; }
    to   { transform: none; opacity: 1; }
  }
}

.journey-sheet {
  border-width: 2px;
  box-shadow: var(--lift);
}


/* ============================================================================
   GRAMMAR — a numbered reference, sixteen plates of one work.
   ============================================================================ */
#v-grammar.show {
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--carbon) 5%, transparent), transparent 280px);
}
.glist {
  display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 0;
  border: 2px solid var(--carbon); background: var(--galley);
}
@media (max-width: 640px) { .glist { grid-template-columns: 1fr; } }
.glesson-card {
  margin: 0;
  border-radius: 0;
  border: none;
  border-right: 1px solid var(--carbon);
  border-bottom: 1px solid var(--carbon);
  background: var(--galley);
  box-shadow: none;
  padding: 16px 18px 16px 58px;
  min-height: 88px;
  overflow: hidden;
  transition: background 160ms linear;
}
.glist .glesson-card:nth-child(even) { border-right: none; }
.glesson-card:hover { background: var(--sheet); transform: none; box-shadow: none; }
.glesson-card .g-no {
  font-family: var(--serif); font-style: italic;
  font-size: 1.35rem; color: var(--steel);
  left: 16px; top: 14px;
}
.glesson-card:hover .g-no { color: var(--signal-ink, var(--signal)); }
.glesson-card .gru {
  font-family: var(--bill); font-weight: 700;
  text-transform: uppercase; letter-spacing: .05em;
  font-size: var(--type-body); line-height:1.5; overflow-wrap:anywhere; color: var(--carbon);
}
.glesson-card .gen2 {
  font-family: var(--serif); font-style: italic;
  font-size: var(--type-body); line-height:1.5; overflow-wrap:anywhere; color: var(--steel); margin-top: 2px;
}
/* a printer's mark ghosts each article — sixteen different faces of one book */
.glesson-card::after {
  content: attr(data-orn);
  position: absolute; right: 6px; bottom: -10px;
  font-size: 52px; line-height: 1;
  color: var(--carbon); opacity: .12;
  pointer-events: none; user-select: none;
  transform: rotate(-12deg);
}
.glesson-card.g-read {
  background: color-mix(in srgb, var(--sheet) 70%, var(--galley));
}
.glesson-card.g-read .g-mark { color: var(--gold-leaf, #b8860b); font-size: 1.05rem; }
.glesson-card.g-read::after { opacity: .14; color: var(--gold-leaf, #b8860b); }

#v-lesson h2 {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .06em;
}
#lesson-body p { max-width: 62ch; }

/* THE PRESENTER'S DESK — the resident of the example's own book sits beside
   the article title and says one line. Grammar taught by the character it
   belongs to is the room's whole thesis in one header. */
.gles-head { display: flex; align-items: flex-start; gap: 16px; margin: 4px 0 14px; }
.gles-who { width: 64px; height: 64px; flex: 0 0 64px; margin-top: 4px;
  filter: sepia(.14) saturate(.9); }
.gles-who:empty { display: none; }
.gles-tw { min-width: 0; }
.gles-say {
  font-family: var(--serif); font-style: italic; font-size: var(--type-body);
  color: var(--ink-2p, var(--steel)); margin: 6px 0 0;
  opacity: 0; transition: opacity 400ms ease;
}
.gles-say.on { opacity: 1; }
.gles-say.on::before { content: '«'; } .gles-say.on::after { content: '»'; }

/* each example wears the pigment of the book it was cut from */
.gex {
  border-left: 4px solid var(--ink, var(--carbon));
  background: var(--sheet);
  padding: 14px 18px;
  margin: 16px 0;
}
.gex .gnote { border-top: 1px dotted color-mix(in srgb, var(--ink, var(--carbon)) 35%, transparent);
  margin-top: 8px; padding-top: 6px; }
/* the rows arrive on a small stagger — a page being laid out, not a dump */
body.motion-full .gex {
  opacity: 0;
  animation: gex-lay 460ms var(--e-std, ease) forwards;
  animation-delay: calc(120ms + var(--i, 0) * 90ms);
}
@keyframes gex-lay {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* the grammar cards carry their book's ink as a top band, and name the book */
.glesson-card { border-top: 3px solid var(--ink, transparent); }
.glesson-card .g-from {
  font-size: var(--type-meta); line-height:1.5; overflow-wrap:anywhere; letter-spacing: .03em; margin-top: 6px;
  color: color-mix(in srgb, var(--ink, var(--steel)) 72%, var(--steel));
  font-style: italic; font-family: var(--serif);
}
/* Grammar cards are already rendered inside native disclosures. Keep them
   visible on first open: a global lesson-index delay made later families
   appear empty for several seconds. */
#btn-grammar-back { margin-top: 18px; }

/* ============================================================================
   PRINTER'S STAMPS — the resident figures struck as emblems.
   The grab-bag of stock dingbats (❦ ✤ ❖ …) standing for books and chapters
   is retired wherever a book has a resident: the same silhouette that
   breathes in the margin is pressed, eyeless and single-ink, as the book's
   mark. One drawing, two lives — character and colophon — which is what
   brand coherence literally is.
   ============================================================================ */
.stamp { display: inline-block; width: 1em; height: 1em; vertical-align: -0.15em; }

/* the road: a book row's margin ghost */
.rd-stamp {
  position: absolute; right: 10px; top: 50%; width: 112px; height: 112px;
  transform: translateY(-50%) rotate(-8deg);
  color: var(--ink, var(--carbon)); opacity: .15; pointer-events: none;
  transition: opacity 240ms linear, transform 240ms var(--e-std, ease);
}
.rd-book:hover .rd-stamp { opacity: .26; transform: translateY(-50%) rotate(-4deg) scale(1.04); }
.rd-book.rd-open .rd-stamp { opacity: .07; }
.rd-stamp .stamp { width: 100%; height: 100%; }
.rd-book.rd-marked::after { content: none; }

/* the chapter band's leading mark (white on the pigment band) */
.rd-orn .stamp { width: 30px; height: 30px; vertical-align: middle; }

/* the lesson room: topic row, margin bloom, printed plate, wax seal */
.gx-topic i .stamp { width: 1.2em; height: 1.2em; vertical-align: -0.25em; }
.gx-bloom i .stamp { width: 1em; height: 1em; }
.gx-orn .stamp { width: 1.15em; height: 1.15em; }
.seal-body .stamp { width: 60%; height: 60%; }

/* the lesson room's corner ghost — the stamp in the chapter's own ink */
.gx-stage.gx-marked::after {
  content: '';
  width: clamp(220px, 40vmin, 420px); height: clamp(220px, 40vmin, 420px);
  background: var(--ghost-img) center / contain no-repeat;
  opacity: .07;
}
[data-theme="dark"] .gx-stage.gx-marked::after { opacity: .1; }

/* the grammar shelf: each article stamped by its source book */
.glesson-card.g-marked::after { content: none; }
.g-stampmark {
  position: absolute; right: 4px; bottom: -8px; width: 66px; height: 66px;
  transform: rotate(-10deg);
  color: var(--ink, var(--carbon)); opacity: .1; pointer-events: none;
}
.g-stampmark .stamp { width: 100%; height: 100%; }
.glesson-card.g-read .g-stampmark { color: var(--gold-leaf, #b8860b); opacity: .16; }

/* the reading page: title-page ghost. It used to stand at right:-2% top:-8%
   of the VIEW (scene-head was never positioned), which put a 178px smudge
   through the last letters of «ПЕРЕПИСЧИК» on every desktop — a printing
   accident, not a press mark. A ghost stamp belongs in the margin: anchored
   to the scene head itself, standing left of the title where paper is empty,
   and only where that margin exists. */
.scene-head { position: relative; }
.scene-head.sh-marked::after { content: none; }
.sh-stamp {
  display: none;
  position: absolute; left: -196px; top: -4px;
  width: 156px; height: 156px;
  transform: rotate(-11deg);
  color: var(--book-ink, var(--carbon)); opacity: .06; pointer-events: none;
}
@media (min-width: 1240px) { .sh-stamp { display: block; } }
.sh-stamp .stamp { width: 100%; height: 100%; }
.scene-head .orn .stamp { width: 1.15em; height: 1.15em; }
#read-blooms .rb i .stamp { width: 1em; height: 1em; }

/* ============================================================================
   THE STANDING LINE — the game, made visible at the top of the desk.
   Class numeral, rank, the rise to the next rung as a filling rule, and the
   run of burning evenings. Every unit is a fact about reading.
   ============================================================================ */
#standing-line { max-width: 720px; margin: 0 auto 20px; }
.stl {
  display: flex; align-items: center; gap: 16px;
  padding: 10px 14px;
  background: var(--sheet);
  border: 1px solid color-mix(in srgb, var(--carbon) 24%, transparent);
  box-shadow: var(--lift-sm);
}
.stl-cls {
  flex: 0 0 auto; width: 36px; height: 36px; display: grid; place-items: center;
  font-family: var(--serif); font-weight: 700; font-size: 1.2rem;
  border: 1px solid color-mix(in srgb, var(--carbon) 45%, transparent);
  box-shadow: inset 0 0 0 2px var(--sheet), inset 0 0 0 3px color-mix(in srgb, var(--carbon) 25%, transparent);
  color: var(--carbon); background: var(--galley);
}
/* at standing, the shield gilds — the room knowing you, in one square inch */
body[data-standing="3"] .stl-cls, body[data-standing="4"] .stl-cls {
  border-color: var(--signal); color: var(--signal);
}
.stl-mid { flex: 1; min-width: 0; display: grid; gap: 4px; }
.stl-rank { font-size: .78rem; letter-spacing: .03em; color: var(--ink-2p, var(--steel)); }
.stl-rank b { color: var(--carbon); }
.stl-bar {
  display: block; height: 4px; position: relative; overflow: hidden;
  background: color-mix(in srgb, var(--carbon) 12%, transparent);
}
.stl-bar i {
  position: absolute; inset: 0 auto 0 0; width: calc(var(--p, 0) * 1%);
  background: var(--signal);
  transition: width 900ms var(--e-std, ease);
}
.stl-next { font-size: .7rem; font-style: italic; font-family: var(--serif); color: var(--steel); }
.stl-run {
  flex: 0 0 auto; text-align: right; font-size: .78rem;
  color: var(--ink-2p, var(--steel)); display: grid; gap: 2px; justify-items: end;
}
.stl-run-top { white-space: nowrap; }
.stl-run b { font-size: 1.1rem; color: var(--carbon); font-family: var(--serif); }
.stl-run em { font-style: italic; font-family: var(--serif); font-size: .72rem; }
/* the one nudge loss aversion is allowed on the desk: quiet, italic, true */
.stl-keep { color: var(--signal-ink, var(--signal)); }
body.motion-full .stl-keep { animation: stl-glow 3.4s ease-in-out infinite; }

/* ============ THE HEAT OF THE PRACTICE SITTING ============
   The card warms in the word's own book-ink as the run climbs — the same
   escalation law as the lesson bench: felt at three, seen at six, gilded
   at nine. A miss cools it in one frame (data-heat drops to 0). */
/* The heat names its GLOW only — never the whole shadow. Writing box-shadow
   here threw away whatever depth the act had just set, so the slab under the
   card blinked out the moment a run reached three and blinked back on the
   first miss. The one rule that assembles the shadow is in
   90-chromatic-game.css; it reads --rev-glow and --rev-depth together. */
#v-review[data-heat="1"] #rev-card {
  --rev-glow: 0 0 24px -8px color-mix(in srgb, var(--ink, var(--carbon)) 38%, transparent);
}
#v-review[data-heat="2"] #rev-card {
  --rev-glow: 0 0 32px -6px color-mix(in srgb, var(--ink, var(--carbon)) 58%, transparent);
  border-color: color-mix(in srgb, var(--ink, var(--carbon)) 60%, var(--line));
}
#v-review[data-heat="3"] #rev-card {
  --rev-glow: 0 0 40px -4px color-mix(in srgb, var(--ink, var(--carbon)) 70%, transparent);
  /* nine right in a sitting is a hot streak, not a seal: --signal, the same
     brass the lesson bench wears at its own heat 3 (26-games.css:282). It was
     --gold-leaf, which meant the identical event was foil on one screen and
     signal on the next. The foil stays with the seal and the finished spine. */
  border-color: var(--signal);
}
.stl-flame { font-style: normal; color: var(--signal); margin-right: 4px; }
body.motion-full .stl-flame { animation: stl-glow 3.4s ease-in-out infinite; }
@keyframes stl-glow {
  0%, 100% { opacity: .7; }
  50% { opacity: 1; text-shadow: 0 0 9px color-mix(in srgb, var(--signal) 55%, transparent); }
}
@media (max-width: 620px) {
  .stl { flex-wrap: wrap; }
  .stl-mid { min-width: 100%; order: 3; }
}

/* ============================================================================
   ECHOES AND HAUNTINGS — the read books leak into the house.
   The almanac gets a second, quieter line when a finished book speaks; and
   once «Шинель» is read, its ghost drifts the desk at night, because Gogol
   put the ghost in the epilogue himself. Faint, slow, unclickable, rare.
   ============================================================================ */
.almanac .al-echo {
  display: block; margin-top: 5px;
  font-family: var(--serif); font-style: italic;
  color: var(--ink-2p, var(--steel));
}
.almanac .al-echo::before { content: '❧ '; color: var(--signal); font-style: normal; }

.haunt {
  position: fixed; top: 13vh; left: -120px;
  width: 92px; height: 92px; z-index: 3;
  pointer-events: none; opacity: 0;
  color: var(--carbon);
  filter: blur(1.2px);
}
.haunt .stamp { width: 100%; height: 100%; }
/* the Overcoat's shade: a sixteen-second crossing, barely there, once */
body.motion-full .haunt-drift { animation: haunt-drift 16s linear forwards; }
@keyframes haunt-drift {
  0%   { transform: translateX(0) translateY(0) rotate(-3deg); opacity: 0; }
  12%  { opacity: .1; }
  40%  { transform: translateX(42vw) translateY(2.5vh) rotate(2deg); }
  52%  { opacity: .12; }
  75%  { transform: translateX(80vw) translateY(-1vh) rotate(-2deg); }
  88%  { opacity: .07; }
  100% { transform: translateX(112vw) translateY(1vh) rotate(1deg); opacity: 0; }
}
/* the Nose does not drift. The Nose has appointments. */
body.motion-full .haunt-dash { animation: haunt-dash 2.8s cubic-bezier(.5, 0, .8, 1) forwards; }
@keyframes haunt-dash {
  0%   { transform: translateX(0) rotate(6deg); opacity: 0; }
  18%  { opacity: .13; }
  100% { transform: translateX(115vw) rotate(10deg); opacity: 0; }
}
[data-theme="dark"] .haunt { color: #e8e0d2; }
/* stillness law: no haunting may cross a page of running text */
body:has(#v-read.show) .haunt, body.gx-on .haunt { display: none; }


/* ============================================================================
   READING — a title page, then the sheet. Nothing moves beside the prose.
   ============================================================================ */
.scene-head {
  position: relative;
  overflow: hidden;
  padding: 8px 0 28px;
}
.scene-head h2 {
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(1.7rem, 3.6vw, 2.4rem);
  margin: 8px 0 4px;
  color: var(--carbon);
  position: relative; z-index: 1;
}
.scene-head .vy {
  letter-spacing: .28em; font-size: var(--t-00); color: var(--steel);
}
.scene-head::after {
  content: attr(data-orn);
  position: absolute; right: -2%; top: -12%;
  font-size: clamp(64px, 12vw, 140px); line-height: 1;
  color: var(--book-ink, var(--carbon)); opacity: .04;
  pointer-events: none; user-select: none;
  transform: rotate(-14deg);
}
[data-theme="dark"] .scene-head::after { opacity: .08; }
.scene-head .orn {
  font-size: 1.4rem; margin-top: 14px;
  color: var(--book-ink, var(--signal));
}

#v-read #text {
  border-top-width: 5px;
}

/* the gloss card wears the word's book, same band the practice card wears */
#pop {
  border-top: 4px solid var(--ink, var(--carbon));
  box-shadow: 0 1px 0 rgba(255,255,255,.5) inset,
              0 3px 6px color-mix(in srgb, var(--carbon) 20%, transparent), 0 14px 30px -12px color-mix(in srgb, var(--carbon) 50%, transparent);
}
#pop .pw { letter-spacing: .01em; }


/* ============================================================================
   THE DOOR — sign-in is being issued a reader's ticket, not filling a form.
   Settings is the same object, opened from the other side.
   ============================================================================ */
#ov-key.show, #ov-settings.show {
  display: flex; align-items: center; justify-content: center;
}
#ov-key .panel, #ov-settings .panel {
  border: 2px solid var(--carbon);
  border-radius: 0;
  box-shadow: var(--lift-lg);
  background: var(--sheet);
  position: relative;
  width: min(440px, calc(100vw - 36px));
}
#ov-key .panel::before, #ov-settings .panel::before {
  content: '';
  position: absolute; inset: 8px;
  border: 1px solid var(--rule);
  pointer-events: none;
}
#key-panel .k-plate, #settings-panel .k-plate {
  text-align: center;
  margin: 0 0 16px;
  padding: 16px 12px 14px;
  background: color-mix(in srgb, var(--signal) 7%, var(--sheet));
  border: 1px solid var(--carbon);
  border-bottom-width: 3px;
}
#key-panel .k-plate .k-orn, #settings-panel .k-plate .k-orn {
  /* the compositor's sort in ink, not gold — gold is celebration, and a
     sign-in plate is furniture */
  display: block; color: var(--carbon); font-size: 22px; line-height: 1;
  font-family: var(--bill); font-weight: 700;
}
#key-panel .k-plate .k-ru, #settings-panel .k-plate .k-ru {
  display: block; font-family: var(--bill); font-weight: 700;
  letter-spacing: .14em; font-size: .72rem;
  color: var(--carbon); margin-top: 8px;
}
#key-panel .k-plate .k-en, #settings-panel .k-plate .k-en {
  display: block; font-family: var(--serif); font-style: italic;
  font-size: .88rem; color: var(--steel); margin-top: 3px;
}
#key-panel input[type=email],
#key-panel input[type=password],
#key-panel input[type=text] {
  clip-path: none;
  border: 1px solid var(--carbon);
  background: var(--galley);
  padding: 12px 14px;
  font-family: var(--serif); font-size: 1.05rem;
}
#key-panel input:focus { border-color: var(--signal); background: var(--sheet); }

/* THE TABS ARE A RULE, NOT A BLACK SLAB. 10-components filled the active
   tab with carbon; this sheet then set the type to carbon too — Sign in
   became an empty black rectangle next to Create an account. Paper tabs,
   vermilion underline, the same language as the nav. The h3 is already
   said by the plate and the tabs, so it does not also wear the card-cap bar. */
/* Only the TABBED render says its title twice (plate + tabs). Every other
   dress of this panel — reset, confirm-your-address, "Check your email",
   the file edition, "Your key" — has the h3 as its ONLY title, and hiding
   it beheaded them all. Scope the trim to the render that earned it. */
#key-panel:has(.k-tabs) > h3, #settings-panel > h3 {
  display: none !important;
}
.k-tabs {
  display: flex; gap: 0; margin: 0 0 14px;
  /* a LIGHT base rule: when it was carbon, the inactive tab wore a heavier
     line than the active one's underline, and the state read backwards */
  border: none; border-bottom: 2px solid var(--rule);
  background: transparent;
}
.k-tabs button {
  flex: 1; min-height: 44px;
  background: transparent !important; color: var(--steel) !important;
  border: none !important; border-bottom: 2px solid transparent !important;
  margin-bottom: -2px;
  font-family: var(--bill); font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase; font-size: .72rem;
  box-shadow: none !important;
}
.k-tabs button.on {
  background: transparent !important; color: var(--carbon) !important;
  border-bottom-color: var(--carbon) !important; font-weight: 700;
}

@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .overlay.show .panel {
    animation: door-lay 340ms var(--e-std, ease) both;
  }
  @keyframes door-lay {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
  }
}


/* ============================================================================
   THE DESK GREETING — bilingual, the room addressing you by rank.
   ============================================================================ */
#v-home .greeting {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1.15rem, 2.2vw, 1.45rem);
  text-align: center;
  margin: 18px 0 4px;
  color: var(--carbon);
  letter-spacing: .01em;
}
#v-home .greeting .gr-ru { font-style: italic; }
#v-home .greeting .gr-rest { color: var(--steel); font-style: italic; }
#v-home .greeting .gr-hour {
  display: block; font-family: var(--bill); font-style: normal;
  font-size: var(--t-00); letter-spacing: .2em; text-transform: uppercase;
  color: var(--signal); margin-bottom: 4px;
}
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) #v-home .greeting .gr-ru {
    animation: gr-in 640ms var(--e-std, ease) both;
  }
  @keyframes gr-in {
    from { opacity: 0; letter-spacing: .08em; }
    to   { opacity: 1; letter-spacing: .01em; }
  }
}

@media (max-width: 760px) {
  #word-filters button { min-width: 0; flex: 1 1 42%; }
  #word-filters input {
    flex: 1 1 100%; border-left: none;
    border-top: 1px solid var(--carbon);
  }
}

/* the friend's bubble is a plate, not a chat bubble */
#v-home .friend-strip .fr-bubble {
  background: var(--sheet);
  border: 1px solid var(--carbon);
  border-radius: 0;
  box-shadow: var(--lift-sm);
}


/* ============================================================================
   BEHIND GLASS — the under-line names the pane, so the still frame tells
   the truth without a padlock.
   ============================================================================ */
#shelf .book.glassed .b-under {
  font-family: var(--serif); font-style: italic;
}
/* ON ITS OWN LINE, and the interpunct goes. All seven glassed under-lines
   measure 34px against a 17px line-height on a phone — every one of them
   wraps — and what wrapped was the separator, so seven tiles trailed an
   orphan «·» on a line of its own. A block does not need a separator. */
#shelf .book.glassed .b-under::after {
  content: attr(data-access);
  display: block;
  font-family: var(--bill); font-style: normal;
  font-size: .72em; letter-spacing: .08em; text-transform: uppercase;
  color: var(--steel);
}


/* ============================================================================
   PRACTICE HEAD — the sitting names itself the way every other room does.
   ============================================================================ */
#v-review .rev-head h2 {
  font-family: var(--bill); text-transform: uppercase; letter-spacing: .12em;
  font-size: 1.35rem;
}


/* standing gilds the room-head rule the same way it gilds the masthead */
body[data-standing="2"] .room-head,
body[data-standing="3"] .room-head,
body[data-standing="4"] .room-head { border-bottom-color: color-mix(in srgb, var(--signal) 55%, var(--carbon)); }
/* and the sort gilds at the top rung — in --signal, like its sibling line above
   and like every other standing mark (29-alive.css:216). Standing is an
   everyday accrual, not a seal beat, so the ceremony foil has no business here.
   The old rule also carried opacity:.14, borrowed from .glesson-card.g-read::after
   where the sort is a bare glyph; this one is a bordered box on --galley, so .14
   faded the whole case to nothing — the opposite of a gild. */
body[data-standing="4"] .room-head::after {
  color: var(--signal);
  border-color: color-mix(in srgb, var(--signal) 55%, var(--rule-room));
}


/* ============================================================================
   THE STORY DOOR — the continue-card is a title page for the next instalment,
   not a dashboard tile. Same plate language as the patent and the ticket.
   ============================================================================ */
.continue-card {
  border: 2px solid var(--carbon);
  box-shadow: var(--lift);
  background: var(--sheet);
  padding: 22px 24px;
}
.continue-card .sc-title { font-family: var(--serif); font-size: 1.55rem; margin: 6px 0 4px; }
.continue-card .sc-sub { font-style: italic; color: var(--steel); font-family: var(--serif); margin-bottom: 12px; }
.cov-rise { height: 7px; background: var(--rule); margin: 10px 0 6px; }
.cov-rise i { background: var(--carbon); }
.cov-rise b { background: var(--signal); width: 2px; }

#v-course .masthead.room-head {
  background: transparent;
  text-align: left;
  padding-bottom: 18px;
}
#v-course .masthead.room-head h1 { margin: 6px 0 10px; }

/* the warm-up banner on the reading page is a plate, not a yellow alert */
.prep-banner {
  background: var(--sheet);
  border: 2px solid var(--carbon);
  border-left: 5px solid var(--book-ink, var(--carbon));
  padding: 14px 18px;
  font-family: var(--serif);
  margin: 0 0 18px;
}
.take-all button {
  font-family: var(--bill); letter-spacing: .08em; text-transform: uppercase;
}


/* ===== 70-shell.css ===== */
/* ============================================================================
   THE HEADER, WHICH WAS UNUSABLE ON A PHONE.

   Measured at 390px: the bar is 390 wide, the navigation gets 184, and its
   buttons need 421. It does scroll — overflow-x is auto — but nothing on
   screen says so, so Grammar and Journey were simply not reachable as far as
   anyone could tell. Meanwhile three theme swatches were taking 134 of those
   390 pixels, because the component sheet turned 21px round dots into large
   hard squares. On the widest surface in the product that reads as a decision;
   on the narrowest it costs a third of the bar to change a colour.
   ============================================================================ */

/* --- the swatches go back to being dots ---------------------------------
   Round, small, and spaced. They were also reading as one three-striped
   object rather than three choices, because hard 1px borders with no gap
   between them make a single bordered strip. The chosen one now carries the
   same red underline the active navigation tab does — one language for "this
   is where you are", used in both places rather than invented twice. */
.theme-dots {
  display: flex; gap: 8px; flex: none;
  /* No container. Three dots in a bordered box read as one control with three
     parts rather than three choices — and the box was the last thing making
     the swatches look like a single striped object. */
  border: none; background: none; padding: 0; box-shadow: none;
}
.theme-dots button {
  width: 20px; height: 20px; border-radius: 50%;
  border: 1px solid var(--rule-room); padding: 0;
  position: relative; box-shadow: none;
}
.theme-dots button.on {
  border-color: var(--carbon);
  box-shadow: 0 0 0 2px var(--room), 0 0 0 3px var(--carbon);
}
.theme-dots button:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }

/* --- the navigation must ADMIT that it scrolls --------------------------
   A scroll container with no visible edge is a scroll container nobody
   scrolls. The mask fades the last few pixels of the rail, which is the one
   affordance that survives having no scrollbar at all — which is the case on
   every phone. It disappears once the rail fits. */
.nav-tabs {
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x proximity;
  mask-image: linear-gradient(90deg, #000 calc(100% - 22px), transparent);
  -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 22px), transparent);
}
.nav-tabs::-webkit-scrollbar { display: none; }
.nav-tabs button { scroll-snap-align: start; flex: none; }

@media (min-width: 900px) {
  /* Nothing to scroll on a desk, so nothing to fade. */
  .nav-tabs { mask-image: none; -webkit-mask-image: none; }
}

@media (max-width: 560px) {
  /* Tighter tracking and padding buy back roughly a fifth of the rail without
     touching the type size, which is the thing that must not shrink. */
  .bar { gap: 8px; padding: 8px 12px; }
  .nav-tabs { gap: 2px; }
  /* .bar prefix ON PURPOSE. The base `.nav-tabs button` rule below in this
     same file is (0,1,1) and sits LATER in source order; a media query adds no
     specificity, so every economy written in here was silently discarded and
     the rail kept its full desktop tracking on a 390px phone. */
  .bar .nav-tabs button { padding: 8px 7px; letter-spacing: .06em; }
  .theme-dots { gap: 6px; }
}

@media (max-width: 420px) {
  /* the one width where the rooms genuinely do not fit: a last, small step
     down in tracking and type so JOURNEY stops rendering as JOU behind the
     fade — a clipped word reads as a broken rail, not an economy */
  .bar { gap: 6px; padding: 8px 8px; }
  .bar .nav-tabs button { padding: 8px 5px; letter-spacing: .02em; font-size: .93em; }
  .theme-dots button { width: 18px; height: 18px; }
}
@media (max-width: 480px) {
  /* the lamp and the theme have a home on the plate; the rail spends
     its pixels on the five rooms and the cameo. */
  .bar .theme-dots { display: none; }
}
/* the settings panel's own dots keep their size everywhere */
#settings-panel .theme-dots { display: inline-flex; }
#settings-panel .theme-dots button { width: 20px; height: 20px; }

/* --- keyboard focus is visible everywhere in the shell ------------------
   A focus ring the browser drew for us is a focus ring that disappears the
   moment anything sets an outline, and several things do. */
.bar button:focus-visible,
.nav-tabs button:focus-visible {
  outline: 2px solid var(--signal); outline-offset: 2px;
}

/* ============================================================================
   THE ROOM WAS 900px WIDE ON A 1440px SCREEN.

   Every view was capped at 900, so on an ordinary laptop 540 pixels of the
   room — a third of it — was empty margin on either side while the type case,
   the one thing on the desk that wants width, was squeezed into 826.

   The cap was right for TEXT and wrong for everything else. A measure of 900px
   is already generous for reading; a rack of thirty compartments and fourteen
   book spines is furniture, and furniture in a narrow column reads as a
   diagram of furniture. So the two get different widths instead of one
   compromise: prose stays at a readable measure, the room opens up.
   ============================================================================ */

.view {
  max-width: min(1280px, 94vw);
  margin-inline: auto;
}

/* Running prose retains its own bounded measure. Room containers can be
   wider when their contents use separate columns. */
/* ONE LEFT EDGE ON THE DESK. The prose blocks were capped at 900 and centred
   while the case took 1040, so nothing lined up with anything — three
   different left edges down one screen. They share the case's width now, and
   the TEXT inside them keeps its own measure, which is the thing that actually
   has to stay short. A container is not a line length. */
#v-home > .friend-strip,
#v-home > .aim-line,
#v-home > .greeting,
#v-home > .ticket-card,
#v-home > .grid2,
#v-story > *,
#v-words > *, #v-grammar > *, #v-journey > *, #v-review > * {
  max-width: 900px;
  margin-inline: auto;
}
#v-home > .friend-strip, #v-home > .aim-line, #v-home > .greeting,
#v-home > .ticket-card, #v-home > .grid2 {
  max-width: min(94%, 1040px);
}
/* A single sentence set across 840px is a sentence nobody reads twice. The
   bubble holds a comfortable measure inside a container that is wider than it,
   which is what lets the block line up without the line getting long. */
#v-home .friend-strip .bubble, #v-home .friend-strip p { max-width: 62ch; }

/* The masthead gets the room. */
#v-home > .masthead { max-width: none; }

@media (max-width: 1000px) {
  /* Below this the room is not wider than the measure anyway, so everything
     shares one column and nothing needs its own rule. */
  .view { max-width: 94vw; }
  #v-home > *, #v-story > * { max-width: none; }
}

/* --- NOTHING MAY OVERLAP -------------------------------------------------
   The two figures on the desk are absolutely positioned in the margins, which
   was safe while the room was 900px and everything else sat inside it. Now
   that the case reaches for the room, they can land on top of it. Both are
   pushed outside the content box, and below 1400px — where there is no margin
   left to stand in — the margin figure steps aside rather than covering
   anything. */
/* .margin-mate is retired (owner's clean-page ruling, 2026-08-22 night):
   the resident lives at the thresholds — epigraph slip and road-door —
   and nothing stands beside running prose. placeMate no longer creates
   the element; show() sweeps any stale one. */

/* --- AND NOTHING MAY PUSH THE PAGE SIDEWAYS -----------------------------
   Measured: the document scrolled horizontally at every width — 2060px of
   scrollWidth in a 1440px window, and 561 in a 390 one. Two unrelated causes,
   and the first is why a search of every element found nothing.

   The masthead's type-metal band is a full-bleed pseudo-element: `left: -50vw;
   right: -50vw`, so it reaches half a viewport past each edge on purpose. A
   pseudo-element is not in `body *`, so it is invisible to the obvious check
   and shows up only as a number that will not go down.

   `overflow-x: clip` rather than `hidden`: clip does not create a scroll
   container, so sticky positioning, scroll-driven animations and
   scrollIntoView all keep working. `hidden` would have quietly broken the
   header and every reveal in 30-choreo.css. */
html, body { overflow-x: clip; }

/* The navigation is a flex item, and a flex item's default min-width is auto —
   it refuses to shrink below its content, so its 421px of tabs pushed the bar
   past 390 no matter what overflow it was given. This is the line that makes
   the scroll rail actually scroll instead of widening the page. */
.bar { min-width: 0; }
.nav-tabs { min-width: 0; }


/* THE SLIP. A note is LAID on the desk — decelerating, never bouncing (the
   wax holds the only licence to overshoot) — ruled under its heading like a
   catalogue card. No foley: a slip may carry a refusal, and sound never
   attends friction. */
.toast.show { animation: none; }
.toast .tt { border-bottom: 1px solid var(--rule); padding-bottom: 3px; margin-bottom: 6px; }

/* ============================================================================
   LAMP-LIGHTING. The themes are lamps, not palettes: the new light spreads
   from the swatch the reader pressed, one decelerating circle, while the old
   room stands there being lit over. Without View Transitions or with motion
   reduced, the body's own colour ease still answers.
   ============================================================================ */
:root[data-lamp] header { view-transition-name: none; }
:root[data-lamp]::view-transition-old(root) { animation: none; mix-blend-mode: normal; }
:root[data-lamp]::view-transition-new(root) {
  mix-blend-mode: normal;
  clip-path: circle(0px at var(--lamp-x, 100%) var(--lamp-y, 26px));
  animation: lamplight calc(560ms * var(--motion, 1)) var(--e-std, ease) both;
}
@keyframes lamplight {
  to { clip-path: circle(142vmax at var(--lamp-x, 100%) var(--lamp-y, 26px)); }
}

/* ============================================================================
   THE PRESS LAW — NOTHING IN THIS SHOP IS DIE-CUT ROUND.

   The public pages are set as an 1842 broadsheet: hard edges, carbon frames,
   objects lying on a bench. The app was still wearing 8-to-16px corner radii
   on every card, button and chip — the one shape language no letterpress
   shop ever produced, and the first thing that makes a surface read as
   template-made. Paper is cut straight. The exceptions below the law are
   the things that are ROUND IN THE WORLD: a wax seal, a lamp, a drop of ink.
   ============================================================================ */
.card, .btn, .icon-btn, .nav-tabs button, .tile, .issue, .scene-end,
.quiz-opt, #rev-card, .rev-btns button, .ltile, .filters button,
.glesson-card, .gchip, .overlay .panel, .toast, .book, .resume-card,
.scene-epigraph, .scene-epigraph .ep-btn, .first-read-banner,
.guide .g-bubble, textarea, select, input[type="text"],
.meter, .meter > i, .warmbar, .warmbar > i {
  border-radius: 0;
}
/* the streak slug: a piece of type, not a pill */
.streak-chip { border-radius: 0; font-family: var(--bill); letter-spacing: .06em; }

/* --- the rail is set in the house bill face -----------------------------
   The tabs were 0.86rem system sans — a toolbar. They are now the same
   condensed wood type the wordmark and the broadsheet heads are cut from,
   which is what makes the bar read as THIS product's bar and nobody
   else's. Size up, tracking open, carbon at rest, vermilion where you are. */
.nav-tabs button {
  font-family: var(--bill); font-weight: 600; font-size: 1.04rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--steel);
}
.nav-tabs button:hover { color: var(--carbon); }
.nav-tabs button.active { color: var(--signal-ink); font-weight: 700; }
.brand { font-family: var(--bill); font-weight: 700; letter-spacing: .12em; }

/* --- the light chips are squares of the papers they set -----------------
   Small stays the law (the 480px budget above is untouched); only the die
   changes: three straight-cut swatches of the three stocks, not three
   traffic dots. The chosen one is framed in carbon like a locked-in sort. */
.theme-dots button, #settings-panel .theme-dots button { border-radius: 0; }
.theme-dots button.on {
  box-shadow: 0 0 0 2px var(--room), 0 0 0 3px var(--carbon);
}

/* the settings lamp sits on the text baseline, not floating */
#btn-settings { display: inline-flex; align-items: center; }
#btn-settings svg { display: block; }

/* --- buttons speak in the compositor's caps ------------------------------
   Chrome labels (Listen, Stop here, Back up…) take the bill face in caps —
   the register every label on the public broadsheet already uses. The
   reading page itself is untouched: prose is prose. */
.btn, .icon-btn {
  font-family: var(--bill); font-weight: 600; text-transform: uppercase;
  letter-spacing: .07em; font-size: .95rem;
}
.btn:hover, .icon-btn:hover { border-color: var(--carbon); }
.btn.primary:hover { border-color: var(--signal); }

/* --- the first-minute notice is a printed bill, not an alert box --------
   Carbon frame, hard offset shadow (an object on the desk), the head cut
   in wood type at poster weight, and the three-line meter set as three
   slugs of lead that fill with ink — not three circles. */
.first-read-banner {
  border: 1px solid var(--carbon);
  box-shadow: 6px 7px 0 color-mix(in srgb, var(--carbon) 82%, transparent);
  background: var(--sheet);
}
.first-read-banner h2 {
  font-family: var(--bill); font-weight: 800; font-size: 2rem;
  line-height: .98; text-transform: uppercase; letter-spacing: .01em;
  text-wrap: balance;
}
.first-read-banner .fr-meter i {
  width: 26px; height: 5px; border-radius: 0;
  border: 1px solid var(--signal-ink); background: transparent;
}
.first-read-banner .fr-meter i.lit { background: var(--signal-ink); transform: none; }

/* ============================================================================
   THE PIGMENT CASE ON THE SHELL — each room owns one period pigment, so the
   reader learns the house by colour the way they learn it by name:
   vermilion is the call (tonight, practice), malachite is growth (the road,
   a right answer), lazur is knowledge held (the bill, the words), gold leaf
   is ceremony (a month that fills, a volume closed). Flat blocks, no washes.
   ============================================================================ */

/* the desk bands take their room's pigment — flat playbill blocks, instantly
   legible as different errands before a single word is read. Un-pigmented
   bands default to pine: the house cloth, not funeral carbon. */
.bill-head { background: var(--pine); color: var(--on-pine); }
.tonight-card .bill-head { background: var(--pine); color: var(--on-pine); }
.bill-head[data-pig="malachite"] { background: var(--malachite); color: var(--on-malachite); }
.bill-head[data-pig="lazur"]     { background: var(--lazur);     color: var(--on-lazur); }
.tonight-card .bill-head[data-pig="verm"],
.bill-head[data-pig="verm"]      { background: var(--signal);    color: var(--on-signal, #f6efe1); }

/* the nav learns the same code: the room you are in shows its pigment */
.nav-tabs button[data-nav="words"].active    { color: var(--lazur-ink); }
.nav-tabs button[data-nav="course"].active   { color: var(--malachite-ink); }
.nav-tabs button[data-nav="practice"].active,
.nav-tabs button[data-nav="home"].active     { color: var(--signal-ink); }
.nav-tabs button[data-nav="journey"].active  { color: var(--signal); }
.nav-tabs button[data-nav="words"].active::after    { background: var(--lazur); }
.nav-tabs button[data-nav="course"].active::after   { background: var(--malachite); }
.nav-tabs button[data-nav="journey"].active::after  { background: var(--signal); }

/* the ledger chip takes on material as the month fills: warm at three
   sittings, gilt at seven — leaf is earned, never given */
.streak-chip.chip-warm { border-color: var(--signal); }
.streak-chip.chip-gilt {
  border-color: var(--signal); color: var(--signal);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--gold-hi) 40%, transparent);
}
.streak-chip.chip-gilt .fl { color: var(--gold-hi); }
.streak-chip.chip-gilt #streak-n { color: var(--signal); font-weight: 700; }


/* ============================================================================
   THE PRESS LAW, THIRD AMENDMENT — three surfaces that were cut after the law
   was written and never went under the guillotine with the rest.

   The law above lists the shapes by hand, so anything added later keeps
   whatever radius it was born with. Three had:

     · #tour-panel .t-bubble ....... 4px 16px 16px 4px
       The worst of them, because the sheet already forbids it in words. Its
       own family rule (10-components §speech) sets all three character
       bubbles to 0 under the comment «a caption card, HARD-EDGED, with a
       square tail rather than a chat bubble's rounded one» — and then, 400
       lines further down the same file, this one bubble is re-rounded into
       exactly the chat bubble that comment refuses. Its two siblings,
       .g-bubble and .fr-bubble, stayed square. This restores the third.

     · #rev-card .still-chip ....... 999px
       A full pill — the one die the law names as the tell of a template. The
       chips beside it (.gchip, .filters button) are square.

     · .road-door .................. 10px
       A card, and the law's own text is about «8-to-16px corner radii on
       every card». Below 1400px it is the first meeting with the resident,
       so it is a poor place to be the only rounded object on the screen.

   Specificity, not sheet order, is why these need their own block: two of the
   three are ID-qualified (1,1,0) and the law's list is classes (0,1,0), so
   adding the names to it would not have taken.
   ============================================================================ */
#tour-panel .t-bubble,
#rev-card .still-chip,
.road-door { border-radius: 0; }


/* ===== 72-masthead.css ===== */
/* ============================================================================
   THE RUNNING HEAD — imprint, rooms, the ticket number.

   The right-hand mark is a compositor's sort (№), the same character the
   Table of Ranks is set in. It takes the rooms' own tab language — wood
   type, yellow when you are there — so the rail is one voice, not a row
   of invented icons.
   ============================================================================ */

.bar { max-width: min(1280px, 96vw); }

.bar-rule {
  flex: none;
  width: 1px;
  align-self: stretch;
  margin-block: 12px;
  background: currentColor;
  opacity: .3;
}

.nav-rooms { flex: 0 1 auto; }

.nav-ident {
  flex: none;
  overflow: visible;
  mask-image: none;
  -webkit-mask-image: none;
  /* Sign up is a yellow plate and the open Account tab is a yellow plate;
     at gap 0 the two fused into one wide block reading «SIGN UP ACCOUNT»
     (seen at every width, 9 Sep 2026). Six pixels keeps them two doors. */
  gap: 6px;
}

/* № is Geologica — the house numeral face — sitting in the bill tab */
#nav-profile {
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
}
#nav-prof-cls {
  font-family: var(--mono);
  font-weight: 700;
}

#nav-profile::after { display: none; }

/* Sign up wears the same plate as an active room — yellow, carbon foot, no
   pill — so the door is as loud as Desk. Hidden once the key is home; the
   ticket number then holds the right edge.
   AND THE SAME TYPE. The rooms left the wood type for the sans
   (102-reading-challenge.css), and this rule kept the condensed capitals, so
   «SIGN UP» stood between «Membership» and «Account» in a face neither of
   them wears. It takes the rail's type now, from the .nav-tabs rule. */
#nav-signup {
  color: var(--on-reward, var(--carbon));
  background: var(--reward, var(--signal));
  border: 0;
  border-bottom: 4px solid var(--carbon);
  box-shadow: 0 3px 0 var(--carbon);
}
#nav-signup:hover { color: var(--on-reward, var(--carbon)); }
#nav-signup::after { display: none; }
#nav-signup[hidden] { display: none !important; }

/* The name plate: once the key is home, the yellow door gives way to the
   reader's own name, set like the imprint — the rail itself answers "who
   am I here" at a glance. It opens the same ticket menu as №, where the
   door out is. Longer names are clipped, never wrapped onto the rooms. */
#nav-reader {
  font-family: var(--bill);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  max-width: 14ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#nav-reader::after { display: none; }
#nav-reader[hidden] { display: none !important; }

/* HOW IT WORKS — the one <a> on a rail of <button>s, so it is dressed by
   hand, and in the ACCOUNT LABEL'S dress rather than a room's: the sans at
   .75rem, 400, .04em (#nav-profile, above and in 99-experience.css), the
   arcade ink, 46px tall. It is a door out of the room, like the ticket, not
   a room, and the two quiet doors now bracket Membership and Sign up.
   THE SIZE IS ALSO WHAT LETS IT STAND. The bar is capped at 1280px while
   the fluid root keeps growing, so the rail is tightest on the widest
   screens: in the rooms' own dress (control size, 650, 14px padding) the
   link fitted beside the wordmark only from 1280 to 1536px and gave way at
   1600 and on every 1920 monitor; in this one it stands from 1280 to 1920
   (measured 27 Sep 2026, English).
   It stands beside Membership only where the rail has room for it, and that
   is MEASURED (app.js fitRail sets .no-room), because the room depends on
   the language the rooms are named in — in German and Spanish it never fits
   beside the wordmark: the same door waits on the Account page at every
   width. On the phone rail (900px and under) it never stands. It steps
   aside with Membership and Sign up on a first reading, where the arrival
   plate carries its own «How reading works». */
#nav-howto {
  display: inline-flex;
  align-items: center;
  min-height: 46px;
  padding: 0 8px;
  border-bottom: 4px solid transparent;
  border-radius: 8px;
  font: 400 .75rem var(--sans);
  letter-spacing: .04em;
  color: var(--on-play, var(--on-pine));
  text-decoration: none;
  white-space: nowrap;
}
#nav-howto:hover { color: var(--reward, var(--signal)); }
#nav-howto:focus-visible { outline: 2px solid var(--reward, var(--signal)); outline-offset: 2px; }
@media (max-width: 900px) { #nav-howto { display: none; } }
#nav-howto.no-room { display: none; }
body:has(#v-read.show.first-reading) #nav-howto { display: none; }
/* AND WHEN EVEN THAT IS NOT ENOUGH, the wordmark stands down, as it does on
   a phone — fitRail sets .rail-tight only after the rooms would still run
   under the ticket without the link (English at 1024 and 1100px, measured
   26 Sep 2026), and then offers the link the room the wordmark left. Named
   to outrank head.html's inline-flex .wordmark.small. */
header .bar.rail-tight .brand.wordmark,
header .bar.rail-tight .brand + .bar-rule { display: none; }

/* ============================================================================
   THE TICKET MENU — № opens a printed card, not a mystery jump.
   Profile, settings, memory, colophon, and the door in or out.
   ============================================================================ */
.nav-ident { position: relative; }
#ident-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 70;
  min-width: 230px;
  background: var(--sheet);
  color: var(--carbon);
  border: 2px solid var(--carbon);
  box-shadow: 5px 5px 0 var(--carbon);
  padding: 6px 0;
}
#ident-menu .im-who {
  font-family: var(--mono);
  font-size: .7rem;
  color: var(--steel);
  padding: 9px 16px 8px;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 4px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 260px;
}
#ident-menu hr {
  border: 0;
  border-top: 1px solid var(--rule);
  margin: 4px 0;
}
/* the light, on the ticket: same dots as Settings, one press closer.
   A row, not a menuitem — pressing a dot changes the lamp and stays. */
#ident-menu .im-themes {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 16px;
  border-top: 1px solid var(--rule);
  margin-top: 4px;
}
#ident-menu .im-thlab {
  font-family: var(--bill);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: .76rem;
  color: var(--carbon);
}
/* the id-selector menu rule strips button backgrounds and borders — the
   dots restate theirs at higher specificity or they render as three
   invisible circles */
/* the container re-asserts itself: 70-shell hides `.bar .theme-dots` on
   phones, and this menu lives inside the bar — higher specificity keeps the
   menu's dots standing at every width */
/* what it must NOT restate is the die. This block once carried
   `border-radius: 50%`, the one shape 70-shell names out loud and forbids —
   «three straight-cut swatches of the three stocks, not three traffic dots».
   Being ID-scoped it beat that law, and below 480px, where the bar's own dots
   are hidden, these were the ONLY theme switcher on the screen. Staying silent
   hands the die back to `.theme-dots button`: square in the base house, 2px
   under the arcade, one swatch everywhere. */
#ident-menu .theme-dots { display: inline-flex; }
#ident-menu .theme-dots button {
  display: inline-block;
  width: 21px;
  height: 21px;
  min-height: 0;
  border: 1.5px solid var(--rule);
}
/* the same three rooms these dots actually open (see head.html) */
/* sepia is #e8dabb, not the retired #f4dca3: the lamp room was re-ground on
   2026-08-25 down to chroma 15 because the old ground sat ΔE 37 from
   --signal — the sacred gold as wallpaper. A swatch shows the room it opens,
   so when the ground moves this line and head.html's copy move with it. */
#ident-menu .theme-dots .t-paper { background: #f7f9ff; }
#ident-menu .theme-dots .t-sepia { background: #e8dabb; }
#ident-menu .theme-dots .t-dark { background: #121d38; }
#ident-menu .theme-dots button.on { border-color: var(--signal); box-shadow: 0 0 0 2px color-mix(in srgb, var(--signal) 30%, transparent); }
#ident-menu button {
  display: block;
  width: 100%;
  min-height: 42px;
  text-align: left;
  background: none;
  border: 0;
  border-bottom: none;
  box-shadow: none;
  cursor: pointer;
  font-family: var(--bill);
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  font-size: .76rem;
  color: var(--carbon);
  padding: 10px 16px;
}
#ident-menu button::after { display: none; }
#ident-menu button:hover {
  background: var(--carbon);
  color: var(--sheet);
}

@media (max-width: 900px) {
  .brand + .bar-rule { display: none; }
}

/* THE RAIL ON A PHONE. At 390px the five rooms, the imprint, SIGN UP and the
   sort did not fit, so the row simply ran off the edge: PRACTICE was cut
   through the middle of the word and WORDS did not exist at all. A reader
   cannot choose a room they cannot see, and the two that vanished are the
   deck and the sitting — the whole record of what they own.
   The wordmark stands down first, because a stranger who is inside the
   product does not need to be told its name a second time; the rooms keep
   the width, and the rail scrolls only if it still has to. */
/* 320px: five rooms, the imprint and the ticket do not fit however tight the
   tracking goes, and WORDS was rendering as «WO». The rail keeps the rooms
   and the sort steps out — it is a number, and its own screen is one tap
   away on the desk. */
@media (max-width: 380px) {
  .bar .nav-ident #nav-reader { display: none; }
  /* the plates' 6px gap (9 Sep) cost 12px here and pushed «Membership» 6px
     into the wordmark's «LITERATURE» on a 320px phone; 2px still reads as
     two doors and gives the wordmark back its edge (measured 10 Sep) */
  .nav-ident { gap: 2px; }
  .bar .nav-tabs button { padding-inline: 3px; letter-spacing: 0; font-size: .9em; }
}
@media (max-width: 560px) {
  .brand { display: none; }
  .brand + .bar-rule { display: none; }
  .bar { max-width: 100vw; padding-inline: 6px; }
  .nav-rooms {
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }
  .nav-rooms::-webkit-scrollbar { display: none; }
  .nav-ident { flex: none; }
}


/* ===== 80-verbs.css ===== */
/* ============================================================================
   THE THREE VERBS, APPLIED TO THE WHOLE PRODUCT.

   An inventory of every animation in the file turned up eleven that fade and
   rise — and two of them were mine, written an hour after I criticised exactly
   that pattern in a commit message. Fade-and-rise is what every site does. It
   is the motion equivalent of Inter on white: not wrong, just silent about
   what this thing is.

   The biggest was `viewin`, on `.view.show > *` — EVERY element of EVERY route.
   The whole product fades and rises on every navigation, which is a lot of
   motion saying nothing at all.

   These are redefinitions rather than new rules. The keyframes keep their
   names, so every existing call site picks up the new motion without a single
   selector being touched or a single element being reclassified. A later
   @keyframes of the same name wins, and the build folds this sheet in after
   head.html's own <style>.

   THE VERBS, all three from the trade:
     SET   a sort arrives from the side and stops dead. Metal does not bounce.
     DRAW  a rule is inked left to right, linearly, because it is a measurement.
     LAY   a sheet is put down and its shadow arrives with it. The sheet does
           not move; the light does.
   ============================================================================ */

/* --- SET. The product's signature, and now its default arrival.
   From the left, decelerating into place, and STOPPING — no overshoot. The
   distance is small on purpose: a whole screen of elements each travelling
   26px is a composing stick filling; each travelling 60px is a slot machine. */
/* from-only, deliberately: this redefinition wins over head.html's original,
   and an explicit `to { opacity: 1 }` frame here fought the GSAP overture
   for #greeting/#desk-spread — the missing end frame resolves to the
   element's own computed style, which is the ownership contract */
@keyframes viewin {
  from { transform: translateX(-14px); opacity: 0; }
}
@keyframes setline {
  from { transform: translateX(-10px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes tilein {
  from { transform: translateX(-16px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes rowin {
  from { transform: translateX(-8px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes attrIn {
  from { transform: translateX(-8px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* --- LAY. For anything that is a sheet of stock rather than a piece of type:
   the word popover is a proof slip put down on the page, and a proof slip does
   not fly in. It appears where it is and the light arrives after it. */
@keyframes popin {
  from { opacity: 0; box-shadow: 0 0 0 var(--carbon); }
  60%  { opacity: 1; }
  to   { opacity: 1; box-shadow: var(--lift); }
}
@keyframes guidein {
  from { opacity: 0; box-shadow: 0 0 0 var(--carbon); }
  to   { opacity: 1; box-shadow: var(--lift); }
}

/* --- The two I shipped an hour ago, corrected to the vocabulary I wrote them
   under. A word in a unit is a sort being set, so it comes from the side like
   every other sort; a letter in the alphabet is a sort in its box. */
@keyframes cs-word {
  from { transform: translateX(-9px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}
@keyframes cs-letter-in {
  from { transform: translateX(-12px); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* --- The companion is the one thing that may still rise, and only because it
   is a figure rather than a piece of stock. Everything else in the room is
   flat, machined and set; the person is not, and the difference should be
   visible in how they arrive. */

/* Reduced motion: every one of these ends on the finished state, and the app's
   own --motion scalar already collapses their duration. Nothing above hides
   anything by default that is not restored by its own last keyframe. */


/* ===== 90-chromatic-game.css ===== */
/* ============================================================================
   90-chromatic-game.css — THE BRIGHT BOOK ARCADE
   Loaded late, NOT last (the cascade contract, build.py CSS_ORDER): after it
   come 95-coherence (ties), 96-scale (type), 97-lessons, 98-place — those
   four win against this sheet by design. The app keeps its books, proof
   sheets and composing furniture, but turns up the chroma, makes progress
   visible, and gives every pressable surface the same physical response.
   Russian prose remains quiet.
   ========================================================================== */

:root {
  --play: #244177;
  --play-deep: #152747;
  --reward: #f6c445;
  --reward-deep: #8a5a00;
  --right: #1ea868;
  --right-deep: #127748;
  --wrong: #e9543f;
  --wrong-deep: #c83d31;
  --paper: #fffdf5;
  --play-tint: #eef3fc;
  --reward-tint: #fff1be;
  --right-tint: #daf5e8;
  --wrong-tint: #ffe2dc;
  --edge: var(--carbon);
  --edge-hi: var(--carbon);
  /* the PRESS shadow: the hard 0-Npx-0 drop under every pressable block.
     By day it is carbon — ink. At night carbon lifts to warm off-white
     (it is the TEXT colour), and an ink shadow must not lift with it:
     a glowing lip under every button is chrome, and chrome may not lift.
     So the press gets its own token and drops to case after dark. */
  --press: var(--carbon);
  --bar: var(--play);
  --on-bar: var(--paper);
  --on-play: #fffdf5;
  --on-play-deep: #fffdf5;
  --on-reward: #151c2e;
  --on-right: #151c2e;
  --mark: var(--play);
  --lift-sm: 0 4px 0 var(--play-deep);
  --lift: 0 6px 0 var(--play-deep);
  --lift-lg: 0 9px 0 color-mix(in srgb, var(--play-deep) 82%, var(--carbon));
}

/* THE LAMP ROOM'S ARCHITECTURE SITS DOWN WITH ITS PIGMENTS (2026-08-25, from
   the mode audit). The sepia theme's own note says that under the lamp the
   pigments sit down toward the stone they were ground from, and it did that
   for malachite, lazur and pine — and left `--play`, which is the one band
   that runs across the top of every screen, at full daylight strength. A hard
   cold navy laid straight on a warm room: the loudest junction in the whole
   set, and the only place in the product where two lights meet edge to edge.

   It has to be stated HERE rather than beside the rest of the sepia tokens in
   head.html, because this sheet owns `--play` and loads after that file — an
   override there has equal specificity and loses on order. That is exactly how
   the first attempt at this failed. */
[data-theme="sepia"] {
  --play: #2b3f63;
  --play-deep: #1a2740;
}

[data-theme="dark"] {
  --play: #486cb4;
  --play-deep: #20355e;
  --reward: #f6c445;
  --reward-deep: #ffe28a;
  --right: #2ec581;
  --right-deep: #127748;
  --wrong: #ff7d70;
  --wrong-deep: #c83d31;
  --paper: #1c273e;
  --play-tint: #16223a;
  --reward-tint: #4b4123;
  --right-tint: #173d32;
  --wrong-tint: #4b2931;
  --bar: var(--play);
  --on-bar: #10182d;
  --on-play: #fffdf5;
  --on-play-deep: #fffdf5;
  --on-reward: #151c2e;
  --on-right: #151c2e;
  --mark: var(--reward);
  --press: var(--case);
  --lift-sm: 0 4px 0 #0b1328;
  --lift: 0 6px 0 #0b1328;
  --lift-lg: 0 9px 0 #080e1e;
}

body {
  background: var(--room);
  background-image:
    linear-gradient(90deg, color-mix(in srgb, var(--play) 4%, transparent) 1px, transparent 1px),
    linear-gradient(color-mix(in srgb, var(--play) 4%, transparent) 1px, transparent 1px);
  background-size: 32px 32px;
}
body::before { display: none; }

h1, h2, h3, h4 {
  font-family: var(--bill);
  font-weight: 800;
  letter-spacing: .025em;
}

/* --- the blue game rail ------------------------------------------------ */
body > header {
  background: var(--play);
  color: var(--on-play);
  border-bottom: 3px solid var(--press);
  box-shadow: 0 4px 0 var(--play-deep);
}
/* At night the rail keeps to the deep ink: the lifted #486cb4 that reads as
   an accent on dark surfaces is far too loud as a 62px band across the top
   of a dark room. Accents may lift at night; architecture may not. */
[data-theme="dark"] body > header {
  background: var(--play-deep);
  box-shadow: 0 4px 0 #0b1328;
}

/* engravings inside pressed things: optical alignment, one line height */
button .ic, .btn .ic, .icon-btn .ic { width: 1.06em; height: 1.06em; vertical-align: -.16em; }
.gx-say .ic { width: 1em; height: 1em; }

.room-head {
  background: transparent !important;
  box-shadow: none !important;
  border: none !important;
  border-bottom: 2.5px solid var(--carbon) !important;
  margin: 12px 0 24px !important;
  padding: 8px 0 16px !important;
}
.room-head .rh-ru {
  font-family: var(--bill) !important;
  font-size: clamp(2rem, 4.5vw, 3rem) !important;
  font-weight: 900 !important;
  letter-spacing: .08em !important;
  text-transform: uppercase !important;
  color: var(--carbon) !important;
  line-height: 1.1 !important;
}
.room-head h1, .room-head h2 {
  font-family: var(--serif) !important;
  font-style: italic !important;
  font-size: 1.25rem !important;
  color: var(--steel) !important;
  margin: 4px 0 6px !important;
}
.room-head .rh-sub, .room-head #journey-sub, .room-head #words-census, .room-head #grammar-census {
  font-family: var(--sans) !important;
  font-size: .88rem !important;
  color: var(--steel) !important;
  margin: 0 !important;
}
.bar { min-height: 62px; max-width: 1280px; padding-block: 7px; }
/* THE HINGE SURVIVES THE RAIL. `through` is the gold italic that holds the
   lockup together — the one non-negotiable of the master wordmark — and
   listing .wm-t here painted it the same cream as LANGUAGE and LITERATURE,
   killing the hinge on every page of the app (the desk masthead, the
   stationery and the header base all keep it; only this rule broke it). */
.brand, .brand .wm-l, .nav-tabs button { color: var(--on-play); }
.brand .wm-t { color: var(--reward); }
.brand b { color: var(--on-play); }
.nav-tabs button {
  min-height: 46px;
  padding-inline: 14px;
  border: 0;
  border-bottom: 4px solid transparent;
}
.nav-tabs button:hover { color: var(--reward); }
.nav-tabs button.active,
.nav-tabs button[data-nav].active {
  color: var(--on-reward);
  background: var(--reward);
  border-bottom-color: var(--carbon);
  box-shadow: 0 3px 0 var(--press);
}
.nav-tabs button::after { display: none; }
.streak-chip {
  background: var(--reward);
  color: var(--on-reward);
  border: 2px solid var(--carbon);
  box-shadow: 0 3px 0 var(--press);
  padding: 6px 10px;
}
.theme-dots button,
#settings-panel .theme-dots button {
  border-radius: 2px;
  border: 2px solid var(--paper);
  width: 22px; height: 22px;
}
.theme-dots button.on {
  box-shadow: 0 0 0 2px var(--play), 0 0 0 4px var(--reward);
}
.px-tal b, .px-verb { color: var(--play); }
input.px-name:focus { box-shadow: inset 0 -4px 0 var(--play); color: var(--carbon); }
.px-if { border-left-color: var(--play); }
.px-guide-plinth { background: var(--reward); }
.px-case button.active { background: var(--play); color: var(--on-play); }

/* --- tactile printing blocks ------------------------------------------ */
.btn, .icon-btn, .filters button, .conf-row button, .rev-btns button,
.quiz-opt, .ltile, .keys button, #pop .row button, #pop .row a,
.ask-card .opts button, .guide-again, .plan-opt, .read-on, .exlibris,
.gx-opt, .gx-say, .gx-tile, .gx-set, .cs-lesson {
  border: 2px solid var(--carbon);
  border-radius: 2px;
  background: var(--galley);
  color: var(--carbon);
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 58%, var(--rule));
  transition: transform 80ms linear, box-shadow 80ms linear,
              background-color 120ms linear, color 120ms linear;
}
.btn:hover, .icon-btn:hover, .filters button:hover, .conf-row button:hover,
.rev-btns button:hover, .quiz-opt:hover, .ltile:hover, .keys button:hover,
#pop .row button:hover, .ask-card .opts button:hover, .guide-again:hover,
.gx-opt:hover:not(:disabled), .gx-say:hover, .gx-tile:hover, .cs-lesson:hover {
  background: var(--reward-tint);
  border-color: var(--carbon);
  box-shadow: 0 5px 0 var(--press);
  transform: translateY(-1px);
}
.btn:active, .icon-btn:active, .filters button:active, .rev-btns button:active,
.quiz-opt:active, .ltile:active, .keys button:active, #pop .row button:active,
.gx-opt:active, .gx-say:active, .gx-tile:active, .cs-lesson:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--press);
}
.btn.primary, .rev-btns .primary, #pat-next, .plan.best,
.buy.primary {
  background: var(--play);
  border: 2.5px solid var(--carbon);
  color: var(--on-play);
  box-shadow: 0 5px 0 var(--play-deep);
  min-height: 48px;
  font-weight: 800;
}
.btn.primary:hover, .rev-btns .primary:hover {
  background: color-mix(in srgb, var(--play) 86%, #ffffff);
  color: var(--on-play);
  box-shadow: 0 6px 0 var(--play-deep);
  transform: translateY(-1px);
}
.btn.primary:active, .rev-btns .primary:active {
  background: var(--play);
  color: var(--on-play);
  transform: translateY(4px);
  box-shadow: 0 1px 0 var(--play-deep);
}
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 3px solid var(--reward);
  outline-offset: 3px;
}

/* --- proof sheets, not soft cards ------------------------------------- */
.card, .overlay .panel, .scene-end, .ticket, .prep-banner, .find-card,
.issue, .glesson-card, .gex, .tr-reveal, .ask-card, .tonight-card,
.frontier-card {
  background: var(--galley);
  border: 2px solid var(--carbon);
  border-radius: 2px;
  box-shadow: 6px 6px 0 color-mix(in srgb, var(--play) 34%, var(--case));
}
.bill-head, .tonight-card .bill-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 18px;
}
.card > h3, .card > .bill-head, .overlay .panel > h3,
.scene-end > h3, .bill-head, .tonight-card .bill-head {
  background: var(--play);
  color: var(--on-play);
  border-bottom: 2px solid var(--carbon);
}
.overlay { backdrop-filter: none; -webkit-backdrop-filter: none; }
.overlay .panel { box-shadow: var(--lift-lg); }

/* --- home: a book arcade with one obvious next move -------------------- */
#v-home > .masthead {
  /* the arena and the ledger fuse into one slab: no gap, shared border —
     the desk's first shared edge (the old floating-cards complaint) */
  margin: 28px auto 0;
  max-width: var(--desk-w, min(94%, 1040px));
  text-align: left;
  background: var(--play);
  color: var(--on-play);
  border: 3px solid var(--carbon);
  box-shadow: 8px 8px 0 var(--reward);
  padding: clamp(24px, 3.4vw, 40px);
  position: relative;
  overflow: hidden;
}
#v-home > .masthead::after {
  /* the folio is the reader's own issue number (renderHome stamps it);
     a hardcoded date was a fact about nobody. left/border reset the base
     masthead ::after (the title-page double rule), which otherwise cascades
     through and prints the number as a wide bordered strip */
  content: attr(data-folio);
  position: absolute;
  left: auto; border: 0;
  right: 18px; bottom: 8px;
  width: auto; height: auto;
  display: grid; place-items: center;
  color: var(--on-play);
  font-family: var(--mono); font-size: var(--t-00); font-weight: 700;
  letter-spacing: .16em; opacity: .55;
}

/* THE GHOST LINE — the next lesson's unfinished sentence, laid across the
   arena's foot under the one button: the itch made visible. Its gaps are
   low-ink, not hidden; the caption names exactly how few words remain. */
.arena-ghost {
  margin-top: clamp(14px, 2vw, 22px);
  padding: 14px 0 12px;   /* rhythm only — the folio owns its own line (95 §13) */
  border-top: 1px solid color-mix(in srgb, var(--on-play) 28%, transparent);
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: 6px 18px;
}
.arena-ghost .ag-q {
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1.05rem, 1.6vw, 1.3rem); line-height: 1.45;
  color: var(--on-play);
}
.arena-ghost .ag-q .gx-ghost { opacity: .42; letter-spacing: .04em; }
.arena-ghost .ag-shy {
  font-family: var(--sans); font-style: normal;
  font-size: var(--t-00); font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-play); opacity: .72;
  white-space: nowrap;
}
@media (max-width: 580px) {
  .arena-ghost .ag-q { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .arena-ghost .ag-shy { white-space: normal; min-width: 0; }
}

.desk-intro {
  display: grid;
  grid-template-columns: minmax(0, .85fr) minmax(380px, 1.15fr);
  gap: clamp(24px, 3.6vw, 48px);
  align-items: center;
  min-height: 280px;
}
.desk-lockup { align-self: center; }
#v-home .masthead .wordmark {
  display: flex; align-items: baseline; gap: 7px;
  text-align: left;
}
#v-home .masthead .wm-l {
  color: var(--on-play);
  font-family: var(--bill);
  font-size: clamp(18px, 2.1vw, 26px);
  font-weight: 800;
  line-height: 1;
  letter-spacing: .08em;
}
#v-home .masthead .wm-t {
  color: var(--reward);
  font-size: clamp(16px, 1.8vw, 22px);
}
#v-home .masthead h1,
#v-home #room-name {
  font-family: var(--bill);
  font-weight: 900;
  font-size: clamp(52px, 6.8vw, 86px);
  line-height: .84;
  letter-spacing: .015em;
  text-indent: 0;
  color: var(--on-play);
  max-width: 7ch;
  margin: 18px 0 12px;
}
#v-home .masthead .room-line {
  color: var(--on-play) !important;
  letter-spacing: .2em;
  opacity: 1;
  font-size: .75rem;
  text-wrap: balance;   /* never a widowed «1842» on its own line */
}
#v-home .masthead .almanac {
  color: var(--on-play);
  margin: 12px 0 0;
  max-width: 48ch;
  text-align: left;
  font-size: .82rem;
  line-height: 1.45;
  opacity: .88;
}
#v-home .masthead .almanac :is(.al-orn, .al-more, .al-echo) {
  color: inherit;
}

.desk-host {
  position: relative;
  min-height: 270px;
  display: grid;
  grid-template-columns: minmax(230px, 1fr) 160px;
  align-items: center;
  gap: 16px;
  isolation: isolate;
}
.desk-host-bubble {
  position: relative;
  align-self: center;
  background: var(--sheet);
  color: var(--carbon);
  border: 3px solid var(--carbon);
  box-shadow: 6px 6px 0 var(--press);
  padding: 18px 20px;
  display: grid;
  gap: 8px;
  z-index: 2;
  border-radius: 2px;
}
.desk-host-bubble::after {
  content: '';
  position: absolute;
  width: 18px; height: 18px;
  right: -11px; top: calc(50% - 9px);
  background: var(--sheet);
  border-right: 3px solid var(--carbon);
  border-bottom: 3px solid var(--carbon);
  transform: rotate(-45deg);
}
.desk-host-kicker {
  font-family: var(--sans);
  font-size: var(--t-00); font-weight: 800;
  letter-spacing: .14em;
  color: var(--play-deep);
  background: var(--reward-tint);
  border: 1.5px solid var(--carbon);
  padding: 3px 8px;
  width: fit-content;
  border-radius: 2px;
}
/* Both halves of this chip are theme-swapped, and at night they swapped
   TOWARD each other: --play-deep sank to #20355e and --reward-tint rose to
   #4b4123, which is deep ink on deep ink — measured 1.20:1, on the first
   card of the first screen. The chip keeps its shape; the label takes the
   reward's own night ink, which is the token that lifts when the tint does. */
[data-theme="dark"] .desk-host-kicker { color: var(--reward-deep); }
/* WHAT THE MARKUP SHIPS IS NOTHING. renderDeskHost writes this kicker inside
   four if/else arms with no final else, so a shipped placeholder is not just
   a paint-window flash — it can stand. Empty, it must not print as a bare
   chip with a border and no word in it. */
.desk-host-kicker:empty { display: none; }
#desk-host-line {
  font-family: var(--bill);
  font-size: clamp(24px, 2.8vw, 32px);
  font-weight: 900;
  line-height: .98;
  letter-spacing: .015em;
  color: var(--carbon);
  margin: 2px 0;
}
.desk-host-meta {
  font-family: var(--sans);
  font-size: .76rem;
  line-height: 1.35;
  color: var(--steel);
}
.desk-host-track {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 2px 0;
}
.desk-host-progress {
  flex: 1;
  display: block;
  height: 12px;
  border: 2px solid var(--carbon);
  background: var(--play-tint);
  border-radius: 2px;
  overflow: hidden;
}
.desk-host-progress i {
  display: block; height: 100%;
  width: calc(var(--v, 0) * 100%);
  min-width: 8px;
  background: var(--right);
  border-right: 2px solid var(--carbon);
  transition: width .25s ease;
}
.desk-host-count {
  font-family: var(--mono);
  font-size: .72rem;
  font-weight: 700;
  color: var(--steel);
  white-space: nowrap;
}
.desk-host-btn {
  width: 100%;
  margin-top: 4px;
  font-family: var(--bill);
  font-size: 1.15rem;
  font-weight: 900;
  letter-spacing: .04em;
  min-height: 48px;
}

.desk-cast-wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  position: relative;
  width: 160px;
  height: 270px;
  justify-self: end;
}
/* THE COMPANY READ «MPANY» AT 1440 IN GERMAN, and it was never a stacking
   fault — the label carries aria-hidden, so nothing was ever unreadable to a
   screen reader, only to an eye. The row is absolutely positioned in a 160px
   well and its contents measure ~218px, so it hung out to the LEFT; the
   German headline wraps to two lines, the bubble grows from 225 to 256px, and
   being vertically centred its top rises across the overhang. Give the label
   a row of its own and the row fits its well at any headline length. */
.desk-cameos {
  position: absolute;
  top: 6px; right: 0;
  display: flex; gap: 6px;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  z-index: 3;
}
.desk-cameos .cameo-lab { flex: 1 0 100%; text-align: right; margin-bottom: 2px; }
/* the row names itself — a compositor's smallcap, not a mystery */
.cameo-lab {
  font-family: var(--bill);
  font-weight: 700;
  font-size: var(--t-00);
  letter-spacing: .14em;
  text-transform: uppercase;
  /* .55 PUT THIS LABEL UNDER THE FLOOR IN EVERY ROOM, and the dim was doing
     the hierarchy work that the size, the weight and the .14em tracking are
     already doing three times over. Measured as rendered — opacity counts,
     because the eye sees the composite and so does WCAG: 4.17:1 in daylight,
     4.39 under the lamp, 2.69 after dark. At .7 it is 5.75 / 6.00 / 6.70 and
     still plainly the quietest line in the cameo. */
  opacity: .7;
  margin-right: 2px;
  white-space: nowrap;
}
.cameo-btn {
  width: 32px; height: 32px;
  display: grid; place-items: center;
  background: var(--sheet);
  color: var(--carbon);
  border: 2px solid var(--carbon);
  box-shadow: 0 2px 0 var(--press);
  border-radius: 2px;
  cursor: pointer;
  transition: transform .1s ease, box-shadow .1s ease, background-color .15s ease;
  padding: 0;
}
.cameo-btn:hover {
  background: var(--reward-tint);
  transform: translateY(-2px);
  box-shadow: 0 4px 0 var(--press);
}
.cameo-btn.active {
  background: var(--reward);
  box-shadow: 0 2px 0 var(--press), inset 0 0 0 1.5px var(--play);
}
.cameo-btn svg { width: 24px; height: 24px; }

.desk-cast {
  width: 154px; height: 184px;
  z-index: 2;
  cursor: pointer;
  color: var(--carbon);
  overflow: visible;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .15s ease;
  margin-top: 26px;
}
.desk-cast:hover {
  transform: translateY(-3px) scale(1.04);
}
.desk-cast svg { width: 100%; height: 100%; display: block; overflow: visible; }
/* the pedestal meets the figure's feet — the resident STANDS on it; the
   100-unit body ends around y·84, so the plinth rises to that line */
.desk-cast-pedestal {
  width: 140px; height: 22px;
  background: var(--reward);
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  border-radius: 2px;
  margin-top: -38px;
  z-index: 1;
}

/* --- THE DAY LEDGER -----------------------------------------------------
   Not four floating chips: ONE ruled slab, the day's standing entered in
   columns the way a counting-house rules a page. Thick-thin double head
   rule, vertical column rules, engravings from ui/icons.js, numerals in
   the counting face. A ledger is a fact, not a button: no hover lift. */
.desk-hud {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0;
  /* fused to the arena above: the -3px swallows the masthead's border so
     the two read as one composed slab, and the ledger takes the same hard
     offset shadow so the silhouette continues */
  margin: -3px auto 26px;
  max-width: var(--desk-w, min(94%, 1040px));
  background: var(--sheet);
  border: 3px solid var(--carbon);
  box-shadow: 8px 8px 0 var(--reward);
  position: relative;
}
.hud-pill {
  background: none;
  border: none;
  border-left: 1.5px solid color-mix(in srgb, var(--carbon) 22%, transparent);
  box-shadow: none;
  border-radius: 0;
  padding: 16px 20px 14px;
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
  position: relative;
  transition: background calc(.2s * var(--motion, 1)) var(--e-std, ease);
}
.hud-pill:hover {
  background: color-mix(in srgb, var(--carbon) 3%, transparent);
}
.hud-pill:first-child { border-left: none; }
.hud-icon {
  width: 44px; height: 44px; flex: none;
  display: grid; place-items: center;
  color: var(--carbon);
  background: var(--galley);
  border: 1.5px solid var(--carbon);
  box-shadow: 2px 2px 0 var(--press);
  position: relative;
}
.hud-icon .ic { width: 26px; height: 26px; }
.hud-body {
  display: flex;
  flex-direction: column;
  gap: 3px;
  line-height: 1.05;
  min-width: 0;
}
.hud-body strong {
  font-family: var(--bill);
  font-size: 1.38rem;
  font-weight: 800;
  color: var(--carbon);
  letter-spacing: .02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  text-transform: uppercase;
}
.hud-body strong .hud-n {
  font-family: var(--bill);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: .01em;
}
.hud-body small {
  font-family: var(--sans);
  font-size: var(--t-00);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .12em;
  line-height: 1.3;
  color: var(--steel);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* the lamp is LIT, not printed: its flame leans as if in a draught. The
   streak entry is the only living cell on the ledger, which is what makes
   it read as the one that is burning now. */
#hud-streak .ic .ic-flame { transform-origin: 12px 9.5px; }
body:not(.motion-low) #hud-streak .ic .ic-flame {
  animation: hud-flame 4.6s var(--breath-in, ease-in-out) infinite;
}
@keyframes hud-flame {
  0%, 100% { transform: rotate(0deg) scale(1); }
  30%      { transform: rotate(-2.6deg) scale(1.04, .97); }
  58%      { transform: rotate(1.9deg) scale(.985, 1.045); }
  78%      { transform: rotate(-.8deg) scale(1); }
}

/* --- Dialog Strip & Player Table of Ranks ------------------------------ */
.friend-strip {
  display: flex;
  align-items: center;
  gap: 14px;
  max-width: var(--desk-w, min(94%, 1040px));
  margin: 0 auto 16px;
}
/* 70-shell pins this width at id specificity — matched here or the strip
   stays at the old 1040 measure while every other band widened */
#v-home > .friend-strip { max-width: var(--desk-w, min(94%, 1040px)); }
.friend-strip[style*="display: none"],
.friend-strip[style*="display:none"] {
  display: none !important;
}
.friend-strip .fr-who {
  width: 48px; height: 48px;
  flex: 0 0 48px;
  display: grid; place-items: center;
}
.friend-strip .fr-who svg {
  width: 44px; height: 44px;
}
.friend-strip .fr-bubble {
  flex: 1;
  background: var(--sheet);
  border: 2.5px solid var(--carbon);
  box-shadow: 4px 4px 0 var(--play);
  padding: 12px 18px;
  border-radius: 2px;
  font-family: var(--serif);
  font-size: 1.05rem;
  color: var(--carbon);
  position: relative;
}
.friend-strip .fr-bubble::before {
  content: '';
  position: absolute;
  left: -8px; top: calc(50% - 6px);
  width: 12px; height: 12px;
  background: var(--sheet);
  border-left: 2.5px solid var(--carbon);
  border-bottom: 2.5px solid var(--carbon);
  transform: rotate(45deg);
}

.greeting {
  text-align: center;
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.18rem;
  color: var(--carbon);
  margin: 12px auto 20px;
  letter-spacing: .02em;
}
.greeting em, .greeting b {
  font-style: normal;
  font-weight: 700;
  color: var(--play);
}

#standing-line {
  max-width: var(--desk-w, min(94%, 1040px));
  margin: 0 auto 24px;
}
#standing-line[style*="display: none"],
#standing-line[style*="display:none"] {
  display: none !important;
}
.stl {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 18px;
  background: var(--sheet);
  border: 2.5px solid var(--carbon);
  box-shadow: 4px 4px 0 var(--press);
  border-radius: 2px;
}
.stl-cls {
  flex: 0 0 auto;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  font-family: var(--bill);
  font-weight: 900;
  font-size: 1.45rem;
  background: var(--reward);
  color: var(--on-signal);
  border: 2px solid var(--carbon);
  box-shadow: 0 2px 0 var(--press);
  border-radius: 2px;
}
/* The gild rule in 66-rooms paints the numeral gold at standing — and gold
   ON the reward square is the same hue on itself: the class vanished into
   its own plate. On this layer the square is already gold, so the numeral
   takes a dark ink at every standing. It asked for --carbon, which reads as
   dark ink only until the room does; at night --carbon is cream and the
   class went back to 1.53:1 on its own plate. --on-signal is the ink this
   fill wants under every light, which is the whole reason it exists. */
body[data-standing="3"] .stl-cls, body[data-standing="4"] .stl-cls {
  color: var(--on-signal);
  border-color: var(--carbon);
}
.stl-mid {
  flex: 1;
  min-width: 0;
  display: grid;
  gap: 5px;
}
.stl-rank {
  font-family: var(--sans);
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .02em;
  color: var(--carbon);
}
.stl-rank b {
  font-family: var(--bill);
  font-size: 1.15rem;
  font-weight: 800;
}
/* The rise is a rule filling with INK — not a fuel gauge. The green fill
   this replaces was the right-answer colour doing a second job, which is
   exactly the drift the palette law exists to stop: paper track, carbon
   ink, and nothing on the desk that has to be learned. */
.stl-bar {
  display: block;
  height: 8px;
  border: 1.5px solid var(--carbon);
  background: var(--sheet);
  border-radius: 2px;
  overflow: hidden;
  position: relative;
}
.stl-bar i {
  position: absolute;
  inset: 0 auto 0 0;
  width: calc(var(--p, 0) * 1%);
  background: var(--carbon);
  transition: width 900ms var(--e-std, ease);
}
.stl-next {
  font-family: var(--serif);
  font-size: .75rem;
  font-style: italic;
  color: var(--steel);
}
.stl-run {
  flex: 0 0 auto;
  text-align: right;
  display: grid;
  gap: 3px;
  justify-items: end;
  font-family: var(--sans);
  font-size: .78rem;
  color: var(--steel);
}
.stl-run-top {
  white-space: nowrap;
  font-family: var(--bill);
  font-size: 1.15rem;
  font-weight: 800;
  color: var(--carbon);
}
.stl-run b {
  font-family: var(--bill);
  font-size: 1.25rem;
  font-weight: 900;
  color: var(--carbon);
}
/* the flame and the sorts speak in the reward ink that CARRIES TEXT —
   the raw hex this replaces was off-palette, and pale gold on paper was
   below reading contrast */
.stl-flame {
  font-style: normal;
  color: var(--signal-ink);
  margin-right: 4px;
}
/* the crossed bridge and the turned notebook: plainer ink, after the fact —
   never a warning, never a loss */
.stl-bridge { font-style: italic; opacity: .72; font-size: .78rem; }
.stl-note { font-style: italic; opacity: .85; }
/* the last inch: when one evening could close the span, the fill and the
   count take the room's signal ink — the one sanctioned use of red heat */
.stl-bar i.stl-hot { background: var(--signal-ink, #a5342a); }
.stl-inch { color: var(--signal-ink, #a5342a); }
.stl-key { font-size: .8rem; opacity: .8; font-style: italic; }
/* 66-rooms' mobile stack, restated: this sheet loads last, and its unscoped
   .stl-mid { min-width: 0 } was silently beating the earlier sheet's ≤620px
   wrap — the standing line rendered as a crushed 175px column on phones. */
@media (max-width: 620px) {
  .stl { flex-wrap: wrap; }
  .stl-mid { min-width: 100%; order: 3; }
}

/* --- Act I: The Spread ------------------------------------------------- */
.desk-spread { gap: 22px; }
.spread-hero {
  background: var(--sheet);
  border: 3px solid var(--carbon);
  box-shadow: 8px 8px 0 var(--play);
}
.tonight-card {
  background: var(--sheet);
  border: 3px solid var(--carbon);
  box-shadow: 8px 8px 0 var(--reward);
}
.vol-cover { background: var(--reward-tint); border-width: 2px; }
/* THE SPINE'S INK HAD TO COME WITH ITS PLATE. 65-home pairs this spine
   sensibly — `background: var(--carbon); color: var(--galley)`, light ink on
   the darkest thing in the room. This line repaints the plate --play and says
   nothing about the ink, which is free in daylight (--galley is near-white
   there, on #244177) and wrong after dark, where --galley IS the dark surface
   and --play lifts to #486cb4: «Rothkäppchen» down the hero volume's spine in
   the German room measured 2.75:1. A plate that moves takes its ink with it. */
.vol-spine, :is(#shelf, #locked-panel) .b-spine {
  background: var(--play);
  color: var(--on-play);
}
.vol-back, :is(#shelf, #locked-panel) .b-back { background: var(--play-deep); }
.vol-mark { background: var(--reward); border-inline: 1px solid var(--carbon); }
.sect-head span {
  background: var(--play);
  color: var(--on-play);
  border: 2px solid var(--carbon);
  box-shadow: 0 4px 0 var(--play-deep);
  padding: 8px 16px;
}
.sect-head::after { height: 4px; background: var(--reward); border-block: 1px solid var(--carbon); }
#bench-sect .shelf { border-bottom: 8px solid var(--play-deep); }

/* --- Act II: Milestone Road -------------------------------------------- */
.road-milestone-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 12px;
  margin: 16px 0 6px;
}
.rm-node {
  background: var(--sheet);
  border: 2px solid var(--carbon);
  box-shadow: 4px 4px 0 var(--press);
  padding: 12px 14px;
  display: grid;
  grid-template-columns: 34px 1fr auto;
  gap: 10px;
  align-items: center;
  border-radius: 2px;
  transition: transform .12s ease;
}
.rm-node:hover {
  transform: translateY(-2px);
}
.rm-node.active {
  border-left: 6px solid var(--play);
  box-shadow: 5px 5px 0 var(--reward);
}
.rm-node.locked {
  opacity: .68;
  background: var(--galley);
}
/* a chapter already walked: quiet, struck, wearing its book's own ink */
.rm-node.done {
  background: var(--galley);
}
.rm-node.done .rm-badge {
  background: var(--galley);
  color: color-mix(in srgb, var(--ink, var(--carbon)) 80%, var(--carbon));
}
.rm-node.done .rm-info strong { text-decoration: line-through; text-decoration-thickness: 1.5px; }
/* the plate's foot: a fact and a written verb, never a second button */
.road-plate .rp-act {
  margin-top: 14px;
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 12px; flex-wrap: wrap;
}
.road-plate .rp-act .faint { font-size: var(--t-0); }
/* the ornament is printed ON the gold, and gold is the one pigment that does
   not move between rooms — --carbon does. After dark it lifted to cream and
   the badge went blank at 1.53:1, on exactly the chapters the road is asking
   the reader to look at. --on-reward is the ink this plate owns. The border
   stays carbon: that edge is against the sheet, not ink on the gold. */
.rm-badge {
  width: 32px; height: 32px;
  background: var(--reward);
  color: var(--on-reward, var(--on-signal));
  border: 2px solid var(--carbon);
  display: grid; place-items: center;
  font-size: 1.05rem; font-weight: 800;
  border-radius: 2px;
}
.rm-info { display: flex; flex-direction: column; line-height: 1.15; }
.rm-info strong {
  font-family: var(--bill);
  font-size: 1.05rem;
  font-weight: 800;
  color: var(--carbon);
}
.rm-info span {
  font-family: var(--sans);
  font-size: .7rem;
  color: var(--steel);
}
.rm-tag {
  font-family: var(--sans);
  font-size: var(--t-00);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  padding: 3px 6px;
  border: 1.5px solid var(--carbon);
  background: var(--play-tint);
  color: var(--steel);
  border-radius: 2px;
}
.btn-sm {
  min-height: 32px !important;
  padding: 4px 10px !important;
  font-size: .8rem !important;
  font-weight: 800 !important;
}

/* --- route: chunky lesson stamps on one ruled itinerary ---------------- */
.rd { gap: 16px; }
.rd::before { left: 31px; width: 6px; background: var(--reward); border-inline: 1px solid var(--carbon); }
.rd-book, .rd-alpha, .rd-cy {
  background: var(--galley);
  border: 2px solid var(--carbon);
  border-left: 10px solid var(--ink, var(--play));
  box-shadow: 6px 6px 0 color-mix(in srgb, var(--ink, var(--play)) 38%, var(--case));
}
.rd-book:hover, .rd-alpha:hover, .rd-cy:hover {
  transform: translate(-2px, -2px);
  box-shadow: 8px 8px 0 color-mix(in srgb, var(--ink, var(--play)) 48%, var(--case));
}
.rd-vol {
  width: 60px; height: 76px;
  background: var(--play-tint);
  border: 3px solid var(--carbon);
  box-shadow: inset 8px 0 0 var(--ink, var(--play)), 0 4px 0 var(--press);
}
.rd-here .rd-vol {
  background: var(--reward);
  box-shadow: inset 8px 0 0 var(--play), 0 5px 0 var(--press);
}
.rd-done .rd-vol { background: var(--right); }
.rd-ch-head {
  background: var(--play-deep);
  color: var(--on-play-deep);
  border: 2px solid var(--carbon);
  box-shadow: 0 5px 0 var(--play-deep);
}
.rd-ch-head:hover, .rd-ch.rd-open .rd-ch-head { background: var(--play); }
.rd-ch-head :is(.rd-ch-ti, .rd-ch-ru, .rd-ch-state, .rd-orn) {
  color: var(--on-play-deep);
  opacity: 1;
}
.rd-m-dot {
  background: var(--reward);
  color: var(--on-reward);
  border: 3px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  text-shadow: none;
}

/* --- exercises: visible momentum and unambiguous feedback -------------- */
.gx-stage {
  background-color: #f4f7fb !important;
  background-image:
    linear-gradient(90deg, rgba(36, 65, 119, .08) 1px, transparent 1px),
    linear-gradient(rgba(36, 65, 119, .08) 1px, transparent 1px) !important;
  background-size: 24px 24px !important;
  box-shadow: none !important;
}
.gx-stage::before, .gx-stage::after, .gx-bloom {
  display: none !important;
}
.gx-stage .gx {
  background: var(--sheet);
  border: 3px solid var(--carbon);
  box-shadow: 8px 8px 0 var(--play);
  border-radius: 2px;
  /* clip SIDEWAYS only (the full-bleed bands hang on negative margins) and
     NEVER vertically: `overflow: hidden` made the sheet a clipped grid item —
     the stage's row clamped to the scrollport, a long question froze at
     ~700px with its answer buttons amputated, and the stage had "nothing"
     to scroll. Measured, toggled, proven: overflow-y must stay visible. */
  overflow-x: clip;
  overflow-y: visible;
  padding: 0 0 24px 0;
  gap: 16px;
}
/* THE RECAP BREATHES. The zero side padding above lets the exercise's bands
   run edge to edge — but it also jammed the recap's prose against the
   border. The done sheet gets its margins back; the bands (header, printed
   line, facts, tonight's page) keep their full bleed. */
.gx-stage .gx.gx-done {
  --gxp: clamp(20px, 4vw, 40px);
  padding: 0 var(--gxp) 28px;
}
/* THE STICKY BANDS PIN FLUSH (2026-08-23). The stage wears clamp(24px,9vh,88px)
   of top padding and 40px of bottom, and a sticky band pins at the PADDING
   edge — so the printed line scrolled on visibly ABOVE the header band and
   the ghost line peeked BELOW the door row: content adrift in the margins
   of the frame, which the owner met and photographed. In the recap the
   breathing room moves onto the card itself: the stage drops its padding,
   the card carries the margin, and the bands pin at the true edge. */
.gx-stage:has(> .gx.gx-done) { padding-top: 0; padding-bottom: 0; }
.gx-stage > .gx.gx-done { margin-top: clamp(24px, 9vh, 88px); margin-bottom: 28px; }
.gx-stage .gx.gx-done > :is(.gx-top, .gx-print, .gx-facts, .gx-night) {
  margin-inline: calc(var(--gxp) * -1);
}
.gx-stage .gx.gx-done > :is(.gx-facts, .gx-night) { padding-inline: var(--gxp); }
/* the door in the recap's own band, where the lesson always kept it */
.gx-stage .gx-done .gx-top .gx-quit { margin-left: auto; }

/* Header bar */
.gx-stage .gx-top {
  background: var(--play) !important;
  color: #fffdf5 !important;
  padding: 12px 20px !important;
  border-bottom: 2.5px solid var(--press) !important;
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  gap: 12px !important;
  flex-wrap: nowrap !important;
}
.gx-stage .gx-top :is(.gx-topic, .gx-topic *, .gx-count, .gx-quit) {
  color: #fffdf5 !important;
  opacity: 1 !important;
}
.gx-stage .gx-top .gx-topic {
  font-family: var(--bill);
  font-size: 1.18rem;
  font-weight: 900;
  letter-spacing: .08em;
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
}
.gx-stage .gx-top .gx-topic i {
  color: var(--reward) !important;
  font-size: 1.25em;
  font-style: normal;
}
.gx-stage .gx-top .gx-topic em {
  font-family: var(--sans);
  font-size: .74rem;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: rgba(255, 253, 245, .85) !important;
  margin-left: 6px;
}
/* the run chip is the one piece of praise a lesson gives, and it carried its
   ink in --carbon !important: dark at noon, cream at midnight, on a gold
   plate that never moves — 1.53:1, with the !important also silencing the
   dark-room lift 26-games wrote for it. --on-reward is the gold's own ink,
   and the !important goes with it: this selector is (0,3,0) and already
   outweighs that lift on specificity alone. */
.gx-stage .gx-top .gx-runn {
  font-family: var(--sans);
  font-size: .75rem;
  font-weight: 800;
  background: var(--reward);
  color: var(--on-reward, var(--on-signal));
  border: 1.5px solid var(--carbon);
  box-shadow: 0 2px 0 var(--press);
  padding: 2px 8px;
  border-radius: 2px;
}
.gx-stage .gx-top .gx-count {
  font-family: var(--mono);
  font-size: .84rem;
  font-weight: 700;
  white-space: nowrap;
}
.gx-stage .gx-top .gx-quit {
  font-family: var(--bill);
  font-size: .95rem;
  font-weight: 900;
  letter-spacing: .1em;
  text-transform: uppercase;
  background: rgba(255, 253, 245, .15) !important;
  border: 1.5px solid rgba(255, 253, 245, .4) !important;
  color: #fffdf5 !important;
  padding: 5px 14px !important;
  border-radius: 2px;
  cursor: pointer;
  transition: all .12s ease;
}
.gx-stage .gx-top .gx-quit:hover {
  background: var(--wrong) !important;
  border-color: var(--wrong) !important;
  color: #fffdf5 !important;
}

.gx-mint-tip {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 30;
  max-width: 240px;
  white-space: normal;
  text-align: left;
  font-family: var(--sans);
  font-size: .8rem;
  font-weight: 600;
  color: var(--carbon) !important;
  background: var(--sheet) !important;
  border: 2px solid var(--carbon) !important;
  box-shadow: 0 4px 0 var(--play-deep) !important;
  padding: 8px 12px !important;
  border-radius: 2px;
}

/* Progress rail */
.gx-stage .gx-rail {
  display: flex;
  gap: 6px;
  margin: 16px 22px 6px;
}
.gx-stage .gx-rail i {
  flex: 1;
  height: 12px;
  background: var(--play-tint);
  border: 2px solid var(--carbon);
  border-radius: 2px;
  transition: all .25s ease;
}
.gx-stage .gx-rail .gx-r-done {
  background: var(--right);
  box-shadow: inset 0 -2px 0 var(--right-deep);
}
.gx-stage .gx-rail .gx-r-now {
  background: var(--reward);
  box-shadow: inset 0 -2px 0 var(--reward-deep);
}
.gx-stage .gx-chbar {
  margin: 0 22px;
  height: 4px;
  background: var(--rule);
}
.gx-stage .gx-chbar i {
  background: var(--play);
}

/* Main question area */
.gx-stage .gx-mid {
  padding: 0 22px;
  display: grid;
  gap: 14px;
}
.gx-stage .gx-ask {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--bill);
  font-size: 1.18rem;
  font-weight: 900;
  letter-spacing: .06em;
  color: var(--carbon);
  margin: 6px 0 12px;
}
/* the printer's mark rides currentColor, so whatever ink this rule names IS
   the glyph. It named --carbon, which lifts to cream after dark while the
   gold under it does not: 1.53:1, and the medallion 26-games calls «the one
   piece of furniture that changes with every question» became a blank gold
   square. --on-reward is the ink the gold plate owns, in both rooms. */
.gx-stage .gx-kind {
  display: inline-grid;
  place-items: center;
  width: 30px; height: 30px;
  background: var(--reward);
  color: var(--on-reward, var(--on-signal));
  border: 2px solid var(--carbon);
  box-shadow: 0 2px 0 var(--press);
  border-radius: 2px;
  font-style: normal;
  font-size: 1rem;
  flex: 0 0 30px;
}
.gx-stage .gx-again {
  font-family: var(--sans);
  font-size: .76rem;
  font-weight: 700;
  color: #8a5a00;
  background: var(--reward-tint);
  border: 1.5px solid var(--carbon);
  padding: 3px 10px;
  border-radius: 2px;
  margin-left: 8px;
}
.gx-stage .gx-last-mark {
  color: var(--play-deep);
  font-weight: 800;
}

/* Study phase & sentences */
.gx-stage .gx-line.gx-study {
  font-family: var(--serif);
  font-size: clamp(24px, 3.2vw, 32px);
  font-weight: 500;
  line-height: 1.45;
  color: var(--carbon);
  padding: 18px 24px;
  background: var(--galley);
  border: 2.5px solid var(--carbon);
  border-left: 6px solid var(--play);
  box-shadow: 4px 4px 0 var(--play-tint);
  margin: 10px 0 16px;
  border-radius: 2px;
}
.gx-stage .gx-en {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(18px, 2.4vw, 24px);
  color: var(--steel);
  margin-bottom: 8px;
}
.gx-stage .gx-word {
  font-family: var(--serif);
  font-size: clamp(34px, 5vw, 48px);
  font-weight: 600;
  color: var(--carbon);
  text-align: center;
  margin: 14px 0 8px;
}
.gx-stage .gx-gloss {
  font-family: var(--serif);
  font-size: clamp(22px, 3vw, 28px);
  font-style: italic;
  color: var(--carbon);
  text-align: center;
  margin: 8px 0 16px;
}

/* Interactive elements & answer options */
.gx-stage .gx-mid[data-kind="word"] .gx-opts,
.gx-stage .gx-mid[data-kind="meaning"] .gx-opts,
.gx-stage .gx-mid[data-kind="listen"] .gx-opts,
.gx-stage .gx-mid[data-kind="stress"] .gx-opts,
.gx-stage .gx-mid[data-kind="form"] .gx-opts,
.gx-stage .gx-mid[data-kind="gap"] .gx-opts {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
  margin: 12px 0;
  background: transparent;
  border: none;
  box-shadow: none;
  padding: 0;
}
@media (max-width: 620px) {
  .gx-stage .gx-mid[data-kind="word"] .gx-opts,
  .gx-stage .gx-mid[data-kind="meaning"] .gx-opts,
  .gx-stage .gx-mid[data-kind="listen"] .gx-opts,
  .gx-stage .gx-mid[data-kind="stress"] .gx-opts,
  .gx-stage .gx-mid[data-kind="form"] .gx-opts,
  .gx-stage .gx-mid[data-kind="gap"] .gx-opts {
    grid-template-columns: 1fr;
  }
}

.gx-stage .gx-opt {
  min-height: 58px;
  background: var(--galley);
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  padding: 14px 20px;
  font-family: var(--serif);
  font-size: clamp(20px, 2.5vw, 26px);
  color: var(--carbon);
  border-radius: 2px;
  cursor: pointer;
  text-align: center;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease, background-color .12s ease;
}
.gx-stage .gx-opt:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play);
  border-color: var(--play);
  background: var(--galley);
}
.gx-stage .gx-opt:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--press);
}
.gx-stage .gx-opt:disabled {
  opacity: .5;
  cursor: default;
}
.gx-stage .gx-right {
  background: var(--right) !important;
  color: #fffdf5 !important;
  border-color: var(--carbon) !important;
  box-shadow: 0 4px 0 var(--right-deep) !important;
}
.gx-stage .gx-right::after {
  content: '  ✓';
  font-family: var(--sans);
  font-weight: 900;
}
.gx-stage .gx-wrong {
  background: var(--wrong-tint) !important;
  color: var(--carbon) !important;
  border-color: var(--wrong-deep) !important;
  box-shadow: 0 4px 0 var(--wrong-deep) !important;
}
.gx-stage .gx-wrong::after {
  content: '  ×';
  font-family: var(--sans);
  font-weight: 900;
}

/* Pairs matching */
.gx-stage .gx-pairs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
  margin: 10px 0;
}
.gx-stage .gx-pairs::before {
  display: none;
}
/* THE SORTING BOARD READS AT THE ROOM'S OWN SIZE (2026-08-23). Every other
   answer in the room is set at clamp(20–26px); the pairs alone were pinned
   at 1.12rem — the smallest type on any card, and the owner said so. The
   WORD column takes the study size (it is the object of the exercise); the
   gloss column sits a step below it, a caption to the word, not its equal. */
.gx-stage .gx-pw, .gx-stage .gx-pg {
  width: 100%;
  min-height: 56px;
  padding: 12px 16px;
  font-family: var(--serif);
  font-size: clamp(16px, 2.1vw, 19px);
  background: var(--galley);
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  border-radius: 2px;
  cursor: pointer;
  text-align: left;
  /* the match tiles obey the same law as the answer tiles: a word is never
     split mid-word — the snug pass steps the type down instead */
  overflow-wrap: normal; word-break: keep-all; hyphens: none;
  transition: all .12s ease;
}
.gx-stage .gx-pw { font-size: clamp(19px, 2.5vw, 24px); }
/* narrow rooms keep BOTH columns — a match needs its two halves in view —
   and pay for it in padding, not in type */
@media (max-width: 620px) {
  .gx-stage .gx-pairs { gap: 8px; }
  .gx-stage .gx-pw, .gx-stage .gx-pg {
    min-height: 46px; padding: 10px 10px;
  }
}
.gx-stage .gx-pw:hover, .gx-stage .gx-pg:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play);
  border-color: var(--play);
}
.gx-stage .gx-p-sel {
  background: var(--play-tint) !important;
  border-color: var(--play) !important;
  box-shadow: 0 4px 0 var(--play) !important;
  color: var(--play-deep) !important;
  transform: translateY(-2px);
}
.gx-stage .gx-p-done {
  background: var(--right-tint) !important;
  border-color: var(--right) !important;
  color: var(--carbon) !important;
  font-weight: 600;
  box-shadow: 0 2px 0 var(--right-deep) !important;
  opacity: .95;
  cursor: default;
}

/* Line builder tiles & stick */
.gx-stage .gx-stick {
  min-height: 64px;
  background: var(--galley);
  border: 2.5px solid var(--carbon);
  box-shadow: inset 0 2px 4px rgba(0,0,0,.06);
  padding: 12px;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  border-radius: 2px;
  margin-bottom: 14px;
}
.gx-stage .gx-set {
  font-family: var(--serif);
  font-size: 1.12rem;
  padding: 8px 18px;
  background: var(--play);
  color: #fffdf5;
  border: 2px solid var(--carbon);
  box-shadow: 0 3px 0 var(--play-deep);
  border-radius: 2px;
  cursor: pointer;
  transition: transform .12s ease;
}
.gx-stage .gx-set:hover {
  background: var(--wrong);
  box-shadow: 0 3px 0 var(--wrong-deep);
}
.gx-stage .gx-tile {
  font-family: var(--serif);
  font-size: 1.12rem;
  padding: 10px 18px;
  background: var(--galley);
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  border-radius: 2px;
  cursor: pointer;
  color: var(--carbon);
  transition: transform .12s ease, box-shadow .12s ease;
}
.gx-stage .gx-tile:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play);
  border-color: var(--play);
}
.gx-stage .gx-tile:active:not(:disabled) {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--press);
}
.gx-stage .gx-tile:disabled {
  opacity: .3;
  box-shadow: none;
  cursor: default;
  transform: none;
  background: var(--rule);
}

/* Buttons */
.gx-stage .btn {
  font-family: var(--bill);
  font-size: 1.2rem;
  font-weight: 900;
  letter-spacing: .06em;
  min-height: 48px;
  padding: 10px 22px;
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  border-radius: 2px;
  cursor: pointer;
  transition: transform .12s ease, box-shadow .12s ease;
}
.gx-stage .btn.primary, #gx-next, #gx-got, #gx-check {
  background: var(--play) !important;
  color: #fffdf5 !important;
  box-shadow: 0 4px 0 var(--play-deep) !important;
}
.gx-stage .btn.primary:hover, #gx-next:hover, #gx-got:hover, #gx-check:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play-deep) !important;
}
.gx-stage .btn.primary:active, #gx-next:active, #gx-got:active, #gx-check:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--play-deep) !important;
}
.gx-stage .btn:not(.primary) {
  background: var(--galley);
  color: var(--carbon);
}
.gx-stage .btn:not(.primary):hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play);
  border-color: var(--play);
}
.gx-stage .btn:not(.primary):active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--press);
}

/* Verdict & receipts */
.gx-stage .gx-verdict.gx-ok {
  background: var(--right-tint);
  border: 2.5px solid var(--right);
  color: var(--right-deep);
  padding: 14px 18px;
  border-radius: 2px;
  font-family: var(--bill);
  font-size: 1.4rem;
  font-weight: 900;
  letter-spacing: .04em;
}
.gx-stage .gx-verdict.gx-no {
  background: var(--wrong-tint);
  border: 2.5px solid var(--wrong);
  color: var(--wrong-deep);
  padding: 14px 18px;
  border-radius: 2px;
  font-family: var(--bill);
  font-size: 1.4rem;
  font-weight: 900;
  letter-spacing: .04em;
}
.gx-stage .gx-src {
  background: var(--galley);
  border: 2px solid var(--carbon);
  border-left: 6px solid var(--play);
  padding: 12px 16px;
  margin: 10px 0;
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--carbon);
  border-radius: 2px;
}
.gx-stage .gx-src span {
  font-size: .88em;
  color: var(--steel);
}
.gx-stage .gx-mintline {
  font-family: var(--sans);
  font-size: .82rem;
  font-weight: 800;
  color: var(--play-deep);
  background: var(--play-tint);
  border: 1.5px solid var(--carbon);
  padding: 3px 10px;
  border-radius: 2px;
  width: fit-content;
  margin: 6px 0;
}

/* Companion Mascot in exercise */
.gx-stage .gx-mate {
  position: fixed;
  right: clamp(24px, 4.5vw, 64px);
  bottom: clamp(20px, 3.5vh, 48px);
  width: 112px; height: 112px;
  pointer-events: auto;
  cursor: pointer;
  z-index: 10;
  display: grid;
  place-items: center;
  transition: transform .15s ease;
}
.gx-stage .gx-mate:hover {
  transform: translateY(-3px) scale(1.05);
}
/* THE SEEING-OUT. On the finish sheet the corner pin is wrong: the figure
   hung detached over the page's edge like a sticker. The resident steps
   ONTO the printed sheet instead, beside its own farewell — part of the
   ceremony, not furniture floating past it. */
/* `position: static` here once let the medallion ::before (absolute, inset:0)
   escape to the stage and paint a screen-filling gold disc that also ate every
   click — the mate must stay a positioned element wherever it sits. */
.gx-stage .gx-done .gx-mate,
.gx-stage .gx-sealed .gx-mate {
  position: relative;
  right: auto; bottom: auto;
  margin: 8px auto 0;
}
.gx-stage .gx-mate::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: var(--reward);
  border: 3px solid var(--carbon);
  box-shadow: 0 6px 0 var(--press);
}
.gx-stage .gx-mate svg {
  width: 84px; height: 84px;
  position: relative;
  z-index: 2;
  filter: none;
}
.gx-stage .gx-mate-line {
  position: absolute;
  bottom: calc(100% + 12px);
  right: 0;
  max-width: 240px;
  font-family: var(--serif);
  font-size: .88rem;
  color: var(--carbon);
  background: var(--sheet);
  border: 2px solid var(--carbon);
  box-shadow: 4px 4px 0 var(--play);
  padding: 8px 14px;
  border-radius: 2px;
  text-align: left;
}
.gx-stage .gx-mate-line::after {
  content: '';
  position: absolute;
  bottom: -6px;
  right: 28px;
  width: 10px; height: 10px;
  background: var(--sheet);
  border-right: 2px solid var(--carbon);
  border-bottom: 2px solid var(--carbon);
  transform: rotate(45deg);
}
/* at the door the figure sits HIGH — a slip opening upward from there
   lands on the sheet's own header (it covered Leave in the German room).
   During the door movement the slip hangs below the medallion instead,
   tail up. */
.gx-stage[data-move="door"] .gx-mate-line {
  bottom: auto; top: calc(100% + 12px);
}
.gx-stage[data-move="door"] .gx-mate-line::after {
  bottom: auto; top: -6px;
  transform: rotate(225deg);
}

/* The «8/8» on a finished book is NOT printed on green. This sheet once
   painted that chip --right and inked the numeral to match; the green went
   away and the ink stayed, so the count sat in dark ink on the book's own
   masstone — 1.6:1 to 3.6:1 across the six pigments, and in BOTH rooms,
   because --ink is set inline and cannot lift. 27-route.css:128 already
   pairs that masstone with --galley, correctly; the line that overrode it
   is gone, and the base sheet is left to stand. */
.gx-print { box-shadow: 8px 8px 0 var(--reward); border-width: 3px; }

/* --- practice sitting (SRS review) ----------------------------------- */
/* THE CARD'S SHADOW HAS ONE OWNER, AND THIS IS IT.
   Four files used to assign #rev-card's box-shadow on selectors of equal
   weight — the acts (10-components), the heat (66-rooms), the lamp
   (29-alive) and the slab here — so three of them were always dead and the
   surviving one changed depending on which state happened to be set. To a
   reader that was a black bar snapping in and out from under the card
   between cards. Every one of those files now names only its own LAYER:
     --rev-depth  how far the sheet is pressed into the desk (the acts)
     --rev-glow   the run's warmth in the book's own ink (the heat)
     --rev-lamp   the night-desk lamp on the paper (29-alive)
   and they are stacked here, once, on one timing. */
#v-review #rev-card {
  --rev-depth: 5px;
  --rev-glow: 0 0 0 0 transparent;
  background: var(--sheet);
  border: 3px solid var(--carbon);
  box-shadow: 0 var(--rev-depth) 0 var(--play-deep), var(--rev-glow), var(--rev-lamp, 0 0 0 0 transparent);
  transition: box-shadow 420ms var(--e-std), border-color 420ms var(--e-std);
  border-radius: 2px;
  padding: 26px 24px;
}
#v-review #rev-card .smallcaps {
  font-family: var(--bill);
  font-size: 1.15rem;
  font-weight: 900;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--carbon);
  margin-bottom: 12px;
}
#v-review #rev-card .produce-gloss {
  font-family: var(--serif);
  font-size: clamp(22px, 3.2vw, 30px);
  font-style: italic;
  color: var(--carbon);
  margin: 12px 0 16px;
  text-align: center;
}
#v-review #rev-card .reveal-word {
  font-family: var(--serif);
  font-size: clamp(28px, 4.5vw, 40px);
  font-weight: 600;
  color: var(--carbon);
  margin: 10px 0 16px;
  text-align: center;
}
#v-review #rev-card .cloze {
  font-family: var(--serif);
  font-size: clamp(20px, 2.6vw, 26px);
  line-height: 1.45;
  color: var(--carbon);
  padding: 16px 20px;
  background: var(--galley);
  border: 2px solid var(--carbon);
  border-left: 6px solid var(--play);
  box-shadow: 4px 4px 0 var(--play-tint);
  border-radius: 2px;
  margin: 12px 0 20px;
}
#v-review #rev-card .conf-row {
  display: flex;
  gap: 12px;
  justify-content: center;
  margin-top: 14px;
}
#v-review #rev-card .conf-row button {
  flex: 1;
  min-height: 52px;
  font-family: var(--bill);
  font-size: 1.25rem;
  font-weight: 900;
  letter-spacing: .06em;
  text-transform: uppercase;
  background: var(--galley);
  color: var(--carbon);
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  border-radius: 2px;
  cursor: pointer;
  transition: all .12s ease;
}
#v-review #rev-card .conf-row button:hover {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play);
  border-color: var(--play);
}
#v-review #rev-card .conf-row button:active {
  transform: translateY(3px);
  box-shadow: 0 1px 0 var(--press);
}
/* CONFIDENCE IS NOT A VERDICT. These buttons ask what the reader believes —
   painting SURE green and NO IDEA red judged the answer before it was given,
   against the room's own law (heat, not judgement, never red). They wear the
   same border-weight language the practice verdicts wear: carbon for the
   confident hand, the signal ink for the honest blank. */
#v-review #rev-card .conf-row button[data-c="2"] {
  border-color: var(--carbon);
  color: var(--carbon);
  border-width: 3.5px;
}
#v-review #rev-card .conf-row button[data-c="0"] {
  border-color: var(--signal-ink, #a5342a);
  color: var(--signal-ink, #a5342a);
}
#v-review #rev-card .pat-opts {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-top: 16px;
}
@media (max-width: 600px) {
  #v-review #rev-card .pat-opts { grid-template-columns: 1fr; }
}
#v-review #rev-card .quiz-opt {
  min-height: 52px;
  background: var(--galley);
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  padding: 12px 18px;
  font-family: var(--serif);
  font-size: 1.15rem;
  color: var(--carbon);
  border-radius: 2px;
  cursor: pointer;
  transition: all .12s ease;
}
#v-review #rev-card .quiz-opt:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 0 var(--play);
  border-color: var(--play);
}
#v-review #rev-card .quiz-opt.right {
  background: var(--right) !important;
  color: #fffdf5 !important;
  border-color: var(--carbon) !important;
  box-shadow: 0 4px 0 var(--right-deep) !important;
}
#v-review #rev-card .quiz-opt.wrong {
  background: var(--wrong-tint) !important;
  color: var(--carbon) !important;
  border-color: var(--wrong-deep) !important;
  box-shadow: 0 4px 0 var(--wrong-deep) !important;
}
/* THE CARD MUST FIT THE HAND IT IS HELD IN. On a 390px phone the recall
   card's own furniture — 26px of padding, a 1.4rem gloss, a 26px cloze —
   pushed «how sure are you?» and its three buttons clean below the fold, so
   the first thing a reader had to do to answer the first card was scroll to
   find the answer. NO IDEA also wrapped to two lines while its neighbours
   stayed on one, which is what made the row look broken rather than merely
   tall. The type comes down a step and the row is told not to break. */
@media (max-width: 600px) {
  #v-review #rev-card { padding: 16px 14px; }
  #v-review #rev-card .smallcaps { font-size: 1rem; margin-bottom: 8px; }
  #v-review #rev-card .produce-gloss { font-size: clamp(19px, 5.4vw, 24px); margin: 8px 0 10px; }
  #v-review #rev-card .reveal-word { font-size: clamp(24px, 7vw, 32px); margin: 6px 0 12px; }
  #v-review #rev-card .cloze {
    font-size: clamp(17px, 4.6vw, 21px); line-height: 1.38;
    padding: 12px 14px; margin: 10px 0 14px;
  }
  #v-review #rev-card .conf-row { gap: 8px; }
  #v-review #rev-card .conf-row button {
    min-height: 46px; font-size: 1rem; letter-spacing: .02em;
    padding: 6px 4px; white-space: nowrap;
  }
  #v-review #rev-card .quiz-opt { min-height: 46px; padding: 10px 12px; font-size: 1.02rem; }
}
#v-review #rev-btns button {
  min-height: 48px;
  font-family: var(--bill);
  font-size: 1.18rem;
  font-weight: 900;
  letter-spacing: .06em;
  border: 2.5px solid var(--carbon);
  box-shadow: 0 4px 0 var(--press);
  border-radius: 2px;
  cursor: pointer;
  padding: 8px 18px;
  transition: all .12s ease;
}

/* --- calm reading plane ------------------------------------------------ */
#v-read { background: var(--sheet); }
#v-read .sent, #v-read .read-text, #v-read .scene-body { font-family: var(--serif); }
#readbar {
  background: var(--play);
  color: var(--on-play);
  border-bottom: 3px solid var(--press);
  box-shadow: 0 4px 0 var(--play-deep);
}
#readbar .icon-btn { background: var(--paper); color: var(--carbon); box-shadow: 0 3px 0 var(--press); }
#pop { border: 3px solid var(--carbon); box-shadow: 7px 7px 0 var(--play); }

/* --- responsive: keep the game legible, not merely smaller ------------- */
@media (max-width: 860px) {
  .desk-intro { grid-template-columns: 1fr; gap: 20px; min-height: 0; }
  .desk-hud { grid-template-columns: repeat(2, 1fr); }
  .desk-hud .hud-pill:nth-child(n+3) { border-top: 1px solid color-mix(in srgb, var(--rule-room) 62%, transparent); }
  .desk-hud .hud-pill:nth-child(3) { border-left: none; }
  .desk-host { min-height: 238px; grid-template-columns: minmax(170px, 1fr) 140px; }
  .desk-cast { width: 136px; height: 170px; }
  .desk-cast-pedestal { width: 130px; }
  .desk-host-bubble { padding: 14px 16px; }
  .desk-host-bubble::after { right: -11px; top: 60px; }
}

@media (max-width: 580px) {
  .bar { min-height: 56px; padding-inline: 10px; }
  /* was putting desktop padding back on top of the shell's phone economies */
  .bar .nav-tabs button { padding-inline: 6px; }
  /* the lesson header pays for the phone in padding and in its duplicate:
     the «3 of 8 set» count restates what the rail already draws, and on a
     390px screen it was the thing squeezing the chapter's name (2026-08-23) */
  .gx-stage .gx-top { padding: 10px 12px !important; }
  .gx-stage .gx-top .gx-count { display: none; }
  /* THE FOLD IS THE FIRST ARGUMENT. On a 390px phone the masthead alone was
     ~690px — a visitor's whole first screen was a title. The room's name can
     be grand at 11vw and still leave the day ledger (the screen that proves
     the desk remembers you) inside the first view. */
  #v-home > .masthead { padding: 16px 14px 18px; margin-block: 14px 12px; box-shadow: 6px 6px 0 var(--reward); }
  #v-home > .masthead::after { right: 8px; bottom: 7px; }
  #v-home .masthead .wordmark { flex-wrap: wrap; }
  #v-home .masthead h1, #v-home #room-name { font-size: clamp(34px, 11vw, 52px); margin-top: 10px; margin-bottom: 8px; }
  #v-home .masthead .room-line { font-size: var(--t-00); }
  #v-home .masthead .almanac { margin-top: 8px; font-size: .85rem; }
  .desk-intro { gap: 12px; }
  .desk-host { grid-template-columns: 1fr; min-height: 0; }
  .desk-cast-wrap { display: none; }
  .desk-host-bubble { padding: 12px 14px; }
  .desk-host-bubble::after { display: none; }
  .desk-hud { grid-template-columns: 1fr 1fr; margin-block: 14px 20px; }
  .hud-pill { padding: 10px 12px; gap: 9px; }
  .hud-pill:nth-child(n+3) { border-top: 1px solid color-mix(in srgb, var(--rule-room) 62%, transparent); }
  .hud-pill:nth-child(3) { border-left: none; }
  .hud-icon { width: 32px; height: 32px; }
  .hud-icon .ic { width: 19px; height: 19px; }
  .hud-body strong { font-size: 1.05rem; }
  #desk-host-line { font-size: clamp(22px, 6.5vw, 28px); }
  .spread-hero, .tonight-card, .gx-stage .gx { box-shadow: 5px 5px 0 var(--play); }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; }
}

/* ============================================================================
   INTEGRATION MENDS — where the arcade skin broke the room underneath it.
   From the 2026-08-15 cascade audit; each rule names its wound. This section
   loads last inside the last sheet, so it wins every tie below on purpose.
   ============================================================================ */

/* 1 · the seal-ceremony sheet had ZERO side padding: the padding restore at
   .gx-done was never extended to .gx-sealed, so the wax pressed the border */
.gx-stage .gx.gx-sealed { padding: 24px clamp(18px, 4vw, 40px) 28px; }

/* 2 · the phone companion: a 112px fixed medallion sat over the answer row
   and ATE its taps — the old ≤700px shrink lost on specificity and died */
@media (max-width: 700px) {
  .gx-stage .gx-mate { width: 72px; height: 72px; right: 10px; bottom: 10px; pointer-events: none; }
  .gx-stage .gx-mate svg { width: 54px; height: 54px; }
}
/* …and the forge grew the medallion to 180px around a fixed 84px figure */
.gx-stage[data-move="forge"] .gx-mate { width: 112px; height: 112px; }

/* 3 · СБОР: the gather stick is a PAGE BEING SET — ruled rows, never a chip
   row. The arcade .gx-stick flex laid Chekhov's scene out as wrapped pills. */
.gx-stage .gx-gather-stick { display: grid; gap: 0; padding: 0 12px; min-height: 80px; }

/* 4 · a green slab and a '✓' were injected INSIDE the word on stress and
   find-the-word — a verdict may never mutilate the word it praises */
.gx-stage .gx-vowel.gx-right, .gx-stage .gx-find-w.gx-right {
  background: transparent !important;
  color: inherit !important;
  box-shadow: inset 0 -3px 0 var(--right) !important;
}
.gx-stage .gx-vowel.gx-right::after, .gx-stage .gx-find-w.gx-right::after { content: none; }

/* 5 · the friend bubble wore two tails: the rewritten ::before kept the old
   triangle's stray borders, and the old ::after was never retired */
.friend-strip .fr-bubble::before { border-top: 0; border-right: 0; }
.friend-strip .fr-bubble::after { display: none; }

/* 6 · VERDICT WEIGHT RESTORED. The tiers vary the acknowledgment by what
   actually happened; the flat arcade verdict erased all of it and every
   answer read identically. Scale by deed; gold is the coup's alone. */
.gx-stage .gx-verdict.gx-ok.gx-v-first { color: var(--play-deep); }
.gx-stage .gx-verdict.gx-ok.gx-v-won   { color: var(--right-deep); }
.gx-stage .gx-verdict.gx-ok.gx-v-run   { color: var(--play); font-size: 1.5rem; }
.gx-stage .gx-verdict.gx-ok.gx-v-half  { font-size: 1.55rem; }
.gx-stage .gx-verdict.gx-ok.gx-v-last  { font-size: 1.6rem; }
.gx-stage .gx-verdict.gx-ok.gx-v-coup  { color: var(--reward-deep); font-size: 1.7rem; }

/* 7 · THE HEAT, in the arcade's own voice. The old ink-band and paper-wash
   channels died with the paper (background !important); the run's warmth now
   lives in the sheet's hard shadow, navy warming toward gold with the streak
   and dropping cold at a miss — in one motionless frame, per the charter. */
.gx-stage .gx {
  box-shadow: 8px 8px 0 color-mix(in srgb,
    var(--play) calc(100% - (var(--heat, 0) * 100%)), var(--reward));
  transition: box-shadow 700ms ease;
}
.gx-stage.gx-heat-cut .gx { transition: none; }

/* 8 · the run BLAZES again at six and nine (box-shadow:none had silenced it) */
.gx-stage.gx-run-blaze .gx {
  box-shadow: 8px 8px 0 var(--reward), 0 0 0 3px var(--reward-tint);
}

/* 9 · the golden sort shows its gilding again — the green right-slab was
   painting straight over the rarest strike in the room */
.gx-stage .gx-opt.gx-gilded, .gx-stage .gx-pw.gx-gilded {
  background: var(--reward-tint) !important;
  border-color: var(--reward-deep) !important;
  box-shadow: 0 4px 0 var(--reward-deep) !important;
}

/* 10 · goal gradient at the seal: the chapter bar takes gold when the end is
   near, and the countdown line speaks beneath it */
.gx-stage .gx-chbar.gx-chbar-near i { background: var(--reward); }
.gx-stage .gx-chbar-say {
  margin: 4px 22px 0; text-align: right;
  font-family: var(--bill); font-weight: 700; font-size: .72rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--steel);
}
.gx-stage .gx-chbar.gx-chbar-near + .gx-chbar-say { color: var(--reward-deep); }

/* 11+12 · retired in place. The movement tints and the mood tints each held
   the ONE background channel, and the later block silently erased the
   earlier — a forge lesson in focus mood lost its forge, and the door never
   had a dress at all. Both now compose in mend 21, at the tail. */

/* 13 · the phone header may wrap again — nowrap crushed the title at 390px */
@media (max-width: 620px) {
  .gx-stage .gx-top { flex-wrap: wrap !important; row-gap: 4px; }
}

/* 14 · the payout's closing image: the finest fallen line, set larger.
   (No min-height reservation here: on the sheet's grid a reserved row froze
   at its minimum and the async payout PAINTED OVER the recap below it — a
   small reflow is honest; overlapping type is not.) */
.gx-stage .gx-src.gx-src-fine { font-size: clamp(21px, 2.9vw, 28px); }

/* 15 · the mint tip's anchor, made structural (it leaned on a rule two
   sheets away that this skin happens not to override — yet) */

/* 17 · THE BROADSHEET FINISH. The recap was a narrow column adrift in an
   empty room — a poster-sized hero line, then a fold that cut the recap and
   sometimes the door itself. The sheet widens; the recap sets in two real
   columns (the literature on the left, the ledger on the right); the hero
   is a band, not a poster; and the door rides a sticky bar that cannot
   leave the screen. */
.gx-stage .gx { width: min(880px, 96vw); }
.gx-stage .gx.gx-done:has(.gx-cols) { width: min(1160px, 96vw); }
.gx-stage .gx-done .gx-print-ru { font-size: clamp(22px, 3vw, 34px); }
.gx-done .gx-cols {
  display: grid; grid-template-columns: 1.12fr .88fr;
  gap: 8px clamp(28px, 4vw, 56px);
  align-items: start;
  margin-top: 10px;
}
.gx-done .gx-side {
  border-left: 2px solid color-mix(in srgb, var(--carbon) 22%, transparent);
  padding-left: clamp(18px, 2.2vw, 30px);
}
/* the lamp block is a full-bleed band on the plain recap; inside the side
   column the band's negative margins would burst the column */
.gx-done .gx-side .gx-night { margin-inline: 0 !important; padding-inline: 0 !important; }
@media (max-width: 940px) {
  .gx-done .gx-cols { grid-template-columns: 1fr; }
  .gx-done .gx-side { border-left: none; padding-left: 0; }
}
.gx-stage .gx-done > .btnrow {
  position: sticky; bottom: 0; z-index: 6;
  margin: 20px calc(var(--gxp, 24px) * -1) -28px;
  padding: 12px var(--gxp, 24px) 16px;
  background: var(--sheet);
  border-top: 2.5px solid var(--carbon);
}

/* 18 · the flanks live: the chapter's own stamp stands ghosted in the left
   margin of wide rooms — the arcade grid had erased every side presence,
   and the companion already holds the right */
@media (min-width: 1240px) {
  .gx-stage.gx-marked::after {
    content: '' !important; display: block !important;
    position: fixed; left: clamp(10px, 2.5vw, 70px); bottom: 7vh;
    width: 32vh; height: 32vh;
    background: var(--ghost-img) center / contain no-repeat;
    opacity: .06; pointer-events: none;
  }
}

/* 19 · THE READING LAMP. The ergonomics literature is consistent: dark text
   on a warm off-white page (never pure white — glare), a surround dimmer
   than the page but never black beside a bright page (halation strains
   astigmatic eyes; extreme ratios strain everyone's), a slightly larger
   size than habit suggests, and warmth rising in the evening. The lamp
   states all of it at once: warm paper, a soft pool of light, the room
   stepped back, the chrome quieted. */
/* the surround sits at roughly a fifth of the page's luminance — the IES
   guidance caps task:surround near 3:1–6:1; a black room behind a bright
   page (50:1) is precisely the strain the mode exists to remove */
body.read-lamp:has(#v-read.show) { background: #6d5f47; }
body.read-lamp #v-read {
  background: transparent;
}
body.read-lamp #read-wrap {
  /* Kindle sepia's family: warm paper, never #fff — glare off the top of
     the luminance histogram, positive polarity kept (the pupil likes it) */
  background: #f9efd9;
  color: #43331f;
  /* and no radius. Paper is cut straight (70-shell), and the lamp is a switch
     the reader can flick mid-page: a 4px here visibly rounded the corners of
     the book the moment it came on, while the ruled surround stayed hard. */
  box-shadow: 0 0 0 1px #d9c9a8,
              0 0 110px 34px rgba(255, 209, 140, .22),
              0 18px 60px -22px rgba(46, 35, 18, .6);
  padding: clamp(20px, 4vw, 52px) clamp(16px, 3vw, 44px);
  font-size: 1.05em;
}
/* the evening dims FIRST and warms second — the melatonin research says
   photon dose beats hue, so the page itself steps down as the hour does */
body[data-daypart="evening"].read-lamp #read-wrap { background: #f2e3c4; }
body[data-daypart="evening"].read-lamp:has(#v-read.show) { background: #61533c; }
body[data-daypart="night"].read-lamp #read-wrap { background: #e9d7b2; }
body[data-daypart="night"].read-lamp:has(#v-read.show) { background: #554834; }
/* the pool of light: the room's edges fall away gently — never to black */
body.read-lamp:has(#v-read.show)::after {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 2;
  background: radial-gradient(ellipse 62% 78% at 50% 38%,
    transparent 54%, rgba(46, 36, 20, .30));
}
/* the chrome steps back until asked for */
body.read-lamp:has(#v-read.show) > header { filter: brightness(.62) saturate(.7); }
body.read-lamp #readbar { opacity: .5; transition: opacity 300ms linear; }
body.read-lamp #readbar:hover, body.read-lamp #readbar:focus-within { opacity: 1; }
body.read-lamp #btn-lamp, #btn-lamp.on {
  background: var(--reward-tint); border-color: var(--reward-deep);
}

/* 20 · THE RIBBON, SEEN. The return scroll marks the resume line — a
   vermilion ribbon in the margin and a warm wash that dries away — so the
   reader lands knowing exactly where they are. */
/* The drying used to be a raw `transition` — 2.4s and 1.2s behind 2.8s and
   4.4s of delay, none of it through --motion, none of it on a house curve,
   all of it on running prose in the one route choreo stands down entirely.
   The reader who asked for calm, or for no motion at all, still watched a
   wash sit on the line for five and a half seconds. So: the mark is simply
   PRESENT, and the drying is an animation like its six neighbours in the
   mends below — through the scalar, on --settle, and not declared at all at
   the reduced tiers, where the ribbon just stays until app.js lifts the
   class at 6.5s. */
@keyframes ribbon-dry {
  0%, 55% { box-shadow: inset 4px 0 0 var(--verm, #a5342a);
            background: color-mix(in srgb, var(--reward, #f6c445) 16%, transparent); }
  100%    { box-shadow: inset 4px 0 0 transparent; background: transparent; }
}
#text .sent.ribbon-here {
  box-shadow: inset 4px 0 0 var(--verm, #a5342a);
  background: color-mix(in srgb, var(--reward, #f6c445) 16%, transparent);
}
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) #text .sent.ribbon-here {
    animation: ribbon-dry calc(5.6s * var(--motion, 1)) var(--settle) both;
  }
}

/* 16 · the phone-width sheet shadow stays 5px, heat included */
@media (max-width: 700px) {
  .gx-stage .gx {
    box-shadow: 5px 5px 0 color-mix(in srgb,
      var(--play) calc(100% - (var(--heat, 0) * 100%)), var(--reward));
  }
  .gx-stage.gx-run-blaze .gx {
    box-shadow: 5px 5px 0 var(--reward), 0 0 0 3px var(--reward-tint);
  }
}

/* ============================================================================
   MENDS, SECOND PASS — the thirteen collisions of the 2026-08-15 Learn audit.
   Same law as above: this tail loads after everything, and wins every tie
   on purpose. Narrow-width Learn rules live HERE or nowhere (the ≤620 rules
   left in 27-route are dead on arrival against the skin's unconditioned
   chips — equal specificity, later file).
   ============================================================================ */

/* 21 · ONE WEATHER SYSTEM. Movement names the room's ink; the chapter
   deepens it (--tint — written every lesson, consumed nowhere since the
   skin landed: "twenty rooms" had collapsed back to one hall); the mood
   then draws the SAME room in or calms it instead of repainting it generic.
   Custom properties compose; only the final rule carries the weight. */
.gx-stage {
  --atm-ink: var(--play);
  --atm-w: 5%;
  --atm-mood-ink: var(--paper);
  --atm-mood-w: 0%;
}
.gx-stage[data-move="door"]   { --atm-ink: var(--ink, var(--play)); --atm-w: 11%; }
.gx-stage[data-move="forge"]  { --atm-ink: var(--wrong-tint); --atm-w: 24%; }
.gx-stage[data-move="gather"] { --atm-ink: var(--right-tint); --atm-w: 22%; }
.gx-stage[data-move="set"]    { --atm-ink: var(--reward-tint); --atm-w: 26%; }
.gx-stage[data-mood="focus"]  { --atm-mood-ink: var(--play-tint); --atm-mood-w: 30%; }
.gx-stage[data-mood="steady"] { --atm-mood-ink: var(--paper); --atm-mood-w: 42%; }
.gx-stage {
  background-color: color-mix(in srgb,
    color-mix(in srgb, var(--atm-ink) calc(var(--atm-w) + var(--tint, 0) * 1.5%), var(--paper))
    calc(100% - var(--atm-mood-w)), var(--atm-mood-ink)) !important;
}
/* the WALLS return with the weather (box-shadow:none had razed them): a
   quiet vignette at rest, drawn in at focus, the lamp lowered at steady —
   and the door's light falls from above again, so the first ten seconds of
   a chapter are lit like an arrival and not like a form */
.gx-stage { box-shadow: inset 0 0 11vmax rgba(24, 19, 9, .07) !important; }
.gx-stage[data-move="door"] {
  box-shadow: inset 0 45vh 35vh -35vh
    color-mix(in srgb, var(--ink, var(--carbon)) 20%, transparent) !important;
}
.gx-stage[data-mood="focus"]  { box-shadow: inset 0 0 14vmax rgba(22, 18, 8, .15) !important; }
.gx-stage[data-mood="steady"] { box-shadow: inset 0 0 18vmax rgba(30, 22, 8, .12) !important; }
[data-theme="dark"] .gx-stage { box-shadow: inset 0 0 11vmax rgba(0, 0, 0, .30) !important; }
/* the door keeps its arrival light in the dark too — the dark base above
   ties it on specificity and would win on order alone */
[data-theme="dark"] .gx-stage[data-move="door"] {
  box-shadow: inset 0 45vh 35vh -35vh
    color-mix(in srgb, var(--ink, var(--carbon)) 32%, transparent) !important;
}
[data-theme="dark"] .gx-stage[data-mood="focus"]  { box-shadow: inset 0 0 14vmax rgba(0, 0, 0, .48) !important; }
[data-theme="dark"] .gx-stage[data-mood="steady"] { box-shadow: inset 0 0 18vmax rgba(0, 0, 0, .38) !important; }

/* 22 · the medallion still ate taps between 701 and 1100px: the sheet is
   min(880px, 96vw) and the fixed figure overlapped its right column — the
   full-width Next button poked the figure instead of advancing. The figure
   yields its pointer in the overlap band and keeps it on wide rooms. */
@media (min-width: 701px) and (max-width: 1100px) {
  .gx-stage .gx-mate { width: 84px; height: 84px; right: 10px; bottom: 12px;
    pointer-events: none; cursor: default; }
  .gx-stage .gx-mate svg { width: 64px; height: 64px; }
  .gx-stage[data-move="forge"] .gx-mate { width: 96px; height: 96px; }
}
/* …and at the door on laptops the top-of-stairs blocking brushed the tray */
@media (min-width: 701px) and (max-width: 1300px) {
  .gx-stage[data-move="door"] .gx-mate { top: clamp(120px, 20vh, 180px); }
}

/* 23 · the phone bubble: the ≤700 mate-line shrink lost to the skin's own
   240px — on a 390px screen the counsel slip covered the answer area */
@media (max-width: 700px) {
  .gx-stage .gx-mate-line { max-width: 170px; font-size: .74rem; }
}

/* 24 · the road's phone shrink, restated at winning specificity */
.rd .rd-vol-a { height: 60px; }   /* the squat chip distinction, restored */
@media (max-width: 620px) {
  .rd .rd-hit { grid-template-columns: 48px 1fr; }
  .rd .rd-vol { width: 46px; height: 62px; }
  .rd .rd-vol-a { height: 46px; }
  ol.rd::before { left: 21px; }
  .rd .rd-stamp { display: none; }  /* a 112px ghost behind 300px of row text */
  .gx-stage .gx { gap: 20px; }
}

/* 25 · three tenants, one pseudo: the glint and the tier press-pass both
   rode ::after, which mend 18 gave to the ghost stamp — on wide marked
   rooms a gold strike slid the 32vh ornament across the wall and a tier
   verdict wiped it. The stamp keeps ::after, whole and still; the glint
   takes ::before; the press-pass is retired for good (the tier already
   speaks through mend 6's verdict scale — one channel, one tenant). */
/* (top:auto also re-kills 26:609's `top:0`, which otherwise teleports the
   ghost stamp to the viewport top on every tier flash — mends 18/34 never
   declare top, so the old ink-band's copy was leaking through) */
.gx-stage.gx-glint::after, .gx-stage[class*="gx-m-"]::after {
  animation: none !important; top: auto !important;
}
@media (prefers-reduced-motion: no-preference) {
  .gx-stage.gx-glint::before {
    content: '' !important; display: block !important;
    position: fixed; inset: 0; pointer-events: none; z-index: 2;
    /* height/width stated: 26:209's 6px ink-band height cascades into this
       pseudo otherwise, and over-constrained fixed positioning keeps
       top+height — the whole lamplight pass collapsed to a sliver */
    height: auto; width: auto;
    background: linear-gradient(105deg, transparent 38%,
      color-mix(in srgb, var(--reward) 26%, transparent) 50%, transparent 62%)
      no-repeat;
    background-size: 220% 100%;
    animation: gx-mend-glint 1.2s cubic-bezier(.4, 0, .2, 1) both;
  }
  @keyframes gx-mend-glint {
    from { background-position: 130% 0; }
    to   { background-position: -30% 0; }
  }
}
/* …and the glint may no longer freeze the room's scroll for its second */
.gx-stage.gx-glint { overflow: hidden auto; }

/* 26 · WHERE YOU ARE had lost the skin: the 8-second heartbeat declared
   box-shadow in its keyframes, and an infinite animation outranks normal
   declarations for its whole life — so the one row that says "here" wore
   the pre-arcade dress forever. The beat now breathes in light alone and
   leaves the chip's ink to the skin. */
@media (prefers-reduced-motion: no-preference) {
  .rd .rd-here .rd-vol { animation: rd-mend-here 8s cubic-bezier(.45, .05, .25, 1) infinite; }
  @keyframes rd-mend-here {
    0%, 92%, 100% { filter: none; }
    96% { filter: drop-shadow(0 0 7px color-mix(in srgb, var(--reward) 75%, transparent)); }
  }
}

/* 27 · the printed line wears the arcade plate for EVERYONE: gx-strike's
   final keyframe pinned the old carbon shadow (fill:both outranks normal
   declarations), so full-motion readers saw a different recap than calm
   readers. Important beats the animation; the strike still plays. */
.gx-stage .gx-print { box-shadow: 8px 8px 0 var(--reward) !important; border-width: 3px; }

/* 28 · focus weather on the navy band: a half-pixel blur at 55% opacity
   reads as broken paint, not as the room drawing in. The chrome steps back
   without smearing. */
.gx-stage[data-mood="focus"] .gx-top,
.gx-stage[data-mood="focus"] .gx-rail { opacity: .85; filter: none; }

/* 29 · the seal sheet's doors ride the same sticky bar as the recap's —
   "Read it now" was falling below the fold of short phones, on the one
   sheet whose whole point is that door */
.gx-stage .gx-sealed #gx-seal-rest .btnrow {
  position: sticky; bottom: 0; z-index: 6;
  margin: 20px calc(clamp(18px, 4vw, 40px) * -1) -28px;
  padding: 12px clamp(18px, 4vw, 40px) 16px;
  background: var(--sheet);
  border-top: 2.5px solid var(--carbon);
}

/* 30 · the run counter no longer jolts the header: the empty span it
   replaced at streak 3 shifted tray, count and Leave at the exact moment
   of praise. The slot is always set; only its ink arrives. */
.gx-stage .gx-runn-off { visibility: hidden; }

/* 31 · «превосходительство» may not break between two letter-buttons: the
   stress word is one word, holds one line, and shrinks before it wraps */
.gx-stage .gx-stress-word {
  display: flex; flex-wrap: nowrap; align-items: baseline;
  justify-content: safe center;
  max-width: 100%; overflow-x: auto;
}
@media (max-width: 700px) {
  .gx-stage .gx-stress-word { font-size: clamp(26px, 10vw, 44px); }
}

/* 32 · the road's standing states that rendered as nothing: walked ground
   reads walked, and the entrance hall's soft "you are here" is a place */
.rd .rd-past .rd-vol { background: color-mix(in srgb, var(--right) 22%, var(--play-tint)); }
.rd .rd-here-soft .rd-vol {
  background: color-mix(in srgb, var(--reward) 40%, var(--play-tint));
  box-shadow: inset 8px 0 0 var(--play), 0 4px 0 var(--press);
}

/* 33 · the margins belong to the scene again: the bloom the course stages
   every question (data-side l/r — written since August, drawn never)
   returns as a wide breath of the chapter's own pigment in the outer
   margin — under the sheet, over the grid, still. */
@media (prefers-reduced-motion: no-preference) and (min-width: 900px) {
  .gx-stage .gx-bloom {
    display: block !important;
    position: fixed; top: 16vh; bottom: 10vh; width: 24vw;
    pointer-events: none; z-index: -1;
    opacity: 0; transition: opacity 2.4s ease-out;
    background: radial-gradient(closest-side,
      color-mix(in srgb, var(--ink, var(--play)) 15%, transparent), transparent 72%);
  }
  .gx-stage[data-side="l"] .gx-bloom { left: -5vw; opacity: 1; }
  .gx-stage[data-side="r"] .gx-bloom { right: -5vw; opacity: 1; }
  .gx-stage .gx-bloom i {
    position: absolute; inset: 0; display: grid; place-items: center;
    font-style: normal; font-size: 16vh; line-height: 1;
    color: var(--ink, var(--play)); opacity: .28;
  }
}

/* 34 · the book's mark returns to laptops: between 620 and 1240px a chapter
   room had no presence at all — mend 18 opened the stamp only past 1240.
   Smaller, lower, fainter; the same stillness. */
@media (min-width: 900px) and (max-width: 1239.98px) {
  .gx-stage.gx-marked::after {
    content: '' !important; display: block !important;
    position: fixed; left: 10px; bottom: 5vh;
    width: 20vh; height: 20vh;
    background: var(--ghost-img) center / contain no-repeat;
    opacity: .05; pointer-events: none;
  }
}

/* 35 · THE LAMP MADE FLESH. The lamp species stands on the finish sheet
   beside its own report; on the evening's first full sitting the flame
   takes — one breath-tempo bloom that settles into a standing glow. The
   waiting lamp stands unlit and a little grey. Calm and reduced-motion
   readers keep the line and the still glyph (course.js skips the mount). */
.gx-night { display: flex; align-items: center; gap: 12px; }
.gx-night-body { flex: 1 1 auto; min-width: 0; }
.gx-lampfig { width: 64px; height: 72px; flex: 0 0 auto; }
.gx-lampfig:empty { display: none; }
.gx-lampfig svg { width: 100%; height: 100%; }
.gx-night .gx-lampfig {
  filter: drop-shadow(0 0 5px color-mix(in srgb, var(--reward) 40%, transparent));
}
.gx-night-wait .gx-lampfig { filter: grayscale(.7) opacity(.75); }
@media (prefers-reduced-motion: no-preference) {
  .gx-night-lit .gx-lampfig {
    animation: gx-lamp-take 4s cubic-bezier(.3, 0, .2, 1) both;
  }
  /* every keyframe carries the SAME filter-function list — mismatched
     lists don't interpolate and the taking would snap instead of bloom */
  @keyframes gx-lamp-take {
    0%   { filter: grayscale(.7) opacity(.75) drop-shadow(0 0 0 transparent); }
    35%  { filter: grayscale(0) opacity(1) drop-shadow(0 0 16px color-mix(in srgb, var(--reward) 85%, transparent)); }
    100% { filter: grayscale(0) opacity(1) drop-shadow(0 0 5px color-mix(in srgb, var(--reward) 40%, transparent)); }
  }
}

/* the first signature: seven leaves, one thread, sewn on the seventh */
.gx-leaves { display: inline-flex; gap: 5px; margin-top: 7px; position: relative; align-items: center; }
.gx-leaves i {
  width: 10px; height: 14px; display: block; border-radius: 1px;
  border: 1.5px solid var(--carbon); background: var(--sheet);
}
.gx-leaves i.gx-leaf-on { background: var(--reward-tint); border-color: var(--reward-deep); }
.gx-leaves .gx-thread {
  position: absolute; left: 0; top: 50%; height: 2px;
  background: var(--reward-deep); pointer-events: none;
  transition: width 1.2s cubic-bezier(.3, 0, .2, 1);
}

/* 36 · LIVING SELECTION. The waiting lesson row names its payoff in the
   chapter's own ink; the locked chapter wears UNCUT LEAVES — a folded,
   never-opened corner — as the lock's grammar; and an opened chapter body
   is CUT open, one knife-stroke down the page, once per opening. */
.rd .rd-l-scent {
  display: block; font-size: .74rem; font-style: italic;
  color: var(--ink, var(--play)); opacity: .85; margin-top: 2px;
}
.rd .rd-l-scent:empty { display: none; }
.rd .rd-ch-glass { position: relative; }
.rd .rd-ch-glass::after {
  content: ''; position: absolute; top: 0; right: 0; width: 40px; height: 40px;
  pointer-events: none; z-index: 3;
  background: linear-gradient(225deg,
    var(--paper) 0 49%,
    color-mix(in srgb, var(--carbon) 30%, var(--paper)) 49% 54%,
    color-mix(in srgb, var(--carbon) 10%, transparent) 54% 58%,
    transparent 58%);
}
@media (prefers-reduced-motion: no-preference) {
  .rd .rd-ch.rd-open .rd-ch-body { animation: rd-knife .7s cubic-bezier(.2, .9, .2, 1) both; }
  @keyframes rd-knife {
    from { clip-path: inset(0 0 100% 0); }
    to   { clip-path: inset(0 0 0 0); }
  }
}

/* 37 · THE SCENE LEDGER'S SURFACES (data/scenework.json — all decline when
   the ledger has no entry). The story question held open at the door; the
   librarian's word about tomorrow on the finish sheet; the "In which…"
   line on the chapter card; the next scene's question under the curtain. */
.gx-stage .gx-doorq {
  font-family: var(--serif); font-size: 1.02rem; margin: 10px 0 0;
  color: var(--carbon);
}
.gx-stage .gx-doorq i { opacity: .6; font-size: .88rem; }
.gx-stage .gx-morrow {
  font-family: var(--serif); font-style: italic; font-size: .95rem;
  color: var(--ink, var(--play)); margin: 6px 0 0;
}
.rd .rd-ch-inwhich {
  display: block; font-family: var(--serif); font-style: italic;
  font-size: .82rem; opacity: .85; margin-top: 1px;
}
.gx-stage .gx-cliff-q {
  font-family: var(--serif); font-style: italic; font-size: .95rem;
  color: var(--carbon); opacity: .9; margin: 4px 0;
}

/* 38 · THE TYPE CASE. Russian keys on screen for the writing questions —
   drawn as what it is in this house: a compositor's case of small sorts.
   Lives in both rooms (lesson #gx-pad, practice #rev-pad — one builder,
   Course.ruPad). An option: folded until the ⌨ toggle opens it. */
.gx-in-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.gx-pad { margin-top: 10px; }
.gx-pad-row { display: flex; gap: 4px; justify-content: center; margin-top: 4px; flex-wrap: nowrap; }
.gx-pad-k {
  font-family: var(--serif); font-size: 1.02rem; line-height: 1;
  min-width: 34px; padding: 8px 2px;
  flex: 0 1 44px;
  background: var(--sheet); color: var(--carbon);
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 2px 0 var(--press);
  cursor: pointer;
}
.gx-pad-k:active { transform: translateY(1px); box-shadow: 0 1px 0 var(--press); }
.gx-pad-k:disabled { opacity: .45; cursor: default; }
.gx-pad-bs { flex-basis: 64px; }
/* the period sorts sit apart — the short rail of the house's own letters */
.gx-pad-row:last-child .gx-pad-k { background: var(--paper); }
.gx-kbd, #rev-kbd {
  font-size: 1.05rem; line-height: 1; padding: 6px 10px;
  background: var(--sheet); border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 2px 0 var(--press); cursor: pointer; color: var(--carbon);
}
.gx-kbd.on, #rev-kbd.on { background: var(--reward-tint); border-color: var(--reward-deep); }
@media (max-width: 700px) {
  .gx-pad-row { gap: 3px; }
  .gx-pad-k { min-width: 26px; padding: 9px 1px; font-size: .95rem; }
}

/* 39 · THE COMPILER'S BEATS. The margin guesses (a pencilled word, large),
   the rest beat's read-along lines, and the scribe's study line. */
.gx-stage .gx-margin-word {
  font-family: var(--serif); font-size: clamp(30px, 5vw, 46px);
  line-height: 1.1; color: var(--carbon); text-align: center; margin: 6px 0 2px;
}
.gx-stage .gx-margin-n {
  text-align: center; font-family: var(--bill); font-weight: 700;
  font-size: .7rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--steel); margin-bottom: 8px;
}
.gx-stage .gx-rwl { display: grid; gap: 14px; }
.gx-stage .gx-rwl-line {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; align-items: start;
  font-family: var(--serif);
}
.gx-stage .gx-rwl-ru { font-size: clamp(19px, 2.6vw, 26px); line-height: 1.45; color: var(--carbon); }
.gx-stage .gx-rwl-en { grid-column: 2; font-style: italic; font-size: .95rem; opacity: .8; }
.gx-stage .gx-scribe-study, .gx-stage .gx-scribe-keep {
  font-size: clamp(20px, 2.8vw, 28px); line-height: 1.5; margin-bottom: 6px;
}
.gx-stage .gx-after2 { margin-top: 14px; }

/* 40 · THE BOOK THRESHOLD. The corridor page — one quiet breath between
   the last lesson and the wax — and the walk-with-you line under the
   curtain. Decelerate-heavy entrance; any tap passes through. */
.gx-stage .gx.gx-corridor {
  display: grid; justify-items: center; align-content: center;
  gap: 10px; min-height: 60vh; text-align: center;
  padding: 40px clamp(18px, 5vw, 56px); cursor: pointer;
}
.gx-stage .gx-corr-orn { width: 84px; height: 84px; opacity: .85; }
.gx-stage .gx-corr-orn svg { width: 100%; height: 100%; }
.gx-stage .gx-corr-ti { font-family: var(--serif); font-size: clamp(26px, 4vw, 40px); margin: 0; }
.gx-stage .gx-corr-line { font-family: var(--serif); font-size: 1.05rem; margin: 0; }
.gx-stage .gx-corr-epi { font-family: var(--serif); font-style: italic; opacity: .85; max-width: 34em; }
.gx-stage .gx-corridor .btnrow { margin-top: 18px; cursor: auto; }
@media (prefers-reduced-motion: no-preference) {
  body.motion-full .gx-stage .gx.gx-corridor { animation: gx-corr-in 1100ms cubic-bezier(.2, .7, .2, 1) both; }
  @keyframes gx-corr-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
}
.gx-stage .gx-cliff-walk {
  font-family: var(--serif); font-size: .98rem; margin: 4px 0;
  color: var(--ink, var(--play));
}

/* 41 · THE MAP'S FURNITURE. Shelf plates between the bands; the crossed
   mark takes its wax; the road's one live figure; the redacted first line. */
.rd .rd-shelfhead {
  position: relative; z-index: 2;   /* above the road's own drawn line */
  list-style: none; margin: 26px 0 2px; padding: 10px 16px;
  background: var(--play-deep); color: var(--on-play-deep);
  border: 2px solid var(--carbon); box-shadow: 0 4px 0 var(--press);
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
}
.rd .rd-shelfhead b {
  font-family: var(--bill); font-weight: 800; letter-spacing: .14em;
  text-transform: uppercase; font-size: .85rem;
}
.rd .rd-shelfhead span { font-family: var(--serif); font-style: italic; font-size: .88rem; opacity: .9; }
.rd .rd-mark.rd-m-crossed .rd-m-dot {
  background: var(--right); color: var(--on-right, #fff);
}
.rd .rd-m-won {
  display: block; font-family: var(--serif); font-style: italic;
  color: var(--right-deep, var(--right)); font-size: .9rem;
}
@media (prefers-reduced-motion: no-preference) {
  /* no overshoot — the seal keeps its monopoly; this is a press-and-settle */
  body.motion-full .rd .rd-m-crossing .rd-m-dot { animation: rd-m-press 1100ms cubic-bezier(.3, 0, .2, 1) both; }
  @keyframes rd-m-press {
    0% { transform: scale(1); }
    45% { transform: scale(1.28); }
    100% { transform: scale(1); }
  }
}
.rd .rd-book { position: relative; }
.rd .rd-roadfig {
  /* seated INSIDE the row — .rd-book clips its overflow, and a figure
     leaning over the top edge lost its head */
  position: absolute; right: clamp(8px, 3vw, 40px); top: 8px;
  width: 52px; height: 52px; pointer-events: auto; z-index: 2;
}
.rd .rd-roadfig svg { width: 100%; height: 100%; }
@media (max-width: 620px) { .rd .rd-roadfig { display: none; } }
.rd .rd-blotwrap { display: block; margin-top: 6px; font-size: .92rem; }
.rd .rd-blotline {
  filter: blur(5px); user-select: none;
  background: color-mix(in srgb, var(--carbon) 12%, transparent);
  border-radius: 3px; padding: 0 4px;
}
.rd .rd-blotnote { font-size: .8rem; opacity: .7; }

/* the crossed mark's caption keeps its ink in the dark */
[data-theme="dark"] .rd .rd-m-won { color: var(--right); }

/* 42 · THE SEVENTH EVENING CARD — the account fleet's one ask, dressed as
   the ticket plate it speaks for: bordered, quiet, no gold (this is
   information, not celebration). */
.gx-stage .gx-acc {
  margin-top: 14px; padding: 14px 16px;
  background: var(--sheet); border: 2px solid var(--carbon);
  box-shadow: 4px 4px 0 color-mix(in srgb, var(--press) 30%, transparent);
  border-radius: 2px;
}
.gx-stage .gx-acc-head {
  font-family: var(--bill); font-weight: 800; font-size: .72rem;
  letter-spacing: .14em; text-transform: uppercase; color: var(--steel);
  margin-bottom: 6px;
}
.gx-stage .gx-acc p { font-family: var(--serif); font-size: .95rem; margin: 0 0 10px; }
.gx-stage .gx-acc .btnrow { display: flex; gap: 8px; flex-wrap: wrap; position: static; margin: 0; padding: 0; border: 0; background: none; }

/* ============================================================================
   43 · THE CASCADE CONCORDAT — light, gold, calm (2026-08-22).
   Clauses b and d land here in the sanctioned tail so they win every tie
   below on purpose. Clause a (lights-down) lives in 30-choreo.css: its
   :root[data-lights="down"] { --room: … } rides specificity (0,2,0) over
   this sheet's :root token blocks (0,1,0), so it needs no tail mend — do
   not "fix" it back into an alias rewrite; --room is the live ground.
   Clause c (the gold/signal split) is minted in head.html. All five are
   asserted by tests/cascade.test.js.
   ============================================================================ */

/* The shell has one stable ground. Time of day and earned rank no longer
   add grey washes or graph paper behind navigation and study. The reader's
   explicit lamp mode retains its more specific treatment above. */
body {
  background: var(--room);
}

/* d · THE CALM TIER REACHES THE ARCADE. Six animations in this sheet were
   declared with raw durations, so the reader who chose calm (--motion: .5)
   got them at full speed — only "low" ever slowed the room. The originals
   are left standing; the durations alone are re-declared here through the
   house scalar. Calm's longest act is now the streak flame at 2.3s; full
   is arithmetically unchanged (× 1); low all but stills them (× .12). */
/* hud-flame · 4.6s */
body:not(.motion-low) #hud-streak .ic .ic-flame {
  animation-duration: calc(4.6s * var(--motion, 1));
}
@media (prefers-reduced-motion: no-preference) {
  /* gx-mend-glint · 1.2s */
  .gx-stage.gx-glint::before {
    animation-duration: calc(1.2s * var(--motion, 1));
  }
  /* gx-lamp-take · 4s */
  .gx-night-lit .gx-lampfig {
    animation-duration: calc(4s * var(--motion, 1));
  }
  /* rd-knife · .7s */
  .rd .rd-ch.rd-open .rd-ch-body {
    animation-duration: calc(.7s * var(--motion, 1));
  }
  /* gx-corr-in · 1100ms */
  body.motion-full .gx-stage .gx.gx-corridor {
    animation-duration: calc(1100ms * var(--motion, 1));
  }
  /* rd-m-press · 1100ms */
  body.motion-full .rd .rd-m-crossing .rd-m-dot {
    animation-duration: calc(1100ms * var(--motion, 1));
  }
}

/* ============================================================
   THE HOUSE DEVICE (Brand.mark) — the LtL roundel, struck small wherever a
   page closes or a room names itself. It rides currentColor so each surface
   inks it in its own ink; only the italic t is gold, the wordmark's hinge.
   ============================================================ */
.ltl-mark { display: inline-block; vertical-align: middle; color: var(--carbon); }
.rh-device { margin-bottom: 6px; }
.rh-device .ltl-mark { opacity: .85; }
.gx-imprint { flex-basis: 100%; text-align: center; margin-top: 10px; opacity: .55; }
.gx-imprint .ltl-mark { color: var(--steel, var(--carbon)); }
.rdoor-kicker .ltl-mark { width: 16px; height: 16px; margin-right: 6px; vertical-align: -3px; opacity: .8; }
[data-theme="dark"] .ltl-mark { color: var(--galley); }

/* ============================================================
   THE ROAD CARRIES ITS PIGMENTS. The arcade skin flattened the base sheet's
   pigmented volumes into one play-tint chip and one cream row — fourteen
   books reading as one row repeated. The pigment returns IN the skin's own
   language: the chunky chip takes a real mix of the book's ink, the row's
   paper takes a breath of it, and the finished volume fills with it. Var
   fallbacks keep the alphabet and reference rows exactly as they were.
   ============================================================ */
.rd .rd-book { background: color-mix(in srgb, var(--ink, var(--play)) 9%, var(--galley)); }
.rd .rd-book:hover { background: color-mix(in srgb, var(--ink, var(--play)) 13%, var(--galley)); }
/* :not(.rd-here) — the reward-gold chip marking WHERE YOU ARE outranks the
   book's own pigment; clobbering it lost the road's one right-now signal */
.rd .rd-book:not(.rd-here) .rd-vol { background: color-mix(in srgb, var(--ink, var(--play)) 44%, var(--play-tint)); }
.rd .rd-book.rd-done .rd-vol { background: color-mix(in srgb, var(--ink, var(--play)) 60%, var(--right)); }
.rd .rd-book .rd-next b { color: color-mix(in srgb, var(--ink, var(--play)) 72%, var(--carbon)); }
[data-theme="dark"] .rd .rd-book { background: color-mix(in srgb, var(--ink, var(--play)) 12%, var(--galley)); }
[data-theme="dark"] .rd .rd-book:hover { background: color-mix(in srgb, var(--ink, var(--play)) 17%, var(--galley)); }

/* ============================================================
   THE READING ROOM STANDS IN THE HOUSE (mends, 2026-08-22 night).
   The reading route painted its whole view --sheet from edge to edge, so at
   any width the page was a cream void with a strip of type in it: no room,
   no margins, no place for the resident to stand. The view goes transparent
   and the house's own grid paper shows in the margins around the lit sheet —
   the same ground every other room stands on. The sheet itself (#text) keeps
   its own paper, lattice and proscenium; the read-lamp mode keeps its
   measured surround (its rules sit above and carry the same specificity,
   but repaint body + #read-wrap, which this does not touch).
   ============================================================ */
#v-read { background: transparent; }
/* the word card wears the book's ink at its head. Declared HERE because the
   arcade's own `#pop { border: 3px solid var(--carbon) }` (calm reading
   plane, above) is a shorthand that resets border-top; head.html's and
   66-rooms' book-ink tops both lose to it. Last section of last sheet wins. */
#pop { border-top: 3px solid var(--book-ink, var(--carbon)); }
/* the resident's bubble on the grid: the house's own ink plate, so it reads
   as speech and not as a toast (.margin-mate is retired — .pop-say remains) */
.pop-say {
  background: var(--play, var(--ink-1)); color: var(--on-play, var(--paper-0));
  border: 1px solid var(--carbon);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--press) 55%, transparent);
}
/* the epigraph slip is the resident's own bill: figure, lines, one door */
.scene-epigraph { box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 18%, transparent); }
.scene-epigraph .ep-btn { border: 2px solid var(--carbon); box-shadow: 0 3px 0 var(--press);
  background: var(--signal); color: var(--carbon); font-weight: 700; }
.scene-epigraph .ep-btn:hover { background: var(--signal); color: var(--carbon);
  transform: translateY(-1px); box-shadow: 0 4px 0 var(--press); }
.scene-epigraph .ep-btn:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--press); }

/* ============================================================
   THE LESSON COMES ALIVE — the dopamine slate (2026-08-23).
   research/learn-alive-2026-08-23.md. Feedback graded by success
   quality (Kao CHI'24), the combo decoupled from score (Kahoot),
   variable reward native to the material (item 35), one flight
   (item 36). Every duration obeys --motion; nothing bounces;
   no green, no red-as-judgement, no numeral that is not material.
   ============================================================ */

/* ---------- A · THE SWIFT HAND ---------- */
/* the touched thing lands harder on a swift, clean, first-ask answer:
   a deeper press with a fast settle — decelerate, never bounce */
.gx-stage .gx-swifthit {
  animation: gx-swifthit calc(.18s * var(--motion, 1)) cubic-bezier(.2, .9, .3, 1) 1;
}
@keyframes gx-swifthit {
  0% { transform: translateY(0); }
  40% { transform: translateY(3px); }
  100% { transform: translateY(0); }
}
/* one brighter pass of ink: the sheet's shadow warms a shade for the beat
   of the pass (the glint family already rides [class*="gx-m-"]) */
.gx-stage.gx-m-swift .gx {
  box-shadow: 8px 8px 0 color-mix(in srgb, var(--reward) 55%, var(--play));
  transition: box-shadow .12s linear;
}
/* the hitstop: ninety milliseconds of held breath at a miss — every ambient
   animation pauses. The consequence lands by absence of motion, the one
   register the charter permits at friction. */
.gx-stage.gx-still, .gx-stage.gx-still * { animation-play-state: paused !important; }
/* the run chip climbs in ink weight — signal at six, filled at nine. The
   escalation is the display; no currency is attached to it, by law. */
/* The escalation lives in the border and the weight; the INK holds. Both
   tiers used to be shadowed by the base chip's !important, so nobody saw
   that --reward-deep is #ffe28a after dark — a pale gold laid on gold, worse
   than the bug the base chip was just cured of. The gold plate keeps its own
   dark ink at every tier. */
.gx-stage .gx-top .gx-runn[data-r="2"] {
  border-color: var(--reward-deep); color: var(--on-reward, var(--on-signal)); font-weight: 800;
}
.gx-stage .gx-top .gx-runn[data-r="3"] {
  background: var(--reward); color: var(--on-reward, var(--on-signal));
  border-color: var(--carbon); font-weight: 900;
}
/* the small stated fact on a swift answer — informational, lowercase, dry */
.gx-stage .gx-v-swift {
  display: inline-block; margin-left: 8px; font-style: normal;
  font-family: var(--sans); font-size: var(--t-00); font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: var(--reward-deep);
}

/* ---------- B · THE PRESSMAN'S PROOF (sparse novelty, pure dress) ----------
   THE PLATE IS PULLED IN THE HOUSE'S OWN INK NOW. It was #1d1713, a warm
   brown-black that is in no palette this product owns: the desk is paper and
   navy with gold for the earned moments, and against those a soot-coloured
   sheet reads as a third thing that wandered in — the owner reported it as
   «this odd colour» twice before it was traced. --carbon is the darkest ink
   the house already has (#17213a, a deep blue-black), so the plate still
   goes dark against a room of paper — which is the whole effect — without
   introducing a hue that appears nowhere else on the screen.

   The gold label and the paper-coloured type are untouched: gold on ink at a
   rare moment is exactly what the palette reserves gold for.

   NOTE for whoever comes next: `.fire-ov .fire-panel` still uses #1d1713 and
   is the only other place it survives. The two were a matched pair of
   ceremony surfaces. Left alone deliberately rather than swept along — it is
   a different ceremony, it was not what was reported, and it should be
   looked at by somebody who can see it render. */
.gx-stage .gx-mid[data-rare="proof"] {
  background: var(--carbon); border-radius: 2px; padding: 14px 16px 16px;
  box-shadow: 6px 6px 0 color-mix(in srgb, var(--reward) 40%, var(--carbon));
}
.gx-stage .gx-mid[data-rare="proof"] .gx-ask { color: color-mix(in srgb, #fffdf5 72%, var(--carbon)); }
.gx-stage .gx-mid[data-rare="proof"] .gx-ask::before {
  content: 'оттиск · the pressman\2019s proof';
  display: block; font-family: var(--sans); font-size: var(--t-00); font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--reward);
  margin-bottom: 6px;
}
.gx-stage .gx-mid[data-rare="proof"] .gx-head { color: #fffdf5; }
.gx-stage .gx-mid[data-rare="proof"] .gx-head .gx-say { color: inherit; }
/* AND THE HORN, WHICH IS THE THIRD TIME THIS PLATE HAS EATEN SOMETHING
   (2026-08-23, owner-photographed). The listening circle takes its ground
   from 26-games.css, which builds it out of --galley — paper — while the
   line above hands it paper-coloured ink. On every other stage that pairs
   correctly; on this plate it is the strips' bug again, in a circle: the
   «again» and its horn were white on white, and the one control the
   question depends on could not be seen at all.

   The plate cannot fix these one at a time and stay fixed — a component
   whose background is written in another file will always arrive here
   wearing paper. What holds is the rule the strips already found: on the
   proof, paper is a WHISPER over the plate and never the plate itself. */
.gx-stage .gx-mid[data-rare="proof"] .gx-head .gx-say {
  background: radial-gradient(circle at 50% 38%,
    color-mix(in srgb, #fffdf5 16%, transparent),
    color-mix(in srgb, #fffdf5 7%, transparent));
  border-color: color-mix(in srgb, #fffdf5 55%, transparent);
  box-shadow: none;
}
/* the ring it throws was drawn in --ink, which on the plate is night on
   night — it kept its rhythm and showed nothing */
body.motion-full .gx-stage .gx-mid[data-rare="proof"] .gx-head .gx-say::after {
  border-color: color-mix(in srgb, #fffdf5 45%, transparent);
}
/* THE PROOF PULLS ITS STRIPS DARK TOO (2026-08-23). The gap and form kinds
   set their sentence on a light galley strip; inside the dark plate the
   ink went paper-white while the strip kept its paper — white ink in a
   white box, and the owner met a sentence he could not see. On the proof,
   the strip is the plate itself: a whisper of paper behind paper-colored
   ink, the blank lit enough to be a blank. */
.gx-stage .gx-mid[data-rare="proof"] .gx-line,
.gx-stage .gx-mid[data-rare="proof"] .gx-study {
  background: color-mix(in srgb, #fffdf5 9%, transparent);
  border-left-color: color-mix(in srgb, #fffdf5 55%, transparent);
  color: #fffdf5;
  box-shadow: none;
}
.gx-stage .gx-mid[data-rare="proof"] .gx-blank {
  background: color-mix(in srgb, #fffdf5 18%, transparent);
  color: #fffdf5;
  box-shadow: none;
}
/* AND ITS HEADWORD (2026-08-23, owner-photographed). The specimen word
   wears the chapter's ink by law — but on the proof's dark plate that
   ink is night on night, and the meaning card's specimen rules sink with
   it. On the proof, the one saturated thing is paper-colored, like the
   rest of the impression. */
.gx-stage .gx-mid[data-rare="proof"] .gx-word,
.gx-stage .gx-mid[data-rare="proof"] .gx-gloss,
.gx-stage .gx-mid[data-rare="proof"] .gx-en {
  color: #fffdf5;
}
.gx-stage .gx-mid[data-rare="proof"] .gx-word::before,
.gx-stage .gx-mid[data-rare="proof"] .gx-word::after {
  border-color: color-mix(in srgb, #fffdf5 55%, transparent);
}

/* ---------- C · THE PATENT SLIP (the ladder, felt in the room) ---------- */
.gx-stage .gx-patent {
  display: flex; gap: 12px; align-items: center; margin: 14px 0;
  padding: 12px 14px; background: var(--galley);
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 58%, var(--rule));
}
.gx-stage .gx-pat-cls {
  flex: 0 0 auto; width: 46px; height: 46px; display: grid; place-items: center;
  border: 2.5px solid var(--carbon); border-radius: 50%;
  font-family: var(--bill); font-weight: 900; font-size: 1.05rem;
  background: var(--paper); box-shadow: inset 0 0 0 2px var(--reward);
}
.gx-stage .gx-pat-mid { flex: 1; min-width: 0; }
.gx-stage .gx-pat-mid b { display: block; font-family: var(--serif); font-size: .98rem; }
.gx-stage .gx-pat-rise {
  display: block; height: 5px; margin: 6px 0 4px; background: var(--paper);
  border: 1.5px solid var(--carbon); border-radius: 2px;
  position: relative; overflow: hidden;
}
.gx-stage .gx-pat-rise::after {
  content: ''; position: absolute; inset: 0; width: var(--p, 0%);
  background: var(--reward); border-right: 1.5px solid var(--carbon);
}
.gx-stage .gx-pat-mid span { display: block; font-size: .78rem; color: var(--steel); }

/* ---------- E · THE DOOR (the № chip becomes a person) ---------- */
#nav-profile { position: relative; }
#nav-profile .np-init {
  display: inline-grid; place-items: center; width: 18px; height: 18px;
  margin-right: 5px; border: 1.5px solid currentColor; border-radius: 50%;
  font-size: var(--t-00); font-weight: 900; vertical-align: -3px;
}
#nav-profile .np-init[hidden] { display: none; }
#nav-profile.np-rising::after {
  content: ''; position: absolute; left: 7px; right: 7px; bottom: 3px; height: 2.5px;
  border-radius: 1px;
  background: linear-gradient(to right,
    var(--reward) calc(var(--rise, 0) * 100%),
    color-mix(in srgb, currentColor 22%, transparent) 0);
}

/* ---------- D · THE TICKET IN FIVE ZONES (recomposed 2026-08-23) ----------
   The zone map: one dominant plate (Z-scanned, name → patent), the counting
   line touching it like a pass's value row, the keeping (diary ∥ seals),
   the record in museum-label lines, and the foot as the document's MRZ.
   Numbers live once; rank escalates by finish, never layout; gold stays on
   the patent ring and the wax alone. */
/* zone 1 — the plate */
.pxx-plate {
  position: relative;
  display: grid; grid-template-columns: 1.15fr .85fr; gap: 20px 30px;
  align-items: center;
  background: var(--play); color: var(--on-play, #fffdf5);
  border: 3px solid var(--carbon); border-radius: 2px;
  box-shadow: 8px 8px 0 var(--reward);
  padding: 30px 30px 26px;
  /* 62px = the companion's standing height above the rim (66 − 12) plus
     8px of daylight to the head's rule — the perch law's clear air */
  margin: 62px 0 0;
}
.pxx-plate input.px-name {
  color: var(--on-play, #fffdf5); margin: 0;
  font-size: clamp(38px, 5.6vw, 74px); line-height: .9;
}
.pxx-plate input.px-name:focus { box-shadow: inset 0 -4px 0 var(--reward); }
.pxx-plate input.px-name::placeholder { color: color-mix(in srgb, var(--on-play, #fffdf5) 40%, transparent); }
.pxx-plate .px-name-invite { margin: 0; }
.pxx-invite {
  background: none; border: 0; padding: 0 0 4px; cursor: pointer;
  color: inherit; font-family: var(--bill); font-weight: 900;
  font-size: clamp(26px, 4.2vw, 42px); text-transform: uppercase;
  border-bottom: 3px solid var(--reward); line-height: 1.08; text-align: left;
}
.pxx-meta {
  margin-top: 12px;
  font-family: var(--sans); font-size: var(--t-00); font-weight: 700;
  letter-spacing: .13em; text-transform: uppercase;
  color: color-mix(in srgb, var(--on-play, #fffdf5) 72%, transparent);
}
/* the patent: a paper document pinned to the navy — one geometry for all
   fourteen classes, the frame's material the only thing that climbs */
.pxx-patent {
  display: flex; gap: 14px; align-items: flex-start;
  background: var(--galley); color: var(--carbon);
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 4px 4px 0 color-mix(in srgb, #000 32%, var(--play));
  padding: 14px 16px 12px;
}
.pxx-pat-frame {
  flex: 0 0 auto; width: 56px; height: 56px; display: grid; place-items: center;
  border: 2.5px solid var(--carbon); border-radius: 50%; background: var(--paper);
  font-style: normal;
}
.pxx-pat-frame b { font-family: var(--bill); font-weight: 900; font-size: 1.45rem; }
.pxx-patent[data-band="mid"] .pxx-pat-frame { box-shadow: inset 0 0 0 3px var(--reward); }
.pxx-patent[data-band="high"] .pxx-pat-frame {
  box-shadow: inset 0 0 0 3px var(--reward), inset 0 0 0 5px var(--paper), inset 0 0 0 7px var(--reward);
}
.pxx-pat-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.pxx-pat-kicker {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--steel);
}
.pxx-pat-ru { font-family: var(--serif); font-size: 1.16rem; line-height: 1.2; font-weight: 600; }
.pxx-pat-en, .pxx-pat-note { font-family: var(--serif); font-style: italic; font-size: .82rem; color: var(--steel); line-height: 1.35; }
.pxx-pat-rise {
  display: block; height: 5px; margin: 7px 0 2px; background: var(--paper);
  border: 1.5px solid var(--carbon); border-radius: 2px; position: relative; overflow: hidden;
}
.pxx-pat-rise::after {
  content: ''; position: absolute; inset: 0; width: var(--p, 0%);
  background: var(--reward); border-right: 1.5px solid var(--carbon);
}
.pxx-pat-climb { font-family: var(--serif); font-style: italic; font-size: .78rem; color: var(--steel); }
/* the companion perches on the plate's top edge — present once, small.
   PERCH LAW: the figure hangs by its FEET (bottom-anchored, sunk 12px
   into the plate's rim), so where it stands never depends on its own
   height or the viewport; the plate's top margin reserves the clear air
   its body needs, so it can never cross the head's double rule. Never
   place it with top offsets — those drift with the gap above the plate. */
.pxx-fig { position: absolute; top: auto; bottom: calc(100% - 12px); right: 26px; width: 66px; height: 66px; cursor: pointer; }
.pxx-fig svg { width: 100%; height: 100%; display: block; overflow: visible; }
/* zone 2 — the counting line, hung from the plate */
.pxx-count {
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  background: var(--sheet);
  border: 3px solid var(--carbon); border-top: 0;
  border-radius: 0 0 2px 2px;
  margin: 0 14px;
  box-shadow: 0 5px 0 color-mix(in srgb, var(--press) 58%, var(--rule));
}
.pxx-c { padding: 15px 18px 13px; border-left: 1.5px solid color-mix(in srgb, var(--carbon) 22%, transparent); min-width: 0; }
.pxx-c:first-child { border-left: 0; }
.pxx-c b {
  display: block; font-family: var(--mono); font-weight: 700;
  font-size: clamp(26px, 3.2vw, 38px); line-height: .9;
  font-variant-numeric: tabular-nums; color: var(--carbon);
}
.pxx-c span {
  display: block; margin-top: 6px; font-family: var(--sans);
  font-size: var(--t-00); font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--steel);
  line-height: 1.3;
}
/* zone 3 — the keeping: diary beside the seal board */
.pxx-mid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 48px; align-items: start; }
.pxx-diary {
  background: var(--galley); border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 45%, var(--rule));
  padding: 14px 16px;
}
.pxx-diary:empty { display: none; }
/* the month page fills its card on the ticket — larger day squares than
   the desk's compact register, the calendar centred like a print month */
.pxx-diary .dy-grid i { width: 19px; height: 19px; }
.pxx-diary .dy-grid { margin-left: auto; margin-right: auto; }
.pxx-diary .smallcaps, .pxx-diary .dy-cap, .pxx-diary .dy-foot { text-align: center; }
.pxx-seals {
  background: var(--play-deep); color: var(--on-play, #fffdf5);
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 58%, var(--rule));
  padding: 14px 16px 16px;
}
.pxx-seals-cap {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; opacity: .82; margin-bottom: 10px;
}
.pxx-wax { display: flex; flex-wrap: wrap; align-items: center; }
.pxx-wax .seal {
  width: 56px; height: 56px; margin: 0 -10px 4px 0;
  transform: rotate(calc(sin(var(--i, 0)) * 9deg));
  filter: drop-shadow(0 3px 3px rgba(0, 0, 0, .35));
}
.pxx-wax .seal svg { width: 100%; height: 100%; display: block; }
.pxx-seals-none { font-family: var(--serif); font-style: italic; font-size: .85rem; opacity: .85; margin: 0; line-height: 1.5; }
.pxx-link {
  background: none; border: 0; padding: 0; cursor: pointer;
  font-family: var(--sans); font-size: .72rem; font-weight: 700;
  letter-spacing: .05em; color: inherit;
  text-decoration: underline; text-underline-offset: 3px;
  margin-top: 6px; display: inline-block;
  /* .72rem caps make a 14px-tall link; padded to a tap target, the margins
     restore the 10px seat above it and take the 4px back below */
  padding: 4px 0; margin-bottom: -4px;
}
/* zone 4 — the record: museum-label lines, dates as accession numbers */
.pxx-record {
  margin-top: 48px; background: var(--paper);
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 45%, var(--rule));
  padding: 16px 20px 6px;
}
.pxx-rec-cap {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 800;
  letter-spacing: .14em; text-transform: uppercase; color: var(--steel);
  margin-bottom: 4px;
}
.pxx-line {
  display: flex; gap: 18px; justify-content: space-between; align-items: baseline;
  padding: 10px 0; border-top: 1px solid color-mix(in srgb, var(--carbon) 20%, transparent);
}
.pxx-rec-cap + .pxx-line { border-top: 0; }
.pxx-line span { font-family: var(--serif); font-style: italic; font-size: .92rem; line-height: 1.5; min-width: 0; }
.pxx-line i { font-family: var(--mono); font-style: normal; font-size: .7rem; color: var(--steel); white-space: nowrap; }
.pxx-line-kept span { font-style: normal; }
/* zone 5 — the foot: colophon and every utility, the document's floor */
.pxx-foot { margin-top: 48px; border-top: 2px solid var(--carbon); padding-top: 16px; }
.pxx-if {
  margin: 0 0 14px; font-family: var(--serif); font-style: italic;
  font-size: 1.05rem; line-height: 1.5; color: var(--carbon);
}
.pxx-if.is-empty { color: var(--steel); }
.pxx-if .pxx-link { margin: 0 0 0 10px; }
.pxx-utils { display: flex; flex-wrap: wrap; gap: 10px 20px; align-items: center; }
.pxx-utils .px-verb { font-size: .85rem; }
.pxx-fine { margin: 12px 0 0; font-size: .72rem; color: var(--steel); }
.px-light { display: inline-flex; gap: 8px; align-items: center; }
.px-light .im-thlab { font-size: var(--t-00); letter-spacing: .1em; text-transform: uppercase; color: var(--steel); font-weight: 700; }
@media (max-width: 820px) {
  .pxx-plate { grid-template-columns: 1fr; padding: 22px 18px 18px; gap: 16px; }
  .pxx-fig { right: 14px; width: 56px; height: 56px; bottom: calc(100% - 10px); }
  .pxx-count { grid-auto-flow: initial; grid-template-columns: repeat(2, 1fr); margin: 0 8px; }
  .pxx-c { border-left: 0; border-top: 1.5px solid color-mix(in srgb, var(--carbon) 22%, transparent); }
  .pxx-c:nth-child(-n+2) { border-top: 0; }
  .pxx-c:nth-child(2n) { border-left: 1.5px solid color-mix(in srgb, var(--carbon) 22%, transparent); }
  .pxx-mid { grid-template-columns: 1fr; margin-top: 36px; }
  .pxx-record, .pxx-foot { margin-top: 36px; }
}

/* ---------- F · UNDER THE WAX (the honest variable reward) ---------- */
.gx-stage .gx-underwax {
  margin: 0 0 14px; padding: 12px 14px; background: var(--paper);
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 58%, var(--rule));
}
.gx-stage .gx-underwax .smallcaps {
  display: block; font-size: var(--t-00); letter-spacing: .14em;
  text-transform: uppercase; color: var(--gold-leaf, #b8860b); margin-bottom: 4px;
}
.gx-stage .gx-underwax b { font-family: var(--serif); font-size: 1.02rem; display: block; }
.gx-stage .gx-underwax p { margin: 4px 0 0; font-size: .85rem; line-height: 1.5; }

/* ---------- G · THE FIREBIRD (Tier 3 — the one flight) ---------- */
/* the ceremony plate commits to one look in every theme, like wax: a night
   plate, engraved paper strokes, the foil feather. Gold here is lawful —
   this is a milestone, the rarest ceremony the house owns. */
.fire-ov { z-index: 292; }
.fire-ov .fire-panel {
  position: relative; overflow: hidden; max-width: 480px; width: min(92vw, 480px);
  text-align: center; background: #1d1713; color: #fffdf5;
  border: 3px solid #1d1713; border-radius: 2px;
  box-shadow: 8px 8px 0 var(--gold-leaf, #b8860b);
  padding: 26px 26px 20px;
}
.fire-ov .fire-panel .fb-stage { position: relative; height: 170px; }
.fire-ov .fire-panel .fb-plate { display: grid; place-items: center; height: 100%; color: #fffdf5; }
.fire-ov .fire-panel .fb-plate .fb-feather,
.fire-ov .fire-panel .fb-plate .fb-feye {
  stroke: #e0ad25;
  filter: drop-shadow(0 0 6px rgba(224, 173, 37, .55));
}
.fire-ov .fire-panel:not(.still) .fb-plate {
  opacity: 0;
  animation: fb-plate-in calc(.9s * var(--motion, 1)) ease-out calc(2.4s * var(--motion, 1)) both;
}
.fire-ov .fire-panel.still .fb-plate { opacity: 1; animation: none; }
@keyframes fb-plate-in {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: none; }
}
.fire-ov .fire-panel .fb-flight {
  position: absolute; top: 34%; left: -80px; color: #fffdf5; pointer-events: none;
  animation: fb-fly calc(2.8s * var(--motion, 1)) cubic-bezier(.3, .1, .2, 1) both;
}
.fire-ov .fire-panel .fb-flight .fb-feather,
.fire-ov .fire-panel .fb-flight .fb-feye { stroke: #e0ad25; }
.fire-ov .fire-panel.still .fb-flight { display: none; }
@keyframes fb-fly {
  0% { transform: translate(0, 30px) rotate(-6deg); opacity: 0; }
  12% { opacity: 1; }
  55% { transform: translate(300px, -30px) rotate(4deg); opacity: 1; }
  100% { transform: translate(620px, -64px) rotate(8deg); opacity: 0; }
}
.fire-ov .fire-panel .fb-name { font-family: var(--serif); font-size: 1.45rem; margin: 10px 0 2px; }
.fire-ov .fire-panel .fb-fact { font-size: .88rem; opacity: .85; line-height: 1.5; }
.fire-ov .fire-panel .fb-row { margin-top: 18px; justify-content: center; }
.fire-ov .fire-panel .fb-row-wait { opacity: 0; pointer-events: none; transition: opacity .5s linear; }
.fire-ov .fire-panel.still .fb-row-wait { opacity: 1; pointer-events: auto; }
.fire-ov .fire-panel .btn { border-color: #fffdf5; color: #fffdf5; background: transparent; box-shadow: 0 4px 0 rgba(255, 253, 245, .35); }
.fire-ov .fire-panel .btn.primary { background: var(--gold-leaf, #b8860b); border-color: #1d1713; color: #1d1713; box-shadow: 0 4px 0 #e0ad25; }

/* ---------- H · THE HOUSE AGREES WITH ITSELF ---------- */
/* the verdicts leave the traffic-light (kill list: no green/red verdict
   palettes). Success is ink filling in, warmed by the reward tint; error is
   the displaced carmine, worn as a border, never as a shout. */
.gx-stage .gx-verdict.gx-ok {
  background: var(--reward-tint); border-color: var(--carbon); color: var(--carbon);
}
.gx-stage .gx-verdict.gx-no {
  background: var(--galley); border-color: #922943; color: var(--carbon);
}
.gx-stage .gx-verdict.gx-ok.gx-v-won { color: var(--play-deep); }
.gx-stage .gx-right {
  background: var(--play) !important; color: var(--on-play, #fffdf5) !important;
  box-shadow: 0 4px 0 var(--play-deep) !important;
}
/* the base sheet holds a (0,3,0) `color: var(--on-malachite) !important` for
   right options — written for the malachite ground it also paints. This skin
   repaints the ground to --play navy but was losing the color war to that
   rule, printing carbon on navy: ink-on-ink, the exact fault the base
   sheet's own comment forbids. Restated at the same weight, skin's ink. */
.gx-stage :is(.gx-opt, .gx-tile, .gx-pw, .gx-pg, .gx-find-w, .gx-vowel).gx-right {
  color: var(--on-play, #fffdf5) !important;
}
.gx-stage .gx-wrong {
  background: color-mix(in srgb, #922943 10%, var(--paper)) !important;
  border-color: #922943 !important;
  box-shadow: 0 4px 0 color-mix(in srgb, #922943 60%, var(--carbon)) !important;
}
.gx-stage .gx-p-done {
  background: var(--play-tint) !important; border-color: var(--play) !important;
  box-shadow: 0 2px 0 var(--play-deep) !important;
}
.gx-stage .gx-rail .gx-r-done { background: var(--play); box-shadow: inset 0 -2px 0 var(--play-deep); }
/* Both of these moved their plate onto --carbon and left the ink where it
   was — a pinned #fffdf5 in the rules above. In daylight that is cream on
   ink and reads; after dark --carbon IS the cream, and both went to 1.04:1:
   the only door out of a lesson, and the words the reader is deciding
   whether to take back, invisible at the exact moment the hand is on them.
   --sheet is the token that follows --carbon into every room, which is the
   pairing 26-games.css:78 already states for .gx-set. */
.gx-stage .gx-top .gx-quit:hover {
  background: var(--carbon) !important; border-color: var(--carbon) !important;
  color: var(--sheet) !important;
}
.gx-stage .gx-set:hover {
  background: var(--carbon);
  color: var(--sheet);
  box-shadow: 0 3px 0 color-mix(in srgb, var(--press) 60%, var(--rule));
}
.desk-host-progress i { background: var(--play); }
.rd-done .rd-vol { background: var(--play); }
/* the journey joins the arcade — the last room still wearing the old shop */
#v-journey .jp-doc, #v-journey .journey-sheet {
  background: var(--galley); border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 6px 6px 0 color-mix(in srgb, var(--play) 34%, var(--case));
}
#v-journey .jseal.got {
  border: 2px solid var(--carbon); border-radius: 2px;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--press) 58%, var(--rule));
}

/* ---------- § · THE SETTINGS SHEET, IN ACTS (2026-08-23) ---------- */
#settings-panel { max-width: 560px; width: min(94vw, 560px); }
#settings-panel .set-act {
  margin: 24px 0 2px; padding-bottom: 5px;
  font-family: var(--sans); font-size: var(--t-00); font-weight: 800;
  letter-spacing: .16em; text-transform: uppercase; color: var(--steel);
  border-bottom: 2px solid var(--carbon);
}
#settings-panel .k-plate + .set-act { margin-top: 16px; }
/* long-copy rows stack, so no option is ever clipped mid-word */
#settings-panel .set-row.set-col { flex-direction: column; align-items: stretch; gap: 8px; }
#settings-panel .set-row.set-col select { max-width: none; width: 100%; }
#settings-panel .set-btns { display: flex; gap: 8px; flex: 0 0 auto; }
#settings-panel .set-btns .btn { font-size: .8rem; padding: 5px 12px; }
#settings-panel .set-note {
  font-size: .75rem; color: var(--steel); line-height: 1.55; margin: 8px 0 2px;
}
/* THE DOOR OUT IS ALWAYS IN REACH, AND IT IS THE FOOT OF THE SHEET (26 Sep
   2026). It used to be a sticky row inside the scrolling panel — and a
   sticky offset is measured inside the scroller's padding, so the bar rode
   17–21px above the panel's bottom edge with the rows sliding past beneath
   it; and the panel's ruled inner frame (66-rooms, an absolute ::before)
   scrolled with the rows, so its bottom rule struck through whichever row it
   met («Quiet chime»). Now the sheet is a column that does not scroll: the
   rows scroll in .set-body, and the frame and the door stay where they are
   drawn. */
#ov-settings #settings-panel {
  display: flex; flex-direction: column; overflow: hidden;
}
#settings-panel .set-body {
  flex: 1 1 auto; min-height: 0;
  overflow: hidden auto; overscroll-behavior: contain;
  /* the clip stands 11px outside the rows: a checkbox's 44px hit box hangs
     11px past its painted square (10-components), and a focus ring on a
     full-width control needs the same room — inside a tighter clip both were
     cut, and the sheet could be nudged sideways by 5px */
  margin-inline: -11px; padding-inline: 11px;
}
#settings-panel .set-save {
  flex: none; margin: 12px 0 0; padding: 12px 0 0;
  border-top: 1px solid var(--rule);
  justify-content: flex-end;
}

/* ---------- the passage between rooms (moved out of Settings) ---------- */
.masthead .room-door { margin-top: 10px; }
.masthead .room-door a {
  font-family: var(--sans); font-size: var(--t-00); font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase;
  color: var(--on-play, #fffdf5); opacity: .75; text-decoration: none;
  border-bottom: 1.5px solid color-mix(in srgb, currentColor 45%, transparent);
  padding-bottom: 1px;
}
.masthead .room-door a:hover { opacity: 1; border-bottom-color: var(--reward); }
.px-foot a.px-verb { color: inherit; }

/* ---------- the gathering learns to give a line back (2026-08-23) ---------- */
/* a placed tile stays live and tinted — tapping it again takes it back */
.gx-stage .gx-line-tile.gx-tile-picked {
  background: var(--play-tint);
  border-color: color-mix(in srgb, var(--carbon) 55%, transparent);
  color: color-mix(in srgb, var(--carbon) 62%, transparent);
  box-shadow: 0 1px 0 color-mix(in srgb, var(--carbon) 40%, transparent);
  transform: translateY(2px);
}
/* the sequence rows are buttons now: hover shows the strike-through offer */
.gx-stage .gx-gathered {
  display: block; width: 100%; text-align: left; cursor: pointer;
  font: inherit; color: inherit; background: none; border: 0;
  border-bottom: 1px solid color-mix(in srgb, var(--carbon) 18%, transparent);
  padding: 10px 12px;
}
.gx-stage .gx-gathered:hover { text-decoration: line-through; background: color-mix(in srgb, var(--carbon) 4%, transparent); }
.gx-stage .gx-gathered:disabled { cursor: default; text-decoration: none; background: none; }

/* ---------- I · THE ROOM AFTER DARK (2026-08-23) ----------
   The night practice card was cream on cream: the skin's surfaces were
   literals (#fffdf5 cards, #ffffff panels) while the ink on them rode
   tokens that lift at night. The literals are tokens now (sheet for cards,
   galley for inner panels — the same hexes by day), and every hard press
   shadow rides --press, which stays ink after dark. Two garnishes remain. */

/* the verdict row is sticky with a paper wash so the card reads through
   it; EMPTY, that wash printed as a bare navy slab under the question card
   (the skin's --paper sits a step above the night room — by day the two
   matched and the empty row passed unseen) */
.rev-btns:empty { display: none; }

/* the session ring's track strokes --case, which sits BELOW the room at
   night — the empty ring read as a hole, not a track. Galley keeps it the
   same quiet one-step lift the light room draws. */
[data-theme="dark"] .session-ring .track { stroke: var(--galley); }

/* ---------- THE TICKET'S EDGES, TRUED (2026-08-23, owner-photographed) ----
   The count row ran narrower than the plate above it, so the plate's hard
   yellow shadow peeked out in odd notches at both corners; plate and count
   are ONE object now — the row takes the plate's full width and the shadow
   moves down to close the whole card at its true bottom edge. The robin
   takes a real perch: feet on the plate's rim, body in the clear air above
   it, right of the subtitle. The seals drawer stands as tall as the diary
   beside it — two drawers of one desk, cut to the same depth. */
#v-profile .pxx-plate { box-shadow: none; }
#v-profile .pxx-count {
  width: 100%; margin-left: 0; margin-right: 0; box-sizing: border-box;
  box-shadow: 8px 8px 0 var(--reward);
}
#v-profile .pxx-mid { align-items: stretch; }
#v-profile .pxx-seals {
  height: auto; box-sizing: border-box;
  display: flex; flex-direction: column;
}
#v-profile .pxx-seals .pxx-link { margin-top: auto; }


/* ===== 95-coherence.css ===== */
/* ============================================================================
   95-coherence.css — THE DESK AT EVERY WIDTH, AND THE FRIEND IN IT.

   This sheet settles ties against everything BEFORE it — the base and the
   arcade — but it does not sort last and never did: 96-scale, 97-lessons and
   98-place follow it by declared order (build.py CSS_ORDER, the cascade
   contract) and win where they speak. It adds no new look: the arcade skin,
   the palette law and the motion charter are untouched. What it does is
   make the room the same room at 360px and at 1920px — and it was measured
   before it was written.

   THE FOUR THEMING AXES, RECONCILED IN WRITING (the reconciliation note the
   architecture audit asked for): [data-theme] (paper/dark/sepia, attribute
   only, never media-query) owns the palette tokens; :root[data-lights]
   (choreo.js, view-scoped) may only dim/lift EXISTING tokens; body
   [data-daypart] (renderHome) may only tint atmosphere tokens; the arcade
   sheet declares its own token layer at 90. Axes write TOKENS, never raw
   colors on components — cascade.test.js holds the daypart half of that
   rule; the same discipline binds lights.

   WHAT WAS MEASURED (2026-08-24, headless Chrome, a seeded six-week reader):

     · THIRTY-ONE type sizes on one screen. head.html declares nine, on a 4px
       baseline, and says in its own comment that anything off the list is a
       bug. The desk rendered 9.28, 10, 10.56, 10.88, 11.52, 12.16, 12.8,
       13.12, 13.76, 13.8, 14.72, 15.2, 18.4, 20.8, 22.08, 23.2, 26 … all of
       them em-relative sizes compounding inside nested rules. Twenty-two of
       them printed BELOW 11px, including every HUD label — «IN A ROW AT THE
       DESK», «COLLEGIATE REGISTRAR» — at 10.88px, and «THE COMPANY» at 9.28.
     · THIRTY-ONE distinct breakpoints across the sheets (380, 400, 420, 430,
       440, 460, 480, 500, 520, 560, 580, 600, 620, 640, 700, 720, 760, 820,
       860, 900, 940, 980, 1000, 1100 …). No two components folded together,
       so every intermediate width was a half-folded page.
     · THE TABLET HOLE, 581–860px. `.desk-intro` folds to one column at 860,
       but the masthead economies were written at 580 — so at 768 the masthead
       stood 705px tall, TALLER than the 550px it takes at 1440. The first
       screen of a tablet was a poster with no offer on it.
     · SIX VERTICAL GAPS, no ladder: −3, 14, 16, 19, 26, 27, 29, 30, 31, 40,
       41, 52 — and the 52 was identical at 390px and at 1920px.
     · THE ORPHAN. Every band on the desk holds the measure. `#tonight-card`
       alone stood centred at 640px inside a 1240px page, with 300px of empty
       gutter either side — the one card that carries tonight's action, laid
       out as though it were a footnote.
     · THE BILL sat at x86 while its own section sat at x100: fourteen pixels
       off the shared left edge, on the widest band of the page.
     · NINE TAP TARGETS under 24px — «Your ticket →» at 88×17, «set one» at
       48×17, «Back up» 51×17. WCAG 2.2 SC 2.5.8 (AA) wants 24×24; Apple and
       Material want 44. These are sixteen and seventeen pixels tall.
     · THE RANKS FIRST. On anything under 1024px the record sheet stacks
       spine → main → margin, so the reader scrolls 652px of the Table of
       Ranks — a list of fourteen titles, thirteen of them other people's —
       before reaching their own record. The least personal thing on the desk
       was the first thing on it.

   THE PRINCIPLE IT IS BUILT ON. Don Norman, «Emotional Design» (2004): a
   design is met at three levels at once. VISCERAL is the first half-second,
   before thought — so the first screen must be the room at its best AND must
   contain the offer, at every width. BEHAVIOURAL is the felt competence of
   use — so one measure, one rhythm, targets a thumb can hit, nothing that
   moves under the hand. REFLECTIVE is what the reader tells themselves
   afterwards about who they are for having used it — which on this desk is
   the record, the ticket and the rank, and which is why they stop being the
   thing you scroll past to get somewhere.

   And the finding underneath all three — «attractive things work better»:
   positive affect broadens thinking and widens tolerance for difficulty,
   negative affect narrows it. A reader construing a hard Russian sentence is
   already spending their narrowing. The room's job is to hand back the width:
   calm, ordered, unhurried, never clever at the reader's expense.
   ========================================================================== */


/* ============================================================================
   §0 · THE LADDER. Two ratios and four folds, named once.

   Fluid values are clamped, never raw vw: an unclamped vw font fails WCAG
   1.4.4 under zoom, and a clamp scale whose maximum exceeds ~2.5× its minimum
   fails it too. Every step below is inside that band.
   ========================================================================== */
:root {
  /* the gutter is a real margin now, not the remainder of 94vw. At 360 it is
     16px of air; at 1440 it is 40; past 1280 the measure simply centres. */
  --gutter: clamp(16px, 4vw, 40px);

  /* THE BAND — the gap between two acts of the desk. One value, and it grows
     with the page instead of holding 52px from a phone to a cinema display. */
  --band:       clamp(30px, 4.4vw, 60px);
  /* THE BREATH — inside an act: a section head to its sheet, a card to the
     next line of the same thought. */
  --breath:     clamp(16px, 2.2vw, 26px);
  /* THE SEAM — parts of one object that must not look separated. */
  --seam:       clamp(10px, 1.4vw, 16px);

  /* the four folds. Everything on the desk turns on one of these and on
     nothing else, so no width is ever half-folded. */
  --fold-hand:  560px;   /* one thumb, one column */
  --fold-page:  820px;   /* the spread closes: two columns become one */
  --fold-desk: 1024px;   /* the room opens: margins and spines return */
}

/* ONE MEASURE, and the gutter is explicit. --desk-w was min(94vw, 1280px),
   which gave a 390px phone eleven pixels of air and a 1920px display three
   hundred and twenty. The page keeps its 1280 ceiling and gains a floor. */
#v-home, #v-library {
  --desk-w: min(calc(100vw - var(--gutter) * 2), 1280px);
}


/* ============================================================================
   §1 · ONE LEFT EDGE.

   Seven bands hang on --desk-w already. The two that did not are corrected
   here rather than in their own sheets, because the correction is the same
   correction and belongs in one place where the next hand will find it.
   ========================================================================== */

/* the bill's card was laid out by a rule that centred it inside a padded
   parent, which put its left edge fourteen pixels outside the shared one.
   It is a section like the others; it stands where the others stand. */
#v-home > #bill-sect > .frontier-card,
#v-home > .desk-sect > .card {
  max-width: 100%;
  margin-inline: 0;
}

/* an act with nothing in it must not print its walls. Containers were
   painting border, shadow and forty pixels of margin around content that had
   not arrived (or never would, for a reader whose road is finished) — the
   desk showed three empty framed boxes and a 1,400px void between them. */
#v-home > .desk-sect:not(:has(*)),
#v-home > .desk-spread:not(:has(*)),
#v-home > .desk-sect > .card:empty,
#v-home > .desk-spread > .card:empty,
#v-home > .friend-strip:not(:has(.fr-bubble:not(:empty))) {
  display: none;
}


/* ============================================================================
   §2 · ONE RHYTHM.

   Twelve ad-hoc gaps become two: --band between acts, --breath inside one.
   The single exception is the masthead-to-HUD seam, which is −3px on purpose
   — the plate and its ledger are ONE object, sharing a 3px border, and that
   fusion is a brand signature. It was silently lost on phones (it read +14
   there); it is restored at every width.
   ========================================================================== */

#v-home > .friend-strip,
#v-home > #standing-line,
#v-home > .desk-spread,
#v-home > .desk-sect {
  margin-top: var(--band);
  margin-bottom: 0;
}

/* the HUD is welded to the masthead above it, at every width */
/* THE PLATE PAYS FOR ITS OWN CLEARANCE. The band used to be spent by the
   NEXT SIBLING (`#desk-hud + *`), and that sibling is `.friend-strip`, which
   is display:none on most evenings — a hidden element renders no margin, so
   the band vanished and the aim line landed at gap 0, its first line printed
   ON the plate's 8px gold shadow. Owned here instead, it cannot be swallowed
   by a dark neighbour: margins between block siblings collapse, so whatever
   turns out to be the first VISIBLE thing under the ledger stands a band
   below it, and one that already asks for a band still gets exactly one. */
#v-home > #desk-hud {
  margin-top: -3px;
  margin-bottom: var(--band);
}
/* the reader's own sentence carries a breath wherever it lands: a band under
   the plate (collapsed with the rule above), a breath under the companion's
   bubble when the strip is lit — the two are one act and stay close */
#v-home > .aim-line { margin-top: var(--breath); }

/* Optional question cards are inserted after the friend strip, whose bottom
   margin is zero. Give the card its own clearance so the bubble's shadow and
   entrance motion cannot overlap it, including when the greeting wraps. */
#v-home > .ask-card { margin-top: var(--breath); }

/* the masthead's own approach from the rail */
#v-home > .masthead { margin-top: var(--breath); margin-bottom: 0; }

/* section heads sit a breath above their sheet, never fifty-two pixels */
#v-home .sect-head { margin: 0 0 var(--breath); }

/* the greeting node is retired but still in the DOM for the overture's
   selector; it must not spend rhythm it does not use */
#v-home > .greeting:empty { margin: 0; }


/* ============================================================================
   §3 · THE FOLD LADDER.

   Three components folded at three different widths — the spread at 980, the
   bill's grid at 900, the ladder at 700 — so between 700 and 980 the page was
   always partly folded and partly not. They fold together now, at --fold-page.
   The original rules are left standing; these restate them on the ladder.
   ========================================================================== */

@media (max-width: 820px) {
  .desk-spread { grid-template-columns: 1fr; }
  .frontier-card .fr-grid { grid-template-columns: 1fr; }
  .lad-fold { grid-template-columns: 1fr; }
}
@media (min-width: 821px) {
  /* and above the fold line they are two columns TOGETHER — the 900 and 980
     rules used to leave the spread stacked while the bill had already opened */
  .desk-spread { grid-template-columns: 1.12fr .88fr; }
  .frontier-card .fr-grid { grid-template-columns: 1fr 1fr; }
}


/* ============================================================================
   §4 · THE TABLET HOLE, 581–820px.

   The masthead's phone economies were written at 580 and the intro's fold at
   860, so every width between them stacked a full-size masthead: 705px at
   768, against 550 at 1440. A first screen that is 84% title is a poster, and
   a poster has nothing for a returning reader to do.

   The economies are re-cut on the ladder, and the title is given a ceiling in
   VIEWPORT HEIGHT as well as width — the constraint that was missing. A short
   landscape phone and a tall portrait tablet are different rooms.
   ========================================================================== */

@media (max-width: 820px) {
  #v-home > .masthead { padding: clamp(16px, 3vw, 26px) clamp(14px, 3vw, 28px); }
  #v-home .masthead h1,
  #v-home #room-name {
    font-size: min(clamp(34px, 6.4vw, 52px), 15vh);
    line-height: .94;
    margin-block: clamp(6px, 1.4vw, 12px) clamp(6px, 1vw, 10px);
  }
  #v-home .masthead .room-line { font-size: var(--t-0); }
  #v-home .masthead .almanac { margin-top: var(--seam); }
  .desk-host { min-height: 0; }
}

/* THE INTRO STAYS A SPREAD LONGER THAN IT DID. Stacking the lockup above the
   host is what made 768px taller than 1440px, so the stack is deferred to the
   hand fold and the CAST PORTRAIT stands down first instead — it is the one
   element in the plate that carries no words, and giving up its 136–160px
   buys the two columns another 260px of life. Below 640 the plate stacks, and
   by then it is a phone and a stack is right. */
@media (min-width: 641px) and (max-width: 820px) {
  .desk-intro {
    grid-template-columns: minmax(0, .82fr) minmax(280px, 1.18fr);
    gap: var(--breath);
    min-height: 0;
  }
}
/* the portrait stands down across the WHOLE folded range, not only where the
   spread survives. 90-chromatic drops it at 580 and this sheet was dropping it
   from 641, which left 581–640px — a large phone in landscape, a small tablet
   in portrait — carrying a stacked plate AND the cast: 667px of masthead at
   600px wide, against 597 at 560. */
@media (max-width: 820px) {
  .desk-cast-wrap { display: none; }
  .desk-host { grid-template-columns: 1fr; gap: var(--seam); }
  .desk-host-bubble::after { display: none; }
}
@media (max-width: 640px) {
  .desk-intro { grid-template-columns: 1fr; gap: var(--breath); min-height: 0; }
}

/* ============================================================================
   §5 · TONIGHT HOLDS THE MEASURE.

   The one card that carries the evening's action was centred at 640px inside
   a 1240px page — the only centred object on a desk with one left edge, and
   it read as an orphan sitting in a hole. It becomes a band like the others,
   and uses the width it is given: the count and its sentence on the left, the
   door and the lamp on the right.

   This is the behavioural level doing its job. The primary action does not
   get smaller for being aligned; it gets a whole band, at the width the eye
   is already tracking down.
   ========================================================================== */

#v-home > .desk-spread { grid-template-columns: 1fr; justify-content: stretch; }
#v-home > .desk-spread > #tonight-card { width: 100%; max-width: none; margin-bottom: 0; }

/* the renderer emits a bare div with an empty class between head and count —
   it spends a row of the grid and a line of leading on nothing */
#v-home > .desk-spread > #tonight-card > div:not([class]):empty { display: none; }

@media (min-width: 821px) {
  /* the card's own body opens into two columns. `.bill-head` (TONIGHT ·
     СЕГОДНЯ … 24 АВГУСТА) is the card's running head and spans both. */
  #tonight-card {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(240px, .65fr);
    column-gap: clamp(24px, 3vw, 44px);
    row-gap: var(--seam);
    align-items: center;
  }
  #tonight-card > .bill-head { grid-column: 1 / -1; }
  /* the count and its sentence hold the left column */
  #tonight-card > .tn-row { grid-column: 1; margin: 0; }
  /* the door and the lamp travel to the right column, in the order they are
     read: press first, then the quiet line under it */
  #tonight-card > .btnrow.tn-act { grid-column: 2; grid-row: 2; margin: 0; }
  #tonight-card > .tn-lamp { grid-column: 2; grid-row: 3; margin: 0; align-self: start; }
  #tonight-card > .btnrow.tn-act > .btn { width: 100%; justify-content: center; }
  /* an empty sitting says so across the whole card, not in a column */
  #tonight-card > .tn-empty,
  #tonight-card > .tn-evening { grid-column: 1 / -1; }
}


/* ============================================================================
   §6 · THE RECORD IS THE READER'S, SO IT COMES FIRST.

   Reflective level, literally: the record sheet is where the reader learns
   what they have become — 431 words, thirty-one evenings, Collegiate
   Registrar. Under 1024px it stacked spine-first, so that self-portrait sat
   652 pixels below a table of fourteen ranks, thirteen of which belong to
   somebody else.

   The reader's own leaf leads; the ladder follows it as context; and on a
   phone the ladder shows the rung they stand on and the two it sits between,
   which is the whole of what a ladder has to say at that size.
   ========================================================================== */

@media (max-width: 820px) {
  .record-sheet { display: flex; flex-direction: column; }
  .record-sheet .rs-main   { order: 1; padding-inline: clamp(16px, 3vw, 28px); }
  .record-sheet .rs-margin { order: 2; border-left: none; border-top: 1px solid var(--rule); }
  .record-sheet .rs-spine  { order: 3; border-right: none; border-top: 1px solid var(--rule); }
}

@media (max-width: 700px) {
  /* the rung you stand on, and its two neighbours. :has() is the only way to
     reach the sibling BEFORE the current one, and it is baseline. */
  .rs-spine .rank-ladder li { display: none; }
  .rs-spine .rank-ladder li.rk-cur,
  .rs-spine .rank-ladder li:has(+ .rk-cur),
  .rs-spine .rank-ladder li.rk-cur + li { display: list-item; }
  .rs-spine .rk-foot { margin-top: var(--seam); }
}


/* ============================================================================
   §7 · THE TYPE FLOOR.

   head.html's nine sizes start at 11px. Twenty-two elements on the desk
   printed under it, most of them em-relative sizes compounding two and three
   rules deep — and they were not decoration: they were the HUD's four labels,
   which are the only words naming what the four numbers mean.

   A floor is set rather than each site hunted, because the compounding is the
   bug and a floor cannot be compounded through.

   A floor is set on the sites that were measured under it, and it is set to
   the SCALE STEP (--t-00, 11px) rather than to max(11px, 1em): an inherited
   1em at these depths is 15px, so max() would have inflated a small-caps
   label into body copy and flattened the very hierarchy the floor is meant to
   protect. Nothing legal moves; the illegal sizes step up to the nearest
   legal one, which is the one they were reaching for.
   ========================================================================== */

#v-home .cameo-lab,
#v-home .desk-host-kicker,
#v-home .arena-ghost .ag-shy,
#v-home .rs-spine .rk-cls,
#v-home .rs-spine .rk-en,
#v-home .rk-foot,
#v-home #room-door a {
  font-size: var(--t-00);
}
/* the HUD labels carry the meaning of the four numbers above them — they were
   the largest group under the floor, at 10.88px */
#v-home .hud-body small { font-size: var(--t-00); letter-spacing: .1em; }


/* ============================================================================
   §8 · A TARGET A THUMB CAN FIND.

   Nine targets on the desk were under 24×24; several were 17px tall. WCAG 2.2
   SC 2.5.8 sets 24×24 as the AA floor, SC 2.5.5 sets 44×44 as AAA, and Apple
   and Material both ask for 44/48.

   The hit area is grown, NOT the ink: an inset pseudo-element widens what the
   finger can land on while the typography stays exactly where the compositor
   set it. Nothing on screen moves by a pixel.
   ========================================================================== */

@media all {
  #v-home .linklike,
  #v-home .rs-foot a,
  #v-home .rs-main a,
  #v-home .rs-margin a,
  #v-home #room-door a,
  #v-home .stl a,
  #v-home .tn-lamp a {
    position: relative;
    touch-action: manipulation;
  }
  #v-home .linklike::after,
  #v-home .rs-foot a::after,
  #v-home .rs-main a::after,
  #v-home .rs-margin a::after,
  #v-home #room-door a::after,
  #v-home .stl a::after,
  #v-home .tn-lamp a::after {
    content: '';
    position: absolute;
    inset: -14px -10px;
    /* the ink keeps its own ::after in several of these; this one must never
       paint, only catch */
    background: none;
    border: 0;
  }
  /* neighbouring links must not overlap each other's grown areas */
  #v-home .rs-foot { display: flex; flex-wrap: wrap; gap: 6px clamp(16px, 3vw, 28px); }
}

/* the pointer-fine room gets the same courtesy in a smaller dose */
#v-home .btn,
#v-home button:not(.linklike) { touch-action: manipulation; }


/* ============================================================================
   §9 · THE ROOM AS A FRIEND.

   Norman's visceral level is not decoration — it is the half-second before
   thought, and what it decides is whether the reader is somewhere careful.
   Three things, and none of them animate anything new: the charter allows
   paper, ink, wax, flame and thread, and asks that every motion narrate a
   fact about the READER. Nothing here narrates the app.
   ========================================================================== */

/* a · THE ROOM ANSWERS THE KEYBOARD. A visible focus ring existed only where
   a component had thought to draw one, so tabbing through the desk went dark
   for stretches. One ring, in the room's own signal, everywhere. */
#v-home :focus-visible,
#v-library :focus-visible {
  outline: 3px solid var(--signal);
  outline-offset: 2px;
  border-radius: 1px;
}
/* on the navy plate the signal ring needs the carbon backing to separate */
#v-home > .masthead :focus-visible,
#v-home > #desk-hud :focus-visible {
  outline-color: var(--signal);
  box-shadow: 0 0 0 5px color-mix(in srgb, var(--carbon) 55%, transparent);
}

/* b · THE PRESSABLE THING ADMITS IT IS PRESSABLE. The arcade's press shadow
   is the house verb for "this can be pushed"; the desk's own doors were
   inheriting a hover that only changed a border colour. Ink settling, not a
   bounce — and it rides --motion like everything else. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  #v-home .btn,
  #v-home .desk-host-btn {
    transition:
      transform  calc(140ms * var(--motion, 1)) var(--settle),
      box-shadow calc(140ms * var(--motion, 1)) var(--settle);
  }
  #v-home .btn:hover,
  #v-home .desk-host-btn:hover { transform: translateY(-1px); }
  #v-home .btn:active,
  #v-home .desk-host-btn:active { transform: translateY(1px); }
}

/* c · THE COMPANION KEEPS ITS SEAT. The friend strip is the one place on the
   desk where somebody speaks to the reader in a voice, and it was laid out to
   collapse its portrait to a 48px box that sometimes rendered empty — a
   speaker with no face, above a line with no speaker. The seat holds, and the
   bubble keeps a readable measure inside a container wider than itself. */
#v-home > .friend-strip {
  align-items: start;
  gap: var(--seam);
}
#v-home > .friend-strip .fr-who { flex: 0 0 48px; align-self: center; }
#v-home > .friend-strip .fr-bubble { max-width: 68ch; }
@media (max-width: 560px) {
  /* on a phone the portrait sits above the line rather than stealing 62px of
     a 358px measure from it */
  #v-home > .friend-strip { flex-direction: column; align-items: stretch; }
  #v-home > .friend-strip .fr-who { align-self: flex-start; }
  #v-home > .friend-strip .fr-bubble::before,
  #v-home > .friend-strip .fr-bubble::after { display: none; }
}


/* ============================================================================
   §10 · THE WIDE ROOM.

   Past 1280 the measure stops growing, which is correct — a line of Spectral
   at 1600px is not a line anyone reads twice. What was wrong is that the page
   then sat in a 640px pool of empty ground with no acknowledgement that the
   room had got bigger. The bands keep their measure; the AIR around them is
   allowed to grow, so a large display reads as a large room rather than a
   small page with a lot of nothing at the sides.
   ========================================================================== */

@media (min-width: 1400px) {
  #v-home { --band: clamp(44px, 3.6vw, 68px); }
}
/* The desk now ends on a closed fold («Your collection & progress»); two
   bands of view padding under it plus the estate footer's own top margin
   left ~290px of empty paper before the house links — a hole, not a breath
   (measured 9 Sep 2026 at 1680, 1280 and 375). One band closes the desk. */
#v-home.view { padding-bottom: var(--band); }


/* ============================================================================
   §11 · THE PIGMENTS LIFT AT NIGHT.

   FOUND WHILE MEASURING THIS OVERHAUL, and older than it: the six book inks
   are masstones ground for a light paper ground — lazur #1d3f7a, zelen
   #2f5d43, bakan #7a2740, sepia #6b4630, grafit #3a3f45, okhra #9a6b1f — and
   they are carried from `data/course.json` to the element as an INLINE custom
   property (`style="--ink:#1d3f7a"`). An inline property cannot be re-declared
   by a stylesheet, so the pigments are the ONLY colours in the product that do
   not move between the day room and the night one.

   Measured on the desk at 1440px: every book title in the bill computed to the
   identical rgb in both themes. On the midnight ground (#121d38) that puts
   grafit at roughly 1.2:1 and lazur at roughly 1.3:1 — the reader's whole
   shelf, printed in ink the colour of the paper it sits on.

   This is the polarity-split bug class the arcade skin has been bitten by
   twice before, arriving from the other side: last time the SURFACES were
   literals and the ink rode tokens; here the ink is the literal.

   THE FIX KEEPS THE PIGMENT AND OBEYS THE STATED LAW — «under the lamp the
   pigments sit down, toward the stone they were ground from», and at night
   they lift «the way the leaf does — toward their highlight». The masstone is
   not replaced; it is mixed toward the room's warm off-white, in oklab so the
   hue survives the lift. Madder still reads as madder, verdigris as verdigris.

   45% is the measured ratio, not a guess. In `color-mix` the percentage is the
   share of the FIRST colour, so a HIGHER number keeps MORE masstone and reads
   darker — 70% was tried first and made things worse (lazur fell to 3.01:1).
   At 45% the eight titles on the desk's bill measure 5.36 – 7.31:1 on the
   night ground, all clear of the 4.5:1 body floor, where they had been sitting
   at roughly 1.2 – 1.3:1.

   Nothing moves in the light room: the theme is attribute-driven (the app
   always writes documentElement.dataset.theme and no sheet carries a
   prefers-color-scheme block), so this rule can only ever apply after dark.
   ========================================================================== */

[data-theme="dark"] .bill li,
[data-theme="dark"] .bill li .b-n,
[data-theme="dark"] .rm-node,
[data-theme="dark"] .un-row,
[data-theme="dark"] .rungs li {
  color: color-mix(in oklab, var(--ink, var(--carbon)) 45%, var(--carbon));
}
/* the dotted leader takes a breath of the lifted pigment, not the masstone */
[data-theme="dark"] .bill li > i {
  border-bottom-color: color-mix(in oklab,
    color-mix(in oklab, var(--ink, var(--rule)) 45%, var(--carbon)) 40%, var(--rule));
}
/* a struck line keeps its own retirement colour — it must not be lifted back
   into prominence by the rule above */
[data-theme="dark"] .bill li.struck,
[data-theme="dark"] .bill li.struck .b-ti { color: var(--steel); }


/* ============================================================================
   §12 · THE GOLD PLATE TAKES A DARK INK, IN EVERY ROOM.

   The token layer already knows this. head.html's dark block says it out loud:
   «A fill that does not change cannot want an ink that does» — signal is the
   one colour that is #f6c445 at noon and at midnight, so `--on-signal` was
   pinned off `--carbon` and given the dark navy the other lifted pigments take.
   `--on-reward` in the arcade skin is #151c2e in BOTH themes for the same
   reason.

   Four sites never asked for that ink. They paint the gold plate and then let
   the label inherit — and what it inherits is `--carbon`, which lifts to warm
   cream after dark. Measured on the night ground, all four:

     · the ACTIVE NAV TAB in the reading room ....... 1.47:1
       30-choreo's lights-down dim (`--chrome-ink`, signal-ink mixed toward
       carbon) was written when that tab was bare text on the header's dark
       blue. The arcade later gave the active tab a gold plate, and the dim
       kept lightening the ink that now sits on it. Specificity, not sheet
       order, is why it wins: `:root[…] header .nav-tabs button.active` is
       (0,4,2) against the arcade's (0,2,1). This is ONLY the reading route —
       choreo.js sets data-lights="down" for `view === 'read'` and nowhere
       else — which is exactly where it was reported from.
     · «READ ↓» ON THE EPIGRAPH SLIP ............... 1.53:1  (color: --carbon)
     · THE ROAD'S CURRENT VOLUME, its «0/2» ........ 1.53:1
       `.rd-done .rd-vol b` was given `--on-right`; `.rd-here .rd-vol b`, on
       the gold one, was not.
     · THE WORD FILTER'S COUNT ..................... 1.53:1
       Inverted: the active chip fills with `--carbon`, so at night the PLATE
       is cream and the gold count is printed on it. Gold cannot carry text on
       a light ground in either room — that is what `--signal-ink` exists for
       — so after dark the count sinks toward the gold's own dark ink and
       keeps its brass. The light room, where the same chip is navy and the
       gold reads at 8:1, is not touched.
   ========================================================================== */

:root[data-lights="down"] header .nav-tabs button.active {
  color: var(--on-reward, var(--on-signal));
}
.scene-epigraph .ep-btn,
.scene-epigraph .ep-btn:hover { color: var(--on-signal); }
.rd-here .rd-vol b { color: var(--on-reward, var(--on-signal)); }
[data-theme="dark"] #word-filters button.active i {
  color: color-mix(in oklab, var(--signal) 44%, var(--on-signal));
}

/* §12b · and the reading room's own ornament lifts with the rest of §11. The
   ✒ over a scene is printed in the book's pigment straight from course.json,
   and okhra (#9a6b1f — «Смерть чиновника», «Спать хочется», «Белыя ночи») is
   the one that reads as orange: 3.52:1 on the night ground. The divider, the
   dropcap and the word card were already lifted here in head.html; the
   ornament and its rule were the two that were missed. */
[data-theme="dark"] #v-read #scene-orn,
[data-theme="dark"] #v-read .scene-head .orn {
  color: color-mix(in oklab, var(--book-ink, var(--signal)) 45%, var(--carbon));
}

/* §12c · the sentences-left note, on the reading bar, after dark. It is set
   quiet with `opacity: .72` over `--play`, which at night is the lightest blue
   in the room (#486cb4) — the composite measured 3.46:1. There is nowhere for
   the quietness to go on that bar: the ink is already warm cream, so anything
   quieter is anything closer to the ground. It keeps its smaller size, which
   is where the hierarchy actually lives, and takes back the opacity. Full
   strength on that ground is 4.78:1 — over the floor, and not by much, which
   is the honest ceiling for cream on this blue. */
[data-theme="dark"] #readbar .left-note { opacity: 1; }


/* ============================================================================
   §13 · THE FOLIO STANDS IN THE FLOW, SO NOTHING CAN PRINT ON IT.

   The arena's issue number is an absolutely-positioned stamp in the plate's
   bottom-right corner, and the only thing that ever held the arena's own card
   off it was `.arena-ghost`'s foot padding — an element that renders ONLY
   when tonight's act is a road lesson. On a sitting evening (four or more
   words ripe: the commonest evening a returning reader has) the ghost is
   display:none, the host card runs all the way to the plate's foot, and its
   hard offset drop shadow prints straight through «№ 19».

   Measured by raster, not by eye: sampling the stamp's own band with the
   stamp itself suppressed, EVERY width from 320 to 620 came back with the
   card's shadow in it. 700 and up were clean only because the plate's bottom
   padding had grown past the stamp by then — luck, not architecture, and it
   ran out exactly where the phones are.

   This is the same law that the aim line cost us in §2, and the ledger's gold
   shadow before that: ON THIS DESK, CLEARANCE MAY NEVER BE OWNED BY A SIBLING
   THAT IS HIDDEN MOST EVENINGS. So the folio stops floating and takes its own
   line at the foot of the plate. In flow it measures ITSELF — there is no
   reserved strip to keep in step with the type scale, no breakpoint where the
   arrangement can come apart, and no evening in which a card can reach it.
   It still prints where a folio prints: last thing, hard right, quiet.
   ========================================================================== */

#v-home > .masthead::after {
  position: static;
  /* the corner offsets 90-chromatic sets (and re-sets at 580) are meaningless
     once the stamp is in flow — cleared together so neither can leak into the
     box model as a stray inset */
  inset: auto;
  display: block;
  width: auto; height: auto;
  /* the seam above, the plate's own bottom padding below — a folio's two
     margins. Set solid: a mono line of caps and figures has no descender to
     lose, and the default leading was spending 6px of a phone's first screen
     on nothing. THE FOLD IS STILL THE FIRST ARGUMENT (90-chromatic, 580). */
  margin-top: var(--seam);
  line-height: 1.15;
  text-align: right;
}

/* the ghost line's foot padding is now rhythm, not a guarantee, and it no
   longer has to stand in for the plate: the folio brings its own seam */
#v-home > .masthead > #arena-ghost { padding-bottom: 0; }

/* THE GHOST LINE PRINTS IN INK, NOT IN CREAM. 90-chromatic set it in
   --on-play (#fffdf5) for the arcade's blue plate; this desk's masthead is
   paper, so the unfinished sentence rendered as a cream smear under the host
   card — visible on every phone desk and on the Spanish desk at 1280, 9 Sep
   2026 — and the strongest invitation on the desk was the one line nobody
   could read. Carbon follows the theme; the gaps keep their low ink. */
#v-home .arena-ghost { border-top-color: color-mix(in srgb, var(--carbon) 22%, transparent); }
#v-home .arena-ghost .ag-q, #v-home .arena-ghost .ag-shy { color: var(--carbon); }


/* ============================================================================
   §12e · THE RECOMMENDED PLAN'S NOTE FOLLOWS ITS PLATE.

   The fifth site of §12's bug class, and the dearest of them: it is on the
   plan the paywall recommends, which is the one screen in the product that is
   asked to earn money.

   `.plan.best` was a GOLD plate when 10-components pinned its note to
   --on-signal — a dark ink, and correct on gold. The arcade skin then
   repainted the plate `--play` (navy) and gave the CARD `--on-play`; the
   child rule for .p-per / .p-note was not brought along, so it went on
   printing the gold-plate ink onto a navy one. Measured on the live paywall:
   carbon #17213a on pine #244177 — 1.59:1, against a 4.5:1 body floor, at
   11px. The «all fifteen books, and every text added while it runs» line —
   the sentence that says what the money buys — was very nearly invisible.

   The €5 card was never affected: it is a light plate and asks for
   --signal-ink, which is precisely what that token exists for («gold cannot
   carry text on a light ground in either room»).

   The remedy is §12's, not a new one: the label asks for the ink of the plate
   it is ACTUALLY printed on, and keeps the gold-plate ink as the fallback so
   any room still painting this card gold is not broken by the repair.
   ============================================================================ */
.plan.best .p-per,
.plan.best .p-note { color: var(--on-play, var(--on-signal)); }


/* ============================================================================
   §12f · THE PROFILE'S DOORS ARE INK, SO THEY MAY NOT WEAR A PLATE COLOUR.

   §12 is about a label keeping the ink of a plate it no longer sits on. This
   is the same error inverted: a PLATE colour used as INK. 90-chromatic sets
   `.px-tal b, .px-verb { color: var(--play) }` (90:190), and --play is a
   ground everywhere else it appears — `.plan.best`, `.px-case button.active`,
   the road's volumes all take it as `background`.

   In the light room this costs nothing, because --play and --pine are the
   same #244177 there. After dark they part: --play lifts only to #486cb4,
   which on the midnight ground #121d38 measures 3.24:1 — under the 4.5:1
   body floor, at 14px, on all seven of the profile's doors (Settings, Sign
   in, Memory, Colophon, Journey, The German Room, The Blot). They are the
   only way out of that page.

   --pine is the token that was actually tuned to be READ on the night
   ground: #5d8cff, 5.29:1. Scoped to the dark room, so the light and sepia
   rooms — measured at 9.51:1 and 9.33:1 — are not touched at all.
   ============================================================================ */
[data-theme="dark"] .px-tal b,
[data-theme="dark"] .px-verb { color: var(--pine); }


/* ============================================================================
   §12g · THE LIGHTS-DOWN DIM WAS DIMMING TOWARD THE WRONG END.

   §12 fixed ONE consumer of --chrome-ink (the active nav tab, which had
   acquired a gold plate) and left the token itself alone. The token is the
   problem. 30-choreo §the step declares:

       --chrome-ink: color-mix(in srgb, var(--signal-ink) 22%, var(--carbon))

   — signal-ink dimmed toward CARBON, which resolves to #302e2d. That is a
   dark ink, and every element it paints sits on the header, which is
   var(--pine) #244177 in ALL THREE ROOMS (the header plate does not flip;
   measured paper, sepia and dark, all #244177). Dark on dark:

       .wm-t «through» ................. 1.35:1
       .brand b ........................ 1.35:1
       .nav-tabs button:hover .......... 1.35:1  (from 9.83:1 at rest —
                                         hovering a tab ERASES it)

   A dim on a dark plate has to travel toward the PLATE, not toward the ink
   of the opposite room. The plate here is the game rail: 90-chromatic paints
   `body > header { background: var(--play); color: var(--on-play) }` and at
   night swaps the ground to --play-deep. --on-play is #fffdf5 in EVERY room
   — it is the one ink that never flips, because the rail is dark in all
   three — and --play-deep is dark in every room (#152747 · #1a2740 ·
   #20355e). So the dim is the rail's own cream walked 28% of the way back
   into the rail: quieter than a resting tab, which is the whole point of
   lowering the lights, and clear of the 4.5:1 body floor in all three rooms.

   (Written once with --on-pine/--pine and measured before shipping: in the
   dark room --on-pine is #10182d, so that mix came out at 1.07:1 — the same
   mistake this section exists to correct, made while correcting it. The
   pairing has to be the plate's OWN ink and the plate's OWN ground.)

   THE WORDMARK IS EXEMPTED ENTIRELY, and that is a design fault, not a
   contrast one: «through» is the middle word of a three-word logotype whose
   other two words, .wm-l, were never in the dim list. Dimming one word of a
   logotype does not quiet it, it breaks it in half. It goes back to
   --signal (6.15:1), the value it wears with the lights up.
   ============================================================================ */
:root[data-lights="down"] {
  --chrome-ink: color-mix(in srgb, var(--on-play) 72%, var(--play-deep));
}
:root[data-lights="down"] header .wm-t { color: var(--signal); }


/* ============================================================================
   §11b · THE BOOK'S NAME KEPT THE LIGHT-ROOM MIX WHILE ITS ROW GOT THE LIFT.

   27-route.css:149 gives `.rd-next` a dark-room lift — the masstone walked
   38% of the way toward --carbon, which after dark is the warm off-white, so
   the pigment survives and the line stays readable. 90-chromatic then paints
   the <b> inside it separately (90:2702) at 72% masstone, and that rule was
   never given a night half. Being the later sheet, it wins.

   So on the road's current row the English title — «Childhood Friends» under
   «Друг детства» — printed at 72% lazur over a ground that is itself 9% of
   the same lazur on the night galley: #5c7396 on #1c2d52, 2.81:1 at 13px.
   The row around it, at 38%, was fine. One word in the middle of a legible
   line was not.

   Same mix as the parent, so the two agree and the pigment is still legible
   as pigment; the light room keeps 90:2702 untouched.
   ============================================================================ */
[data-theme="dark"] .rd .rd-book .rd-next b {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 38%, var(--carbon));
}


/* ============================================================================
   §13 · THE LAMP HAS TO LIGHT THE WHOLE PAGE, NOT JUST THE EDGE OF IT.

   Reported from a reader's own screen: the reading lamp on, the dark room on,
   and the prose the colour of the page it is printed on.

   The lamp is a paper mode. 90-chromatic gives #read-wrap warm paper and the
   brown ink that belongs on it — but it stops at that one element, and every
   surface INSIDE the book goes on taking the room's own tokens. In daylight
   nobody noticed: --sheet is warm off-white there, so the page under the
   lamp's paper was near enough. After dark --sheet is #253454, and the ink
   that the lamp set on the wrapper is inherited straight down onto it:

       #text .................... #43331f on #253454 ....... 1.02:1
       .scene-epigraph .......... #43331f on #1c2a4c ....... 1.17:1
       .scene-end ............... #43331f on #1c2a4c ....... 1.17:1
       .found-object ............ #43331f on #1c2a4c ....... 1.17:1
       #fsa-card ................ #43331f on #1c2a4c ....... 1.17:1

   1.02:1 is brown on navy: the story, unreadable, for anyone who likes the
   lamp and the dark room at once. Only the elements that state their OWN ink
   escaped — the quiz options and the buttons measured 13.3:1 throughout,
   which is why this survived: it looks like a working page with the words
   taken out of it.

   So the tokens are re-cut for the duration of the lamp, on the wrapper, and
   everything nested follows without needing to be named: the page, the
   epigraph slip, the close, the found object, the words-kept card, and
   anything added to the book later. The paper is the lamp's own — including
   its two daypart deepenings, which is why it is carried as --lamp-paper
   rather than repeated — and the warm greys are the ones the sepia room
   already measured for a ground of this luminance (head.html:335-338).

   The book's pigments are deliberately NOT touched: the six masstones were
   ground for light paper and read correctly on it.
   ============================================================================ */
body.read-lamp #read-wrap {
  --lamp-paper: #f9efd9;
  background: var(--lamp-paper);
  --sheet:     var(--lamp-paper);
  --galley:    color-mix(in srgb, var(--lamp-paper) 55%, #fffdf5);
  --case:      color-mix(in srgb, var(--lamp-paper) 88%, #43331f);
  --carbon:    #43331f;
  --steel:     #6b5a44;
  --rule:      #d9c9a8;
  --rule-room: #8e7240;
  --edge:      #d9c9a8;
  /* AND THE ALIASES, WHICH DO NOT FOLLOW THE TOKENS THEY ALIAS.
     head.html:185-198 mints a second layer of names — --ink is var(--carbon),
     --ink-2p is var(--steel), --paper-hi is var(--sheet) — and those `var()`s
     are resolved AT :ROOT, where they are declared. What inherits down the
     tree is the resolved VALUE, not the reference. So re-cutting --steel on
     this wrapper moves --steel and leaves --ink-2p exactly where it was.

     Found by measuring: `.found-object .fo-note` (head.html:1459) asks for
     --ink-2p, and under the lamp in the dark room it printed the night's
     #c4cde2 on the lamp's #f8efda — 1.39:1 — while its own parent, which asks
     for --carbon, sat at 10.6:1. Two lines of the same card, one legible and
     one not, because one used a token and the other used a nickname for it.

     Every alias of a token re-cut above is therefore re-cut here too. */
  --ink: #43331f; --ink-0: #43331f; --ink-1: #43331f;
  --ink-2p: #6b5a44; --ink-2: #6b5a44;
  --ink-3p: #8a7a5e; --ink-3: #8a7a5e; --faint: #8a7a5e;
  --paper-hi: var(--lamp-paper); --paper-0: var(--galley); --card: var(--galley);
  --panel: var(--galley); --gesso: var(--galley);
  --line: #d9c9a8; --size: #d9c9a8; --line-2: #8e7240; --foot: #8e7240;
}
body[data-daypart="evening"].read-lamp #read-wrap { --lamp-paper: #f2e3c4; }
body[data-daypart="night"].read-lamp   #read-wrap { --lamp-paper: #e9d7b2; }

/* §13b · AND THE SCENE HEAD TAKES THE LAMP ROOM'S GUARD, IN EVERY THEME.

   head.html gives the pigment-inked head a guard for the night room (lift) and
   this sheet gave it one for the lamp room (darken). The reading LAMP is a
   third light ground and it is not a theme — it is a switch that turns any of
   the three into warm paper — so neither guard reached it. Measured under the
   lamp with the head's own 78% mix: 3.04:1 in daylight, 3.50 in the lamp room,
   3.73 after dark, all on #f2e3c4.

   Same remedy as the sepia guard, and it needs no theme test at all: inside
   the lamp --carbon is already the lamp's own brown (above), so walking the
   pigment halfway toward it is correct in whichever room the reader came
   from. */
body.read-lamp #v-read .scene-head .vy,
body.read-lamp #v-read .divider,
body.read-lamp #v-read #scene-orn,
body.read-lamp #v-read .scene-head .orn {
  color: color-mix(in oklab, var(--book-ink, var(--carbon)) 50%, var(--carbon));
}


/* ============================================================================
   §11c · THE LESSON STAGE KEEPS ITS PIGMENT ON THE NIGHT GROUND TOO.

   Reported from a reader's own screen, the lesson room after dark: the frame
   barely there, the sentence card's edge barely there, and the stage
   direction — «— indoors — a window off-frame, dust standing in the beam of
   it —» — sunk almost into the paper.

   §11 lifted the six masstones where they are TEXT, and 31-stages carried
   that to nine more selectors. Three sites in the stage were not in either
   list, and two of them are not text at all, which is how they were missed:

     .gx (the sheet's edge) ............... 1.22:1 on the night ground
     .gx-line.gx-study (the card's edge) .. 1.39:1
     .gx-direction (the direction) ........ 2.65:1, and it is prose

   All three derive from the book's own ink, and they derive it the way the
   light room wants: `color-mix(… var(--ink) N%, var(--carbon))` walks the
   masstone toward the ink of the room, which in daylight is #17213a and
   darkens it correctly onto white paper. After dark it is the same
   arithmetic against a ground that has moved and a pigment that cannot,
   because --ink arrives as an inline hex.

   The direction also carried 74% alpha toward transparent, which on a dark
   ground spends the little contrast the pigment had left. It takes an opaque
   lift here: a stage direction is prose, and prose is held to the body floor.
   Measured after, across the six masstones: 5.6–7.7:1 on the stage ground
   (grafit lowest at 5.64, okhra highest at 7.68), 6.39 for okhra where the
   direction sits on the sheet instead.

   THE TWO EDGES ARE NOT FIXED HERE, and the first cut of this section tried
   to and failed. They are painted from `--frame`, which 98-place declares on
   the stage and applies with !important — deliberately, and the reasoning in
   that file is sound: a room inked in madder wants the ink's own black for
   its rule, not the house's blue-black. A border-color rule in this sheet
   lost to it twice over, on the !important and on sheet order (98 > 95).
   The variable is the right place and 98-place says so itself — «NOT AS
   OVERRIDES BUT AS THE VARIABLES THEMSELVES» — so --frame is given a night
   half there, beside --play-tint, which already has one.
   ============================================================================ */
[data-theme="dark"] .gx-stage .gx-direction {
  color: color-mix(in oklab, var(--ink, var(--carbon)) 45%, var(--carbon));
}


/* ============================================================================
   §14 · A BOOK'S BOARDS ARE PAPER IN EVERY ROOM.

   The volumes on the road are built the way a bound book is: the board is
   PAPER tinted with the book's own ink, with a block of page-edges showing at
   the fore-edge. 27-route writes it as `color-mix(--ink 26–42%, var(--galley))`
   and 90-chromatic restates it for the books you are not standing on as
   `color-mix(--ink 44%, var(--play-tint))`. Both are correct arithmetic and
   both tint PAPER.

   After dark --galley is #1c2a4c and --play-tint is mixed toward #14120f, so
   the paper stops being paper: the tint lands on navy and the volume becomes
   a murky block that reads as neither a book nor a colour. Measured against
   the rows they stand on:

       Лошадиная фамилия (zelen) .... #213c3e on #1e304b .... 1.13:1
       Смерть чиновника (okhra) ..... #50422e on #2b3247 .... 1.31:1
       The alphabet ................. #16223a on the page ... 1.32:1

   A shelf of books you cannot tell apart is not a shelf. And the error is one
   of MATERIAL, not contrast: a board does not turn navy because the room got
   dark, any more than the reading lamp's page does (§13). The luminance of a
   room is a fact about the room; the whiteness of paper is a fact about the
   paper.

   So the volume carries its own paper after dark — muted, because a bright
   white chip repeated down a dark list glares, but paper. Every rule that
   tints it keeps working untouched, in both sheets, because what changed is
   what they tint rather than how they tint it.
   ============================================================================ */
[data-theme="dark"] .rd-vol {
  --galley: #e7e1d2;
  --play-tint: #e7e1d2;
  /* AND ITS INK, which the first cut of this section forgot — the same
     mistake it was written to correct, made one line further down.

     The count on a volume is drawn with `mix-blend-mode: difference` and
     `filter: invert(1)` (27-route §the volume): a trick that finds a legible
     colour against whatever chip is under it, and one that was tuned when
     that chip was DARK after nightfall. Give the board back its paper and the
     trick faithfully inverts the other way — light ink on a light board, and
     «0/3» and «0/4» went invisible on the ochre and the zelen covers. It is
     the ink-follows-plate rule again, and this time the plate was moved by
     the fix above.

     A board of paper takes the ink paper takes. --carbon is the light room's
     own #17213a here, and the blend stands down: on a surface that is the
     same paper in every room there is nothing left for it to solve. */
  --carbon: #17213a;
}
[data-theme="dark"] .rd-vol b { mix-blend-mode: normal; filter: none; }


/* ============================================================================
   §15 · THE DESK'S MASTHEAD IS ARCHITECTURE, SO IT MAY NOT LIFT EITHER.

   90-chromatic already settled this for the header rail, in its own words:
   «At night the rail keeps to the deep ink: the lifted #486cb4 that reads as
   an accent on dark surfaces is far too loud as a 62px band across the top of
   a dark room. Accents may lift at night; architecture may not.»

   The desk's masthead is the same argument and did not get the same rule. It
   is a slab three times the height of the rail, and after dark it was painted
   --play — the lifted accent — while everything printed on it kept the inks
   chosen for a plate at --play's DAYLIGHT luminance. On #486cb4 cream tops out
   at 5.05:1, so a label with any dim at all falls through the floor:

       .cameo-lab «THE COMPANY» ......... 2.69:1  (opacity .55)
       #room-door a «The German Room →» . 3.62:1  (opacity .75)

   On --play-deep the same two inks measure 4.77:1 and 7.43:1, with nothing
   else touched — no opacity moved, no colour restated. The plate was the
   fault. (.cameo-lab's dim is raised too, at its own site: it was under the
   floor in the light rooms as well, which is a separate fault the dark room
   merely made obvious.)

   The gold offset shadow stays: on the deep ink it is the same signature it
   is on the rail, and it is what tells the slab from the page behind it.
   ============================================================================ */
[data-theme="dark"] #v-home > .masthead { background: var(--play-deep); }


/* ===== 96-scale.css ===== */
/* Shared, relative type roles. The 17px floor keeps controls readable on
   phones; modest growth avoids oversized furniture on laptops. Study text
   has its own larger role, rather than enlarging every heading and badge.
   Relative floors/ceilings retain the reader's browser text preference.
   Evidence and limits: research/type-layout-review-2026-09-24.md. */
html { font-size: clamp(1.0625rem, 1rem + .1vw, 1.1875rem); }

:root {
  --type-body: 1rem;
  --type-control: 1rem;
  --type-meta: .875rem;
  --type-prompt: clamp(1.375rem, 1.25rem + .5vw, 1.75rem);
  --type-heading: clamp(1.5rem, 1.3rem + .6vw, 2rem);
  --t-00: .8125rem;
  --t-0: .875rem;
  --t-1: 1rem;
  --t-2: 1.125rem;
  --t-3: 1.25rem;
  --t-4: 1.5rem;
  --t-5: 1.8125rem;
  --t-6: 2.1875rem;
  --t-7: 2.75rem;

  /* --- THE 4PX MODULE MOVES WITH THE TYPE, or the type grows into padding
     that did not. All nine steps, exact at a 16px root. Checked before
     converting: every one of the 174 var(--sN) call sites is a padding, gap,
     margin, or an inset — none is a border, a hairline, or a fixed hitbox, so
     none of them is a thing that must stay put while the room breathes. --- */
  --s1: 0.25rem;  --s2: 0.5rem;   --s3: 0.75rem;  --s4: 1rem;   --s5: 1.25rem;
  --s6: 1.5rem;   --s7: 2rem;     --s8: 2.5rem;   --s9: 3rem;

  /* --- THE FURNITURE THAT HOLDS TYPE, on the same lift.
     ~225 hard-pixel dimensions in this product sit under token-sized text.
     Left in pixels they would not clip — the road's boxes have slack — they
     would do something quieter and worse: keep their size while the type grew
     inside them, so a drawing composed at 15px would arrive at 17.8px subtly
     crowded, and the enlargement would read as a mistake rather than as room.
     The ones that actually bear type are restated in rem, exact at base. --- */
  --rd-gut:   4rem;      /* 64 — the road's leading column */
  /* the volume's LIVE size is the chromatic skin's 60×76 (90-chromatic §),
     NOT 27-route's 58×80: that sheet loads later and wins. Ramping from the
     superseded number would have shrunk the volume on a desk. */
  --rd-vol-w: 3.75rem;   /* 60 — the standing volume */
  --rd-vol-h: 4.75rem;   /* 76 */
  --rd-fig:   4.5rem;    /* 72 — the resident's medallion */
  --rd-orn-c: 2.125rem;  /* 34 — chapter ornament column */
  --rd-no-c:  1.875rem;  /* 30 — the line-number margin */
  --rd-no-hc: 2.75rem;   /* 44 — …on the live row */
  --rd-seal:  2.625rem;  /* 42 — the milestone wax */
  --card-min: 11.125rem; /* 178 — auto-fill card floor */
}


/* ============================================================================
   §96.2 · THE ROOM ITSELF.

   The shell caps at min(1280px, 94vw). 1280 was chosen against 15px type; at
   17.8px it holds a fifth fewer characters per line, so holding the same
   number is a widening, not an indulgence. It moves only above 1500px, where
   there is screen left over to move into.
   ========================================================================== */

@media (min-width: 1500px) {
  .view { max-width: min(1440px, 94vw); }
}


/* Grammar and Journey are collections of cards. Their room width is
   independent of the bounded line length within each card. */
@media (min-width: 1100px) {
  /* Grammar is a shelf of cards; the journey is a patent, a chart and a
     timeline. Both are furniture, and furniture wants the room. */
  #v-grammar > *,
  #v-journey > * { max-width: min(94%, 1120px); }

  /* three across once there is room for three: the 2-up grid was written for
     900px and holds a 17px Russian headword in a 445px card. */
  .glist { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
}

/* Practice keeps a bounded question column. Wide-screen companion space is
   added by study-delight without increasing the prose measure. */

/* ============================================================================
   §96.4 · THE ROAD AND THE SHELVES, AT DESK SIZE.

   ALL OF IT IS INSIDE min-width: 1100px, and that guard is load-bearing
   rather than tidy. 27-route.css and 90-chromatic-game.css both restate these
   same selectors inside max-width: 560/620 queries at equal specificity, and
   this sheet sorts after both. An unguarded rule here would have won those
   queries and handed a 390px phone the desk's gutter and a two-column lesson
   row that no longer fits. Below 1100px every value below is identical to
   today's anyway — the guard is what makes that a fact and not a coincidence.
   ========================================================================== */

@media (min-width: 1100px) {
  .rd-hit, .rd-mark, .rd-beyond { grid-template-columns: var(--rd-gut) 1fr; }
  .rd-who                       { grid-template-columns: var(--rd-fig) 1fr; }
  .rd-ch-head                   { grid-template-columns: var(--rd-orn-c) 1fr auto; }
  .rd-lesson                    { grid-template-columns: var(--rd-no-c) 1fr auto; }
  .rd-l-here                    { grid-template-columns: var(--rd-no-hc) 1fr auto; }

  .rd-vol     { width: var(--rd-vol-w); height: var(--rd-vol-h); }
  .rd-who-fig { width: var(--rd-fig);   height: var(--rd-fig); }
  .rd-m-dot   { width: var(--rd-seal);  height: var(--rd-seal); }
  /* `.rd .rd-vol-a`, not `.rd-vol-a`: 90-chromatic restates the squat chip at
     (0,2,0), and a single class here would lose to it whatever the order. */
  .rd .rd-vol-a { height: 3.75rem; }   /* 60 */

  /* --- ELEVEN PIXELS IS RETIRED AS A TEXT SIZE ON A DESK.
     --t-00 rising to 13.1 does most of the work, but three labels on the road
     are 11px uppercase at .12em tracking, which is the hardest combination on
     a screen to identify at a glance — and one of the three is the chapter
     state, the line that answers "where was I", which is the question that
     decides whether an evening happens at all.

     Tracked capitals need either size or air. Below 1100px they keep the
     tracking, because at 11px it is the only thing holding the letters apart.
     Above it they take the size and give some of the tracking back: once the
     letters are large enough to tell apart on their own, tracking that wide
     stops helping and starts costing — it is what makes a caption read as a
     grey band rather than as words. --- */
  .rd-ch-state { font-size: var(--t-0); letter-spacing: .08em; opacity: .92; }
  .rd-ch-ru    { font-size: var(--t-0); }
  .rd-ti em    { font-size: var(--t-0); letter-spacing: .03em; }

  /* THE SECOND HALF OF THE COMPLAINT was spacing, not size. 11 → 13 → 15 → 17
     puts four of the nine steps inside six pixels, which at desk distance is
     at or under the just-noticeable difference — four ranks of meaning
     arriving as one grey. The lift widens those gaps proportionally; these
     three rules widen the ones that carry wayfinding by a whole step, which
     is the part a proportional lift cannot do. The chapter title is the
     road's signage and sat one step under the book title above it. It takes
     that step back. */
  .rd-ch-ti { font-size: var(--t-3); letter-spacing: .05em; }
}

/* The road's decorative ghosts — .rd-book::after at 72px and .rd-stamp — are
   NOT lifted. They are 8–16% marks behind running text, they already overflow
   their rows by design, and growing them buys atmosphere at the price of the
   thing the atmosphere is behind. */


/* ============================================================================
   §96.5 · THE CARD FLOORS.

   Seven auto-fill grids are written `minmax(NNNpx, 1fr)`. The floor is what
   decides how many cards fit; leave it at a phone-era number while the type
   inside grows, and the cards stop fitting their own contents. Guarded,
   because 10-components.css collapses .shelf and .tiles to a single column on
   a phone at the same specificity this sheet would otherwise beat.
   ========================================================================== */

@media (min-width: 1100px) {
  #v-course .cs-letters { grid-template-columns: repeat(auto-fill, minmax(var(--card-min), 1fr)); }
  .shelf        { grid-template-columns: repeat(auto-fill, minmax(11.75rem, 1fr)); }  /* 188 */
  .tiles        { grid-template-columns: repeat(auto-fit,  minmax(7rem, 1fr)); }      /* 112 */
  .plans        { grid-template-columns: repeat(auto-fit,  minmax(9.375rem, 1fr)); }  /* 150 */
  .glesson-card { min-height: 4rem; }                                                 /* 64  */
}


/* ============================================================================
   §96.6 · THE READING PAGE RIDES THE SCALE, AND THAT IS THE DECISION.

   #text is the one size in the product the reader sets themselves (--fs, from
   app.js). It is stored in rem, so it lifts with everything else — a reader
   who chose 1.28 gets 1.28 OF THIS ROOM rather than 1.28 of a sixteen-pixel
   room they are not sitting in. The measure holds: #read-wrap's 34em is
   root-relative too, so the line grows and the character count does not, and
   34em is 34em at every width. That is the whole reason it was written in em.

   The alternative — freezing the one room the complaint was loudest about —
   is not a smaller change, it is an incoherent one.

   To pin the reader to a fixed size instead, the change is in app.js: have it
   write --fs in px (sett.fs * 16) rather than rem. There is no CSS-only way to
   divide out the root, and a half-true recipe here would cost the next reader
   an afternoon.
   ========================================================================== */


/* ============================================================================
   §96.7 · THE FLOOR, FINISHED.

   95-coherence.css §7 set a type floor and it was right to: twenty-two
   elements on the desk printed under head.html's own smallest step. It fixed
   the desk. Sweeping the whole product for this sheet turned up five more
   outside it, and they are not compounding em-inheritance bugs like the ones
   §7 caught — they are declared, deliberate, and far lower:

       .exlibris .xl-cap    .5rem   =  8.0px      ← the smallest text shipped
       .ticket .tk-head     .6rem   =  9.6px
       .book .lvl          .64rem   = 10.2px
       .guide .g-next      .66rem   = 10.6px
       .room-line          .66rem   = 10.6px

   The lift in §96 carries them to 9.5–12.5px, which fixes four of them and
   leaves the ex-libris caption still under ten. So the floor is finished
   here rather than left to arithmetic: nothing in this product sets type
   below its own bottom step, at any width, and every one of these now rides
   the scale from --t-00 like everything else.

   Every one is a letterspaced uppercase micro-label — the ex-libris caption,
   the ticket's head, the shelf's level chip, the guide's next-link, the room
   line. Those are the hardest things on a screen to read at a glance and the
   ones most often set smallest, because they look like ornament in a mockup
   and turn back into words the moment somebody needs them.
   ========================================================================== */

.exlibris .xl-cap,
.ticket .tk-head,
.book .lvl,
.guide .g-next,
.room-line { font-size: var(--t-00); }


/* ============================================================================
   §96.8 · THE ROUND THINGS.

   Thirteen badges in this product are a fixed square box with a glyph or a
   number centred in it: the patent's class medallion, the journal seals, the
   achievement discs, the ledger tiles, the book's finish stamp, the popover's
   close. None of them BREAKS when the type lifts — every one has slack, and
   several set their own contents in em and so already grow.

   That is exactly why they are here. A badge that keeps a 48px box while its
   glyph goes from 27px to 32px has not broken; it has quietly become a
   different drawing — tighter, heavier, less like the thing that was drawn.
   The failure mode of leaving these alone is not a bug report, it is a page
   that looks slightly worse for a reason nobody can name. Each box is
   restated in rem at its exact current size, so the ratio between the glyph
   and the ring around it is the one that was drawn, at every width.

   Each value is the LIVE one — .ach is 10-components' 48px, not head.html's
   54px; .b-seal is 28px, not 40px; the patent is 66-rooms' 86px, not 74px.
   The later sheet wins in all three cases, and ramping from the superseded
   number would have shrunk the badge on a desk instead of growing it.
   ========================================================================== */

@media (min-width: 1100px) {
  #journey-patent .jp-cls { width: 5.375rem; height: 5.375rem; }   /* 86 */
  .jseal-wax              { width: 2.625rem; height: 2.625rem; }   /* 42 */
  .jseal:has(.seal-art)   { grid-template-columns: 3.25rem 1fr; }  /* 52 — the column the wax sits in */
  .ach                    { width: 3rem;     height: 3rem; }       /* 48 */
  .ltile                  { min-width: 2.375rem; height: 2.75rem; }/* 38 / 44 */
  .book .b-seal           { width: 1.75rem;  height: 1.75rem; }    /* 28 */
  #pop .close             { width: 2.5rem;   height: 2.5rem; }     /* 40 */
  .gx-stage .gx-pat-cls   { width: 2.875rem; height: 2.875rem; }   /* 46 */
  .stl-cls                { width: 2.75rem;  height: 2.75rem; }    /* 44 */
}

/* Give translated destination names their natural width. On very narrow
   phones the destinations wrap as whole buttons; no label crosses another
   button and every destination remains visible without horizontal scrolling. */
@media (max-width: 560px) {
  /* 6px of head-room over the plates: at 0 the Sign up and Account plates
     stood flush on the top edge of the screen. A notch, where the page is
     laid under one, adds its own inset on top of that. */
  header .bar { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0; padding: calc(6px + env(safe-area-inset-top, 0px)) 10px 0; }
  header .bar .brand.wordmark { grid-row: 1; grid-column: 1; display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 0; margin: 0; padding-block: 4px; }
  header .bar .brand.wordmark .wm-l { font-size: 11px; line-height: 1.1; letter-spacing: .12em; }
  header .bar .brand.wordmark .wm-t { font-size: 11px; line-height: 1; }
  header .bar > .spacer { display: none; }
  header .bar .nav-ident { grid-row: 1; grid-column: 2; justify-content: flex-end; overflow: visible; mask-image: none; -webkit-mask-image: none; }
  header .bar .nav-rooms { grid-row: 2; grid-column: 1 / -1; display: flex; flex-wrap: wrap; width: 100%; overflow: visible; mask-image: none; -webkit-mask-image: none; gap: 4px; border-top: 1px solid currentColor; padding-bottom: 6px; }
  header .bar .nav-tabs button { min-height: 44px; padding: 8px 4px; letter-spacing: .025em; font-size: .875rem; }
  header .bar .nav-rooms button { flex: 1 0 auto; min-width: 0; max-width: 100%; box-sizing: border-box; padding-inline: 8px; white-space: normal; overflow-wrap: anywhere; letter-spacing: 0; }
}
@media(max-width:360px){
  header .bar .nav-rooms button{flex-basis:calc(33.333% - 4px);padding-inline:6px;font-size:.8125rem;}
  /* the reading bar's four plates need 300px; at 320 the house margins left
     294, and the ··· plate hung 7px past the edge. Tighter margins keep the
     one row whole at the narrowest phone — the break in head.html is for
     labels that truly do not fit, not for this. */
  #readbar .inner{padding-inline:8px;column-gap:4px;}
}


/* ===== 97-lessons.css ===== */
/* ============================================================================
   97-lessons.css — THE LESSON ROOM, MADE A PLACE.

   Written after an audit of a full nineteen-shot walkthrough of one lesson
   («Лошадиная фамилия», lesson 6 of 13) at 1920. The room WORKS — the ladder
   of question shapes is excellent, the progress pips are honest, the copy is
   in voice throughout. What it is not is a PLACE. Thirteen lessons and eight
   question shapes all wear the same chrome, and the only thing that changes
   between one card and the next is the sentence in the middle of it.

   Numbered 97 on purpose. The arcade sheet (90) states most of this room with
   `!important`, so a mend has to sort after it — the same reason 95 and 96
   exist. Nothing here invents a vocabulary: it spends the pigments, moods and
   weather the product already computes and was not showing.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   §1 · GOLD, TAKEN BACK FROM THE FOOTNOTES.

   The palette law: gold is celebration and illumination ONLY, never semantic.
   In this room it had become the background of an explanatory note — the chip
   that says «a reading between sorts — nothing here is graded», «a pencil, not
   a grade», «from the last sitting». Three different footnotes, all wearing
   `--reward-tint`, which is the tint the actual reward wears.

   So in the walkthrough, «НАКОНЕЦ-ТО!» — a word the reader has just earned —
   arrives in exactly the same colour as a sentence explaining that nothing is
   being graded. The gold was spent before it could be earned, on the one
   surface where earning it is the entire point.

   The notes are notes now: paper, a rule, and the house's quiet ink. The gold
   is left for the thing that deserves it, which is two centimetres below.
   --------------------------------------------------------------------------- */
.gx-stage .gx-again {
  background: color-mix(in srgb, var(--carbon) 5%, var(--gesso, transparent));
  color: var(--ink-2);
  border: 1px solid color-mix(in srgb, var(--carbon) 22%, transparent);
  font-weight: 600;
}
[data-theme="dark"] .gx-stage .gx-again {
  background: color-mix(in srgb, var(--carbon) 8%, transparent);
  color: var(--ink-2);
}

/* ---------------------------------------------------------------------------
   §2 · THE ROOM WEARS THE BOOK IT IS TEACHING.

   Every book on the shelf is assigned one of six inks a printing house
   actually kept — Prussian blue, ochre, verdigris, sepia, madder lake,
   graphite — and the road, the reading page, the word card and the practice
   card all print in it. The lesson room was the one surface that did not: its
   head is the house navy for «Шинель» and for «Лошадиная фамилия» alike, so
   twenty lessons in twenty chapters read as twenty visits to one hall.

   The head keeps its navy — white type on six different grounds is six
   different contrast problems, and this room is read for twenty minutes at a
   time. It takes the book's pigment as a band beneath it instead: the same
   move the reading page makes, where the sheet's top edge is the book's ink.
   --------------------------------------------------------------------------- */
.gx-stage .gx-top {
  box-shadow: inset 0 -8px 0 -3px var(--ink, var(--press));
}

/* ---------------------------------------------------------------------------
   §3 · AND THE SCENE IT IS TEACHING.

   The reading page already wears its instalment's mood as a wash — deeper ink
   where the author grows heavy, a whisper of gold where he is light — set from
   the same arc that poses the resident. The lesson room teaches the words of
   that same instalment and showed none of it.

   Identical strengths to the reading page's (29-alive §the page wears the
   scene's weather), for one reason: a reader who moves from the lesson to the
   page should not feel the light change. It is one room in one book.
   --------------------------------------------------------------------------- */
/* THE GROUND HAS TO BE IN THIS LIST (2026-08-25). This was
   `transition: box-shadow 1100ms` — a SHORTHAND, in a sheet that sorts after
   26-games, at the same specificity. So it silently replaced 26-games:661's
   `background 900ms, box-shadow 1400ms` and left the stage with no background
   transition at all. Every change to the ground — the exercise's own weather
   (data-move), the run's mood (data-mood), the heat, the chapter's tint —
   therefore SNAPPED, on every question, while the walls faded politely behind
   it. That mismatch is what read as the room lurching between questions.

   The lesson: a transition shorthand in a late sheet is a silent revert of
   every transition an earlier sheet declared on the same selector. Name the
   properties you mean and check what already claims the element.

   The ground is slower than the walls on purpose: it is the largest surface
   on the screen and the one nobody should catch moving. */
.gx-stage {
  transition: background 1200ms var(--e-std, ease),
              box-shadow 1100ms var(--e-std, ease);
}
.gx-stage[data-mood="toska"], .gx-stage[data-mood="sad"] { box-shadow: inset 0 0 22vmax rgba(26, 21, 12, .15); }
.gx-stage[data-mood="wary"] { box-shadow: inset 0 0 18vmax rgba(26, 21, 12, .10); }
.gx-stage[data-mood="happy"], .gx-stage[data-mood="proud"] { box-shadow: inset 0 0 16vmax rgba(191, 146, 42, .08); }
[data-theme="dark"] .gx-stage[data-mood="toska"],
[data-theme="dark"] .gx-stage[data-mood="sad"] { box-shadow: inset 0 0 22vmax rgba(0, 0, 0, .42); }
[data-theme="dark"] .gx-stage[data-mood="wary"] { box-shadow: inset 0 0 18vmax rgba(0, 0, 0, .3); }
[data-theme="dark"] .gx-stage[data-mood="happy"],
[data-theme="dark"] .gx-stage[data-mood="proud"] { box-shadow: inset 0 0 16vmax rgba(191, 146, 42, .04); }

/* ---------------------------------------------------------------------------
   §4 · THE CARD DOES NOT FLOAT IN A VOID.

   At 1200px tall the panel ends around 730 and everything below it is empty
   ruled paper — a third of the screen, doing nothing, on the surface a reader
   spends most of their evening looking at. `align-content: start` is right
   (the question must not move when an option list is longer than the last),
   so the answer is not to centre it: it is to give the room a floor, so the
   emptiness reads as the rest of the room rather than as the page running out.

   One wash, in the book's own ink at 4%, rising from the bottom edge. It is
   under everything, it never moves, and at that density it is felt rather than
   seen — which is the whole intention.
   --------------------------------------------------------------------------- */
/* IT IS A BACKGROUND LAYER, NOT A PSEUDO-ELEMENT, and that is the whole of
   the fix reported at the end of a grammar article: «things were cut off…
   like the lamp thing». An ::after paints in the same stacking context as the
   content and has to be held under it by z-index, which works until some
   sheet gives a descendant a stacking context of its own — and the article's
   finish sheet, with its lamp and its two doors, is exactly that. A
   background layer is behind every descendant by definition. There is no
   z-index to lose. */
.gx-stage:not([data-move="gather"]) {
  background-image: linear-gradient(to top,
    color-mix(in srgb, var(--ink, var(--carbon)) 5%, transparent) 0 46vh,
    transparent 46vh);
}
[data-theme="dark"] .gx-stage:not([data-move="gather"]) {
  background-image: linear-gradient(to top, rgba(0, 0, 0, .3) 0 46vh, transparent 46vh);
}

/* ============================================================================
   §5 · EIGHT ASKS, EIGHT COSTUMES.

   The walkthrough contains eight genuinely different acts of mind and they are
   visually indistinguishable apart from a line of text and a 20px icon. The
   room already carries `data-kind` on .gx-mid and already spends it on
   ENTRANCES (26-games §the ten sheets) — each shape arrives differently. What
   it never spends it on is the SURFACE the shape is played on.

   So each ask gets a ground: a watermark of the instrument the reader is
   actually using, printed into the paper at the density of a laid line rather
   than drawn on it. No new hue anywhere — every one of these is the house's
   own ink at between 3% and 6%, which is the range where a mark is felt on the
   third card and noticed on the tenth.

   They are CSS gradients rather than assets on purpose: this product ships as
   one file that has to open off a disk in ten years, and eight background
   images is eight things that can fail to load.
   ========================================================================== */
.gx-stage .gx-mid {
  position: relative;
  background-repeat: no-repeat;
  background-position: 50% 62%;
}
.gx-stage .gx-mid > * { position: relative; z-index: 1; }

/* ---- SORT THE TYPE. A compositor's case: the shallow wooden compartments a
   sort is lifted out of. The one ask that is literally about sorting type, and
   it was played on blank paper. ---- */
.gx-stage .gx-mid[data-kind="pairs"] {
  background-image:
    repeating-linear-gradient(to right,
      color-mix(in srgb, var(--ink, var(--carbon)) 7%, transparent) 0 1px, transparent 1px 84px),
    repeating-linear-gradient(to bottom,
      color-mix(in srgb, var(--ink, var(--carbon)) 7%, transparent) 0 1px, transparent 1px 58px);
  background-size: 100% 100%;
  background-position: 0 0;
}

/* ---- PUT THE WORD BACK. A ruled line with a space left in it — which is
   exactly what the reader is looking at, and exactly what the page does when a
   word has been lifted out of it. ---- */
.gx-stage .gx-mid[data-kind="gap"],
.gx-stage .gx-mid[data-kind="form"],
.gx-stage .gx-mid[data-kind="dict"],
.gx-stage .gx-mid[data-kind="case"] {
  background-image: repeating-linear-gradient(to bottom,
    transparent 0 37px,
    color-mix(in srgb, var(--ink, var(--carbon)) 6%, transparent) 37px 38px);
  background-position: 0 8px;
}

/* ---- BY EAR. Concentric arcs off the left edge — sound arriving, drawn the
   way a diagram of it is drawn, and faint enough to be a watermark. ---- */
.gx-stage .gx-mid[data-kind="listen"],
.gx-stage .gx-mid[data-kind="clip"],
.gx-stage .gx-mid[data-kind="hearline"] {
  background-image: repeating-radial-gradient(circle at 6% 50%,
    transparent 0 46px,
    color-mix(in srgb, var(--ink, var(--carbon)) 6%, transparent) 46px 47px);
}

/* ---- WRITTEN OUT. Pencil paper: a close-ruled sheet, the surface you write a
   word on rather than pick one from. ---- */
.gx-stage .gx-mid[data-kind="write"],
.gx-stage .gx-mid[data-kind="stress"] {
  background-image: repeating-linear-gradient(to bottom,
    transparent 0 25px,
    color-mix(in srgb, var(--ink, var(--carbon)) 5%, transparent) 25px 26px);
}

/* ---- THE COMPOSING STICK. Setting a line in order, held in the hand: one
   deep rule under the whole business, the way the stick's wall sits under the
   type standing in it. ---- */
.gx-stage .gx-mid[data-kind="order"] {
  background-image: linear-gradient(to top,
    color-mix(in srgb, var(--ink, var(--carbon)) 9%, transparent) 0 3px, transparent 3px);
  background-position: 0 100%;
  background-size: 100% 100%;
}

/* ---- THE DRAWER. Recognition and meaning are the word-case: a card standing
   in a drawer, with the drawer's own rule above it. ---- */
.gx-stage .gx-mid[data-kind="meaning"],
.gx-stage .gx-mid[data-kind="word"] {
  background-image: linear-gradient(to bottom,
    color-mix(in srgb, var(--ink, var(--carbon)) 8%, transparent) 0 2px, transparent 2px);
  background-position: 0 0;
  background-size: 100% 100%;
}

/* ---- THE GRAMMAR ASKS. Aspect, gender, and the arc beats are not about a
   word at all — they are about a RULE, and the reader is being sent to a
   reference. So they wear a reference's furniture: the double rule a printed
   grammar puts down its margin, where the article number would stand. ---- */
.gx-stage .gx-mid[data-kind="aspect"],
.gx-stage .gx-mid[data-kind="gender"],
.gx-stage .gx-mid[data-kind="gram"] {
  background-image: linear-gradient(to right,
    color-mix(in srgb, var(--ink, var(--carbon)) 10%, transparent) 0 2px,
    transparent 2px 6px,
    color-mix(in srgb, var(--ink, var(--carbon)) 6%, transparent) 6px 7px,
    transparent 7px);
  background-position: 0 0;
  background-size: 100% 100%;
}

/* ---- AND THE NARRATIVE BEATS WEAR NOTHING AT ALL. «What happens next» and
   «where you stood» are the two cards in the room that are not exercises: the
   reader is reading. Every other ask gets an instrument; these get a clean
   page, a wider measure, and no furniture — because the correct costume for
   reading is no costume. ---- */
.gx-stage .gx-mid[data-kind="attend"],
.gx-stage .gx-mid[data-kind="authored"],
.gx-stage .gx-mid[data-kind="pending"] { background-image: none; }
.gx-stage .gx-mid[data-kind="attend"] .gx-opts,
.gx-stage .gx-mid[data-kind="authored"] .gx-opts { gap: 14px; }

/* ============================================================================
   §6 · THE OPTIONS ARE TYPE SORTS.

   A reader touches one of these every eight seconds for twenty minutes; it is
   the most-handled surface in the product. It already had a plate treatment —
   a carbon rule and a 4px shadow — which is right, but generic: it is A
   BUTTON, and everything else in this house is an object.

   A metal sort has a NICK — a groove cast into one side so the compositor can
   feel, without looking, which way up the letter stands. That is the detail
   that makes a sort a sort, and it costs one pseudo-element. It also happens
   to be true to the one thing this room is: a case of words being set.

   The bevel is the second half of it: cast metal catches light on its top edge
   and loses it at the foot. One inset highlight, one inset shade, both in the
   house ink so neither is a new colour.
   ========================================================================== */
.gx-stage .gx-opt {
  position: relative;
  box-shadow:
    0 4px 0 var(--press),
    inset 0 1.5px 0 color-mix(in srgb, #fff 55%, transparent),
    inset 0 -2px 0 color-mix(in srgb, var(--carbon) 12%, transparent);
}
/* the nick: the groove down the side of the sort */
.gx-stage .gx-opt::before {
  content: ''; position: absolute; left: 7px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 26px; border-radius: 2px;
  background: color-mix(in srgb, var(--carbon) 16%, transparent);
  pointer-events: none;
}
.gx-stage .gx-opt:not(:disabled):hover::before {
  background: color-mix(in srgb, var(--ink, var(--carbon)) 42%, transparent);
}
/* pressed: the sort goes down into the stick and the bevel goes with it */
.gx-stage .gx-opt:not(:disabled):active {
  box-shadow:
    0 1px 0 var(--press),
    inset 0 2px 0 color-mix(in srgb, var(--carbon) 14%, transparent);
}
[data-theme="dark"] .gx-stage .gx-opt {
  box-shadow:
    0 4px 0 var(--press),
    inset 0 1.5px 0 color-mix(in srgb, #fff 12%, transparent),
    inset 0 -2px 0 rgba(0, 0, 0, .3);
}
/* the pairs game's tiles are sorts too — the same object, being matched */
.gx-stage .gx-mid[data-kind="pairs"] .gx-opt::before { height: 18px; }

/* ============================================================================
   §7 · A WORD THAT IS SET IS STRUCK, NOT ANNOUNCED.

   «НАКОНЕЦ-ТО!» is the reward for the whole card, and it faded in. This house
   has a glyph-strike (28-feel) written for exactly this and never spent here:
   full ink from the first frame, over-scaled a hair, compressed to rest. A
   fade is a ghost; type is struck.

   The line under it keeps its fade — it is a caption, not an impression — so
   the strike lands on the one word that was earned and on nothing else.
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  body:not(.motion-low) .gx-stage .gx-verdict.gx-ok {
    animation: gx-glyph-strike calc(.16s * var(--motion, 1)) var(--e-std, ease) both;
  }
}

/* ============================================================================
   §8 · THE ROOM STOPS GOING DARK — and stops going dark FOR SUCCEEDING.

   Reported from a real card: cream wall, a near-black slab of a room, and
   pure-white options on it. Three value extremes stacked, and it «happens more
   often for different cards», which is the tell — it is not one card, it is a
   formula with no ceiling.

   26-games states the room as the book's ink mixed into the wall:

       15% + tint × 2% + heat × 7%

   `tint` is the chapter's index inside its book and `heat` is the run's rung.
   So the eleventh chapter of a book, read by somebody on a run of nine, gets
   the ink at FIFTY-SIX PER CENT. Against Prussian blue that is a mid-dark
   room, and the white option standing on it measures 3.5:1 — a stark plate on
   a wall, not paper on paper. The sheet's own comment promises «always far
   below text contrast; the paper-neutrals clause holds at both ends», and at
   the top end it does not: nothing capped it.

   TWO FIXES, AND THE SECOND MATTERS MORE.

   (a) A CEILING. 26% of the book's ink, whatever the chapter and whatever the
       run — which keeps the option between 1.5:1 and 1.9:1 on the room at
       every combination, which is paper on paper, which is what was intended.

   (b) SUCCESS MUST NOT DARKEN THE ROOM. Heat fed the wash 7% a rung, so the
       better a reader did, the darker the room got. That was defensible when
       it was the only thing heat did — «the ink taking», as the sheet says.
       It is not defensible now: heat also lifts the weather, clears the sky
       and quietens the room's sound, all in the direction of LIGHT. Two
       systems were pulling against each other on the same signal and the
       reader was watching the losing one. Heat keeps a trace here — 2% a
       rung, under the ceiling — so the pressman's idea survives without
       contradicting the reward.

   Stated in this sheet rather than edited into 26-games because the arcade
   sheet (90) restates the same selector after it, and only a sheet sorting
   after BOTH can hold a ceiling over both.
   ========================================================================== */
.gx-stage {
  background: color-mix(in srgb, var(--ink, var(--carbon))
    min(26%, calc(14% + var(--tint, 0) * 1.4% + var(--heat, 0) * 2%)), var(--room));
}
/* the door is morning: it stays the lightest room of the four */
.gx-stage[data-move="door"] {
  background: color-mix(in srgb, var(--ink, var(--carbon))
    min(22%, calc(12% + var(--tint, 0) * 1.2%)), var(--room));
}
/* the forge is allowed to be the deep room — it is the smithy — but a deep
   room is still a room, and 48% was a wall */
.gx-stage[data-move="forge"] {
  background: color-mix(in srgb, var(--ink, var(--carbon))
    min(34%, calc(22% + var(--tint, 0) * 1.4%)), var(--room));
}
/* the gathering is ruled paper, and paper is the lightest thing here */
.gx-stage[data-move="gather"] {
  background:
    repeating-linear-gradient(transparent 0 34px,
      color-mix(in srgb, var(--ink, var(--carbon)) 20%, transparent) 34px 35px),
    linear-gradient(180deg,
      color-mix(in srgb, var(--signal) 9%, transparent), transparent 40vh),
    color-mix(in srgb, var(--ink, var(--carbon))
      min(20%, calc(5% + var(--tint, 0) * 1.4%)), var(--room));
}



/* The invitation shows one playable step and a real lesson line. The resident
   is a still stamp here; the lesson owns its live reactions and ceremonies. */
#v-course .cs-next {
  display: grid;
  grid-template-columns: 6rem minmax(0, 1fr) minmax(10rem, .38fr);
  gap: clamp(20px, 2.5vw, 32px);
  margin-block: var(--breath, 24px);
  padding: clamp(20px, 2.5vw, 32px);
  background: var(--paper);
  border: 1px solid var(--carbon);
  border-top: 4px solid var(--carbon);
  box-shadow: 4px 4px 0 var(--rule-room);
  color: var(--carbon);
}
.cs-next-guide { align-self: start; padding-top: var(--seam, 12px); }
.cs-next-guide svg { display: block; width: 100%; height: auto; }
.cs-next-main { min-width: 0; }
#v-course .cs-next h2 { margin: .25em 0 .5em; font-size: clamp(1.5rem, 2.2vw, 2rem); line-height: 1.15; text-transform: none; }
.cs-next-kicker { margin: 0; font-family: var(--sans); font-size: .75rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; }
.cs-next-note { max-width: 52ch; margin: 0; line-height: 1.55; }
.cs-next-excerpt { margin-block: var(--seam, 12px); padding: var(--seam, 12px) 0; border-block: 1px solid var(--rule-room); }
.cs-next-excerpt blockquote { margin: .4em 0; font-size: 1.3rem; line-height: 1.5; overflow-wrap: anywhere; }
.cs-next-excerpt summary { width: fit-content; cursor: pointer; font-family: var(--sans); font-size: .875rem; text-decoration: underline; text-underline-offset: .2em; padding-block: .5em; }
.cs-next-excerpt details p { margin: .5em 0; }
.cs-next-letters { font-size: 1.6rem; letter-spacing: .1em; margin-block: var(--seam, 12px); }
.cs-next-actions { display: flex; align-items: center; gap: var(--seam, 12px); flex-wrap: wrap; margin-top: var(--seam, 12px); }
.cs-next-actions span { font: .875rem var(--sans); }
.cs-method { margin: 0; padding: 0 0 0 var(--gutter, 24px); border-left: 1px solid var(--rule-room); list-style: none; counter-reset: lesson-step; align-self: center; }
.cs-method li { counter-increment: lesson-step; position: relative; padding-left: 2rem; margin-block: var(--seam, 12px); }
.cs-method li::before { content: counter(lesson-step, decimal-leading-zero); position: absolute; left: 0; top: .25em; font: .75rem var(--sans); }
.cs-method b { display: block; font-size: 1.125rem; }
.cs-method span { display: block; margin-top: .3em; font: .875rem/1.45 var(--sans); }
#v-course .cs-next :focus-visible { outline: 2px solid var(--carbon); outline-offset: 4px; }
@media (max-width: 820px) {
  #v-course .cs-next { grid-template-columns: 4rem minmax(0, 1fr); }
  .cs-method { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--seam, 12px); border-left: 0; border-top: 1px solid var(--rule-room); padding: var(--seam, 12px) 0 0; }
}
@media (max-width: 560px) {
  #v-course .cs-next { grid-template-columns: minmax(0, 1fr); position: relative; }
  .cs-next-guide { position: absolute; right: var(--gutter, 24px); top: var(--gutter, 24px); width: 3rem; padding: 0; }
  .cs-next-kicker, #v-course .cs-next h2 { padding-right: 3rem; }
  .cs-next-excerpt .cs-next-kicker { padding-right: 0; }
  .cs-next-actions .btn { width: 100%; min-height: 44px; }
  .cs-method li { padding-left: 0; padding-top: 1.5rem; margin: 0; }
  .cs-method b { font-size: 1rem; }
}

/* The language name is the first readable heading, even before a learner
   can decode the decorative native-language running head. */
#v-course > .masthead .rh-ru { font-size: .875rem !important; letter-spacing: .12em !important; }
#v-course > .masthead h1 { font-size: clamp(1.8rem, 3vw, 2.6rem) !important; font-style: normal !important; margin-block: .2em !important; }
#v-course > .masthead .cs-lede { margin-block: .4em; }
#v-course > .masthead { padding-block: var(--seam, 12px); }

/* The finished sentence stays central. Detailed counters are a disclosure;
   keeping a favourite line and choosing the next step remain direct acts. */
.gx-line-keep { display: flex; align-items: center; flex-wrap: wrap; gap: var(--seam, 12px); margin-top: var(--seam, 12px); }
.gx-line-keep [role="status"] { font: .875rem/1.45 var(--sans); }
.gx-line-keep .btn { min-height: 44px; }
.gx-ledger-more { border-block: 1px solid var(--rule-room); margin-block: var(--seam, 12px); }
.gx-ledger-more > summary { cursor: pointer; min-height: 44px; padding-block: var(--seam, 12px); font: .875rem var(--sans); }
.gx-ledger-more > summary:focus-visible { outline: 2px solid var(--carbon); outline-offset: 3px; }
.gx-earned-seals:empty { display: none; }
.gx-earned-seal { display: flex; align-items: center; gap: 12px; margin-block: 12px; }
.gx-earned-seal > span { flex: 0 0 48px; }
.gx-earned-seal svg { display: block; width: 48px; height: 48px; }
.gx-earned-seal p { margin: 0; font: .875rem/1.5 var(--sans); }


/* ===== 98-place.css ===== */
/* ============================================================================
   98-place.css — THE LESSON ROOM BECOMES SIXTEEN ROOMS.

   The sheet ui/place.js spends. Read that file first: it explains where the
   room comes from (the scene's own weather, already authored per instalment
   in ui/atmos.js), why nothing here is drawn, and what is allowed to move.

   Numbered 98 for the usual reason — the arcade sheet (90) states the lesson
   room's ground with `!important`, so anything that means to change it has to
   sort after 90 AND carry the same weight. Two of the three things this sheet
   fixes were invisible for exactly that reason, and both had shipped:

     · 90 sets `.gx-stage { background-color: … !important }` to a flat cold
       #f4f7fb grid, 24px squares, identical under all eighty-nine Russian
       chapters and all eight German tales, in both rooms, at every hour. It
       is also the one cold ground in a product whose palette law is «warm
       paper neutrals, never a pure-white theme».
     · 97 §3 gave the room the scene's mood as a wash — and 90's
       `box-shadow: … !important` had already taken the whole property, so the
       wash never rendered once. Measured: setting data-mood on the stage
       changed no computed style at all.

   WHAT IS KEPT. The arcade voice is not being undone. The hard 3px carbon
   rule around the sheet stays, the flat offset cast stays — it just stops
   being nailed 8px down-right on every screen and starts falling away from
   wherever the light in this particular room actually is. The squared paper
   stays too, but as ONE ground among sixteen, on the scenes that are actually
   a schoolroom or a copybook, instead of as the floor of the world.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   §1 · THE GROUND.

   Four layers, built once per scene and moved for ever after. Back to front:
   the far wash, the air's own signature, the light, the corners. Only the
   middle two are ever touched per frame, and only with transform and opacity.
   --------------------------------------------------------------------------- */

.gx-stage[data-place] {
  /* ---- THE TWO LIGHTS, AND WHY THEY ARE NOT ONE ----------------------

     The first cut of this sheet mixed a warm pole and a cold one into a
     single `--pl-glow` and it was wrong in a way worth writing down, because
     it is the default mistake with `color-mix`:

     WARM AND COLD ARE OPPOSITE HUES, AND THE ROAD BETWEEN THEM RUNS THROUGH
     GREY. Measured, rasterised to a pixel: #f4b962 mixed 34% with #a8bedd —
     the platform's own warmth — came out `195,190,185`. Dead neutral. Every
     room whose light was neither a fire nor a snowfield got no colour at all,
     which is most of the shelf, and it is exactly the "weird light effect on
     a sheet of paper" this whole sheet exists to answer.

     So the two lights are COMPOSITED, never interpolated: both are always
     present and warmth decides how much of each is turned up. Nothing ever
     passes through grey, and it is also the truer picture — a room with a
     fire in it and a window in it has both, and the hour decides which one
     you are reading by. */
  --pl-hot:  #f6b257;   /* tallow, a stove, the low sun */
  /* the cool light lost a third of its chroma (was #8fb3e2). At 34% over the
     whole reach of a room it was not «snow-light», it was a periwinkle field —
     and with most of the shelf inked in blues, every room drowned in it. */
  --pl-cool: #a8bdd8;   /* snow-light, a north window, dusk */
  /* every ground is the BOOK's pigment on the house's paper. The room changes
     the STRENGTH, never the hue: six books, six grounds, and «Шинель» is
     Prussian blue in the empty square exactly as it is on its own page. */
  --pl-ink: var(--ink, var(--carbon));
  /* THE DEPTH IS THE WHOLE RANGE, and the first cut spent almost none of it.
     At `9% + deep * 30%` the darkest room in the product — the empty square,
     deep .62 — came out at 28% ink over cream, which is a pale blue, and the
     lightest — a June afternoon at .04 — came out at 10%. Sixteen rooms
     inside eleven points of ink is not sixteen rooms. The square is now
     properly dark and «Спать хочется»'s last instalment, where the light goes
     out of the room entirely, is darker still — while the summer scenes are
     barely tinted at all. The SHEET is opaque and unaffected: nothing here
     ever touches the contrast the question is read at. */
  /* KEPT QUIET. At `7% + deep * 64%` the deep rooms reached 60% of the book's
     pigment and the ground stopped being paper — it became a coloured field
     with a sheet on it, which reads as an interface skin rather than a place.
     Halved. The DEPTH now lives in the corners and in the light, which is
     where depth belongs; the ground itself stays close to the house's own
     warm paper and only leans the way the book leans. */
  /* ---- THE PAPER UNDER THE ROOM (2026-08-25, «the backgrounds are always
     super annoying colors»). The floor used to mix the book's ink into
     `--room`, which in the light theme is #f7f9ff — a COLD blue-white. The
     shelf's inks are almost all blues, so blue into blue-white made every
     one of the sixteen rooms the same washed periwinkle: sixteen scenes,
     one annoying colour. The house's own palette law says warm paper
     neutrals, and the SHEET (#fffdf5) already keeps it — the ground now
     stands on the same warm stock, so the ink reads as ink on paper rather
     than as a tinted screen. In the warm theme --room is already paper and
     the mix changes almost nothing; the dark theme keeps its own formula
     below. */
  --pl-paper: color-mix(in srgb, var(--room) 30%, #f4eddc);
  /* ---- NIGHT FALLS ON A CURVE, NOT A SLOPE (2026-08-25). With one linear
     term the choice was between a skin (deep*64%) and sixteen rooms inside
     a dozen points of ink (deep*26% — measured: the starlit sky, the dying
     room, fog and a fire all rasterised as near-identical pale grey). So
     two terms: the linear one keeps the whole daytime shelf on quiet paper,
     and `--pl-night` — zero until a room passes half-deep, written by
     place.js — takes the truly dark rooms the rest of the way. A June
     afternoon stays at ~6% ink; «Спать хочется»'s last instalment now
     stands at ~44%, and the difference between them is the point. */
  --pl-floor: color-mix(in srgb, var(--pl-ink)
    calc(4% + var(--pl-deep, .2) * 22% + var(--pl-night, 0) * 34%), var(--pl-paper));
  /* defaults, so a room that never stood up is still a warm room and not a
     stack of unresolved calc() */
  --pl-lx: 50%; --pl-ly: 30%; --pl-warm: .5; --pl-reach: 100%;
  --pl-deep: .2; --pl-night: 0; --pl-rise: 20%; --pl-gust: .2;
  --pl-w: 0; --pl-hx: 0; --pl-hy: 0; --pl-lift: 0; --pl-press: 0;
  --pl-skew: 0deg; --pl-drift: 26s; --pl-grain: .8; --pl-off: 50%;
  /* what the AIR is tinted with — the room's dominant light, picked by a
     threshold rather than a blend, for the same reason the two lights are
     composited: a signature the colour of nothing is a signature nobody
     sees. Above half warm the air carries the fire; below it, the window. */
  --pl-lit: var(--pl-cool);
}

/* THE FLAT GRID GOES. Both halves of it: the cold #f4f7fb and the 24px blue
   rule.

   THE ATTRIBUTE IS LOAD-BEARING, not decoration. Among `!important`
   declarations specificity still decides, and 90 states four of these at
   (0,2,0) — `.gx-stage[data-move="door"]`, `[data-mood="focus"]`,
   `[data-mood="steady"]` and the dark-room pair. A plain `.gx-stage` here is
   (0,1,0) and loses to every one of them however late this sheet sorts, which
   is how the door's arrival light came to be lit over the top of all twenty
   minutes of a door-move lesson. `[data-place]` is written by place.js the
   moment the room stands up, ties those rules on specificity, and wins on
   order — and it is only ever present when there IS a room to win for. */
.gx-stage[data-place] {
  background-color: var(--pl-floor) !important;
  background-image: none !important;
  box-shadow: none !important;
}
[data-theme="dark"] .gx-stage[data-place] {
  --pl-floor: color-mix(in srgb, var(--pl-ink)
    calc(6% + var(--pl-deep, .2) * 22% + var(--pl-night, 0) * 30%), var(--room));
  background-color: var(--pl-floor) !important;
  box-shadow: none !important;
}

/* ---- THE TOOTH. Sixteen rooms built out of nothing but gradients read as
   sixteen gradients, however well chosen — a real ground has a SURFACE, and
   the eye finds its absence long before it can say what is missing. One 140px
   turbulence tile, inlined (this product ships as a single file and cannot
   make a second request), laid over everything at a few per cent. It is the
   same grain the paper stock has everywhere else in the house; here it is
   what stops the light being a screen effect. `--pl-grain` is seeded off the
   chapter, so the tooth is not identical from one scene to the next. ---- */
.gx-stage .pl::before {
  content: ''; position: absolute; inset: 0; display: block;
  z-index: 5; pointer-events: none;
  opacity: calc(.055 * var(--pl-grain, .8));
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
[data-theme="dark"] .gx-stage .pl::before { opacity: calc(.085 * var(--pl-grain, .8)); }

.gx-stage .pl {
  position: fixed; inset: 0;
  z-index: 0; pointer-events: none;
  overflow: hidden;
  /* the ground is under the work and over nothing: the sheet, the rail and
     the companion all sit above it, and it never takes a click */
  contain: strict;
}
.gx-stage .pl > i {
  position: absolute; inset: 0;
  display: block;
}
/* ONLY WHAT MOVES MAY OVERHANG, and this cost the first cut of the sheet
   every dark corner it had. A blanket `inset: -12%` put each layer's outer
   12% outside the visible box — which is exactly where a vignette keeps its
   darkest ring and where a horizon keeps the ground. Measured on a contact
   sheet of all sixteen rooms: the empty square (deep .62) and the dying light
   (deep .80) rendered at the same value as a June afternoon, because both
   their corner stops were off-screen. The two static layers now sit flush;
   the two that are translated every frame overhang by more than they can ever
   travel, so no edge is ever exposed. */
.gx-stage .pl-air  { inset: -16%; will-change: transform; }
.gx-stage .pl-lamp { inset: -10%; will-change: transform, opacity; }
.gx-stage .pl-deep { will-change: opacity; }

/* ---- THE FAR WASH. The floor this room stands on, and the distance behind
   it. Two stops: the ground rising to whatever height this place keeps its
   horizon, and — over it — the ink of the book pooling in the corners. A room
   with rise 0 (fog) has no horizon at all, which is exactly what fog is. ---- */
.gx-stage .pl-sky {
  /* the ground was 54% ink at the bottom edge with a short throw, which on a
     wide screen rendered as a hard horizontal BAND across the window — a
     stripe, not a floor. Quieter, and it fades over twice the run, so the
     eye finds ground and never finds a line. */
  background:
    linear-gradient(to top,
      color-mix(in srgb, var(--pl-ink) 34%, transparent) 0%,
      color-mix(in srgb, var(--pl-ink) 12%, transparent) calc(var(--pl-rise) * .9),
      transparent calc(var(--pl-rise) * 2.4));
}

/* ---- THE LIGHT. Two sources, always both present, and warmth decides which
   one this room is read by. They are composited over the wash, never mixed
   into it — see the note at the top of this sheet. The pool leans toward the
   reader's hand and opens as the run grows. ---- */
.gx-stage .pl-lamp { transition: opacity 1.6s var(--settle, ease); }
.gx-stage .pl-lamp::before,
.gx-stage .pl-lamp::after {
  content: ''; position: absolute; inset: 0; display: block;
}
.gx-stage .pl-lamp::before {
  background: radial-gradient(ellipse var(--pl-reach) calc(var(--pl-reach) * .78)
    at var(--pl-lx) var(--pl-ly),
    color-mix(in srgb, var(--pl-hot) 38%, transparent) 0%,
    color-mix(in srgb, var(--pl-hot) 16%, transparent) 40%,
    transparent 74%);
  opacity: var(--pl-warm);
}
.gx-stage .pl-lamp::after {
  background: radial-gradient(ellipse var(--pl-reach) calc(var(--pl-reach) * .78)
    at var(--pl-lx) var(--pl-ly),
    color-mix(in srgb, var(--pl-cool) 34%, transparent) 0%,
    color-mix(in srgb, var(--pl-cool) 14%, transparent) 40%,
    transparent 74%);
  opacity: calc(1 - var(--pl-warm));
}
[data-theme="dark"] .gx-stage .pl-lamp::before { opacity: calc(var(--pl-warm) * .58); }
[data-theme="dark"] .gx-stage .pl-lamp::after { opacity: calc((1 - var(--pl-warm)) * .5); }

/* ---- THE CORNERS. How much room there is not. It opens as the run grows and
   never closes: a room that darkens when the reader misses is punishment
   whatever it is called, and the kill list forbids it. ---- */
.gx-stage .pl-deep {
  background: radial-gradient(ellipse 116% 96% at 50% 46%,
    transparent 18%,
    color-mix(in srgb, var(--pl-ink) calc(var(--pl-deep) * 52%), transparent) 58%,
    color-mix(in srgb, var(--pl-ink) calc(var(--pl-deep) * 118%), transparent) 100%);
  transition: opacity 2.2s var(--settle, ease);
}
[data-theme="dark"] .gx-stage .pl-deep {
  background: radial-gradient(ellipse 116% 96% at 50% 46%,
    transparent 20%,
    color-mix(in srgb, #000 calc(var(--pl-deep) * 55%), transparent) 66%,
    color-mix(in srgb, #000 calc(var(--pl-deep) * 96%), transparent) 100%);
}

/* ---------------------------------------------------------------------------
   §2 · THE AIR'S SIGNATURES.

   The one thing that makes a room itself rather than a gradient with the
   right colour in it. Nine of them, one per `data-air`, and every one is the
   same shape of trick: the LAYER carries the wind (place.js writes its
   transform each frame) and its two pseudo-elements carry their own slower
   clock. The nesting is the physics — exactly as in ui/atmos.js, where the
   fall, the sway and the tumble are three nested elements and never three
   terms of one sum.

   Every clock here is `--pl-drift`, which is seeded off the chapter, so no
   two chapters breathe on the same beat.
   --------------------------------------------------------------------------- */

.gx-stage .pl-air { will-change: transform; }
.gx-stage .pl-air::before,
.gx-stage .pl-air::after {
  content: ''; position: absolute; inset: 0; display: block;
}
/* the still rooms have air and no signature in it, which is most of Chekhov:
   an office, a sickroom, a hall at noon */
.gx-stage[data-air="still"] .pl-air::before,
.gx-stage[data-air="still"] .pl-air::after { content: none; }

/* ---- BAND. The platform, and the seafront. Vapour lies at chest height and
   TRAVELS — the one thing everybody who has stood on a station remembers. Two
   bands at different heights and different speeds, because one band is a
   stripe and two are a distance. ---- */
.gx-stage[data-air="band"] .pl-air::before {
  background: linear-gradient(to bottom, transparent 28%,
    color-mix(in oklab, var(--pl-lit) 14%, transparent) 42%,
    color-mix(in oklab, var(--pl-lit) 26%, transparent) 50%,
    transparent 64%);
  filter: blur(14px);
  opacity: .72;
  animation: pl-band var(--pl-drift) linear infinite;
}
.gx-stage[data-air="band"] .pl-air::after {
  background: linear-gradient(to bottom, transparent 52%,
    color-mix(in oklab, var(--pl-lit) 22%, transparent) 66%,
    transparent 80%);
  filter: blur(24px);
  opacity: .5;
  animation: pl-band calc(var(--pl-drift) * 1.7) linear infinite reverse;
}
@keyframes pl-band {
  from { transform: translateX(-22%) scaleX(1.4); }
  to   { transform: translateX(22%) scaleX(1.4); }
}

/* ---- SCOUR. Wind across open ground: the empty square, and a winter field.
   Not a band — a RAKE, drawn at the wind's own angle and travelling along it,
   which is why it is skewed by the chapter's seed and nothing else here is. ---- */
.gx-stage[data-air="scour"] .pl-air::before {
  background: repeating-linear-gradient(var(--pl-skew),
    transparent 0 58px,
    color-mix(in oklab, var(--pl-lit) 30%, transparent) 58px 78px,
    transparent 78px 168px);
  filter: blur(9px);
  opacity: calc(.4 + var(--pl-gust) * .5);
  animation: pl-scour calc(var(--pl-drift) * .34) linear infinite;
}
.gx-stage[data-air="scour"] .pl-air::after {
  background: repeating-linear-gradient(calc(var(--pl-skew) + 5deg),
    transparent 0 132px,
    color-mix(in oklab, var(--pl-lit) 14%, transparent) 132px 168px,
    transparent 168px 360px);
  filter: blur(18px);
  opacity: .62;
  animation: pl-scour calc(var(--pl-drift) * .62) linear infinite;
}
@keyframes pl-scour {
  from { transform: translate3d(-14%, -6%, 0); }
  to   { transform: translate3d(14%, 6%, 0); }
}

/* ---- FLICKER. A fire, and what is left of one. The charter names flame as a
   thing that is allowed to move — «paper, ink, wax, flame» — and this is the
   only place in the room that smoulders. Two offset clocks so it never
   repeats visibly: a fire that pulses on one beat is a status light. ---- */
.gx-stage[data-air="flicker"] .pl-air::before {
  background: radial-gradient(ellipse 80% 55% at var(--pl-lx) var(--pl-ly),
    color-mix(in oklab, var(--pl-hot) 42%, transparent) 0%, transparent 70%);
  filter: blur(16px);
  animation: pl-flick calc(var(--pl-drift) * .13) ease-in-out infinite alternate;
}
.gx-stage[data-air="flicker"] .pl-air::after {
  background: radial-gradient(ellipse 55% 40% at var(--pl-lx) var(--pl-ly),
    color-mix(in oklab, var(--pl-hot) 32%, transparent) 0%, transparent 62%);
  filter: blur(26px);
  animation: pl-flick calc(var(--pl-drift) * .085) ease-in-out infinite alternate-reverse;
}
@keyframes pl-flick {
  from { opacity: .58; transform: scale(.97); }
  to   { opacity: 1;   transform: scale(1.04); }
}

/* ---- POOL. The desk after dark. It does one thing: it breathes, on the
   house's four-second tempo, and everything outside it is gone. ---- */
/* `circle 40%` IS NOT CSS — a circle's radius must be a length, and every
   engine drops the whole declaration. The lamp's pool, the one thing the
   desk-after-dark room is FOR, had been animating an invisible layer since
   the sheet was written. An ellipse takes percentages; at equal radii it is
   the same pool. The dapples below carried the identical mistake. */
.gx-stage[data-air="pool"] .pl-air::before {
  background: radial-gradient(ellipse 40% 34% at var(--pl-lx) var(--pl-ly),
    color-mix(in oklab, var(--pl-hot) 40%, transparent) 0%, transparent 66%);
  filter: blur(14px);
  animation: pl-breathe 4s ease-in-out infinite alternate;
}
.gx-stage[data-air="pool"] .pl-air::after { content: none; }
@keyframes pl-breathe {
  from { opacity: .74; transform: scale(.99); }
  to   { opacity: 1;   transform: scale(1.02); }
}

/* ---- DAPPLE. A canopy: the garden, and the autumn wood. Light coming
   through something that is itself moving, so the pattern ROCKS rather than
   travels — a branch swings, it does not migrate. ---- */
.gx-stage[data-air="dapple"] .pl-air::before {
  background:
    radial-gradient(ellipse 14% 14% at 22% 18%, color-mix(in oklab, var(--pl-lit) 23%, transparent) 0 40%, transparent 70%),
    radial-gradient(ellipse 11% 11% at 63% 12%, color-mix(in oklab, var(--pl-lit) 14%, transparent) 0 40%, transparent 70%),
    radial-gradient(ellipse 17% 17% at 84% 30%, color-mix(in oklab, var(--pl-lit) 23%, transparent) 0 40%, transparent 70%),
    radial-gradient(ellipse  9%  9% at 41% 34%, color-mix(in oklab, var(--pl-lit) 22%, transparent) 0 40%, transparent 70%);
  filter: blur(13px);
  opacity: .9;
  transform-origin: 50% -20%;
  animation: pl-rock calc(var(--pl-drift) * .28) ease-in-out infinite alternate;
}
.gx-stage[data-air="dapple"] .pl-air::after {
  background:
    radial-gradient(ellipse 20% 20% at 12% 46%, color-mix(in oklab, var(--pl-ink) 12%, transparent) 0 40%, transparent 70%),
    radial-gradient(ellipse 15% 15% at 72% 54%, color-mix(in oklab, var(--pl-ink) 10%, transparent) 0 40%, transparent 70%);
  filter: blur(22px);
  opacity: .8;
  transform-origin: 50% -30%;
  animation: pl-rock calc(var(--pl-drift) * .44) ease-in-out infinite alternate-reverse;
}
@keyframes pl-rock {
  from { transform: rotate(-1.1deg) translateX(-1.5%); }
  to   { transform: rotate(1.1deg) translateX(1.5%); }
}

/* ---- PANE. Rain is not a drop, it is a WINDOW — the wall of grey light a
   desk beside one has on a wet afternoon, with the water running down it.
   The rules are the glazing bars; the sheen is what is on the glass. ---- */
.gx-stage[data-air="pane"] .pl-air::before {
  background:
    linear-gradient(90deg, color-mix(in oklab, var(--pl-ink) 62%, transparent) 0 3px, transparent 3px 100%) 0 0 / 34% 100%,
    linear-gradient(color-mix(in oklab, var(--pl-ink) 62%, transparent) 0 3px, transparent 3px 100%) 0 0 / 100% 44%;
  opacity: .55;
  filter: blur(1px);
  animation: none;
}
.gx-stage[data-air="pane"] .pl-air::after {
  background: repeating-linear-gradient(179deg,
    transparent 0 14px,
    color-mix(in oklab, var(--pl-lit) 36%, transparent) 14px 17px,
    transparent 17px 46px);
  filter: blur(3px);
  opacity: .72;
  animation: pl-run calc(var(--pl-drift) * .16) linear infinite;
}
@keyframes pl-run {
  from { transform: translateY(-24%); }
  to   { transform: translateY(24%); }
}

/* ---- SHAFT. Daylight, with a real edge on it. It crosses the room over
   minutes: a bar of sun moves because the earth does, and anything faster
   reads as a searchlight. ---- */
.gx-stage[data-air="shaft"] .pl-air::before {
  background: linear-gradient(calc(74deg + var(--pl-skew)),
    transparent 30%,
    color-mix(in oklab, var(--pl-lit) 22%, transparent) 44%,
    color-mix(in oklab, var(--pl-lit) 26%, transparent) 50%,
    transparent 64%);
  filter: blur(10px);
  opacity: .85;
  animation: pl-cross calc(var(--pl-drift) * 9) linear infinite alternate;
}
.gx-stage[data-air="shaft"] .pl-air::after {
  background: linear-gradient(calc(74deg + var(--pl-skew)),
    transparent 46%,
    color-mix(in oklab, var(--pl-lit) 13%, transparent) 56%,
    transparent 68%);
  filter: blur(22px);
  opacity: .6;
  animation: pl-cross calc(var(--pl-drift) * 13) linear infinite alternate-reverse;
}
@keyframes pl-cross {
  from { transform: translateX(-11%); }
  to   { transform: translateX(11%); }
}

/* ---- BANK. Fog, which is the room going away. It has no horizon and no
   corners, so its signature EATS its own edges: two slow banks crossing in
   opposite directions, and where they overlap there is nothing left. ---- */
.gx-stage[data-air="bank"] .pl-air::before {
  background: radial-gradient(ellipse 70% 46% at 26% 58%,
    color-mix(in oklab, var(--room) 82%, var(--pl-lit)) 0%, transparent 72%);
  filter: blur(22px);
  animation: pl-bank calc(var(--pl-drift) * 1.4) ease-in-out infinite alternate;
}
.gx-stage[data-air="bank"] .pl-air::after {
  background: radial-gradient(ellipse 60% 40% at 76% 40%,
    color-mix(in oklab, var(--room) 76%, var(--pl-lit)) 0%, transparent 70%);
  filter: blur(30px);
  animation: pl-bank calc(var(--pl-drift) * 2.1) ease-in-out infinite alternate-reverse;
}
[data-theme="dark"] .gx-stage[data-air="bank"] .pl-air::before,
[data-theme="dark"] .gx-stage[data-air="bank"] .pl-air::after { opacity: .5; }
@keyframes pl-bank {
  from { transform: translate3d(-9%, 2%, 0) scale(1.05); opacity: .62; }
  to   { transform: translate3d(9%, -2%, 0) scale(1.14); opacity: 1; }
}

/* ---------------------------------------------------------------------------
   §3 · THE SQUARED PAPER, KEPT — AND PUT WHERE IT BELONGS.

   The arcade's grid was the best idea in the old ground and the worst place
   for it: тетрадь в клетку is a SCHOOLBOY's exercise book, which is a real
   and specific thing to be sitting at, and it was being used as the floor of
   a railway platform and a snowfield alike. It stays, warm now, on the two
   surfaces that actually are a copybook — the alphabet sprint and the
   courtesies, where the reader is a beginner doing letters — and nowhere
   else.
   --------------------------------------------------------------------------- */
.gx-stage[data-place][data-move="sprint"], .gx-stage[data-place][data-gram] {
  background-image:
    linear-gradient(90deg, color-mix(in oklab, var(--pl-ink) 8%, transparent) 1px, transparent 1px),
    linear-gradient(color-mix(in oklab, var(--pl-ink) 8%, transparent) 1px, transparent 1px) !important;
  background-size: 26px 26px !important;
}

/* ---------------------------------------------------------------------------
   §4 · THE SCENE'S MOOD, WHICH HAS NEVER ONCE RENDERED.

   97 §3 wrote these and 90's `box-shadow: … !important` ate the property
   whole. They are restated here at the weight it takes to actually land, and
   as a WASH on the ground rather than a shadow on the room — the same
   strengths as the reading page's, so a reader who moves from the lesson to
   the page does not feel the light change. It is one room in one book.
   --------------------------------------------------------------------------- */
.gx-stage .pl::after {
  content: ''; position: absolute; inset: 0; display: block;
  pointer-events: none;
  background: radial-gradient(ellipse 130% 110% at 50% 50%,
    transparent 30%, var(--pl-mood, transparent) 100%);
  transition: background 1400ms var(--settle, ease);
}
.gx-stage[data-mood="toska"], .gx-stage[data-mood="sad"] { --pl-mood: rgba(26, 21, 12, .15); }
.gx-stage[data-mood="wary"] { --pl-mood: rgba(26, 21, 12, .10); }
.gx-stage[data-mood="happy"], .gx-stage[data-mood="proud"] { --pl-mood: rgba(191, 146, 42, .08); }
[data-theme="dark"] .gx-stage[data-mood="toska"],
[data-theme="dark"] .gx-stage[data-mood="sad"] { --pl-mood: rgba(0, 0, 0, .30); }
[data-theme="dark"] .gx-stage[data-mood="wary"] { --pl-mood: rgba(0, 0, 0, .22); }

/* ---------------------------------------------------------------------------
   §5 · THE PAPER.

   A galley sheet lying on the room's boards, and from here on it behaves like
   one. It is HEAVY: six tenths of a degree at the worst gust in the product,
   and it lifts at its leading edge rather than tilting bodily, which is what
   a sheet in a draught actually does. It draws a little toward a hand near
   it. It takes the weight of an answer and settles, once, without bouncing.

   And its cast falls AWAY FROM THE LIGHT. This is the detail that does the
   most work for the least: the arcade's flat offset shadow is kept — it is a
   signature and it is good — but 8px down-and-right is a lamp bolted to the
   top-left corner of the universe, and every room here keeps its light
   somewhere else. Now the shadow is thrown by the light that is actually in
   the room, and it moves when the reader's hand moves the light.
   --------------------------------------------------------------------------- */
.gx-stage .gx {
  --gx-lean: 0deg; --gx-dx: 0px; --gx-dy: 0px;
  --gx-shx: 8px; --gx-shy: 8px; --gx-shb: 1;
  position: relative;
  z-index: 1;
}
/* the finish sheet is a ceremony and stands still for it: the seal is the one
   sanctioned overshoot in this product and nothing may lean under it */
.gx-stage:not([data-gram]) .gx:not(.gx-done):not(.gx-sealed) {
  /* the transform and the shadow are written straight onto the element by
     ui/place.js — neither is an inherited property, so neither dirties the
     sheet's subtree. These are the resting values, for the frame before the
     loop's first tick and for any reader whose loop never runs at all. */
  transform: none;
  box-shadow: 8px 8px 0 var(--play),
    19px 19px 30px -14px color-mix(in oklab, var(--pl-ink) 46%, transparent);
  /* the transform is written every frame by place.js and must NOT be eased —
     a transition on top of a per-frame write is a smoothing filter over a
     value that is already smoothed, and it lands as lag. The shadow may ease:
     it is only ever changed by the press, which is a state, not a stream. */
  transition: box-shadow 240ms var(--settle, ease);
}
@media (prefers-reduced-motion: reduce) {
  .gx-stage:not([data-gram]) .gx:not(.gx-done):not(.gx-sealed) { transform: none; }
}

/* ---------------------------------------------------------------------------
   §5b · AND THE LIGHT FALLS ON IT.

   This is the piece the first cut of the sheet was missing, and without it
   none of the rest could ever have worked. The room is 1440px wide and the
   sheet is 760 of them: the ground is a FRAME around the work, and most of
   what the reader is actually looking at — including, on a platform, the
   exact spot the light is standing — is covered by opaque paper. A room lit
   beautifully behind an opaque rectangle is a wallpaper.

   So the room's light falls across the PAPER as well, from the same position,
   and moves when the reader's hand moves it. It is the difference between a
   sheet with a picture behind it and a sheet lying in a lit room, and it is
   three or four per cent of white: enough to see the fall of it across the
   page, never enough to touch the contrast the question is read at. The
   measured ink of the type is not altered by a single step anywhere.

   The LEADING EDGE rides on the same pseudo-element. The wind reaches the
   paper before it reaches the desk, so the edge it arrives at lifts first and
   catches the light — one hairline, and it changes sides with the wind.
   --------------------------------------------------------------------------- */
/* THE LIGHT IS THE PAPER'S OWN SURFACE, not a layer laid over it.

   This was a `::before` at `z-index: 3`, then at `0`, and both were wrong in
   the same way. A positioned pseudo-element — at ANY z-index from 0 upward —
   paints above the sheet's non-positioned content. The 3→0 move rescued the
   question and the options, which are `position: relative` (26-games.css), and
   left every static thing on the sheet still under the wash. Measured in the
   night room with the light pulled onto the type, wash on → wash off:

     .gx-again        4.87:1 → 6.13:1
     .gx-margin-n     5.55:1 → 6.99:1
     .gx-margin-word  8.08:1 → 10.31:1

   Never below the floor, but a light that dims a fifth of the reading is not
   what «light falls on paper» means, and no z-index can fix it: the layer is
   in the wrong place in the paint order by construction.

   A BACKGROUND paints beneath ALL content, positioned or not, and that is
   exactly the fact being modelled — the fall of light is a property of the
   sheet's surface, not an object in the room. So it moves onto `.gx` itself.
   The arcade's `background: var(--sheet)` shorthand is respected: only the
   IMAGE is set here, so the sheet keeps its own colour underneath. */
.gx-stage:not([data-gram]) .gx:not(.gx-done) {
  background-image:
    radial-gradient(ellipse 150% 130%
      at calc(var(--pl-lx, 50%) + var(--pl-hx, 0) * 90%)
         calc(var(--pl-ly, 30%) + var(--pl-hy, 0) * 70%),
      rgba(255, 253, 245, .62) 0%,
      rgba(255, 253, 245, .18) 34%,
      transparent 62%),
    radial-gradient(ellipse 150% 130%
      at calc(var(--pl-lx, 50%) + var(--pl-hx, 0) * 90%)
         calc(var(--pl-ly, 30%) + var(--pl-hy, 0) * 70%),
      color-mix(in srgb, var(--pl-lit) 6%, transparent) 0%,
      transparent 58%),
    radial-gradient(ellipse 145% 125%
      at calc(100% - var(--pl-lx, 50%)) calc(100% - var(--pl-ly, 30%)),
      color-mix(in srgb, var(--pl-ink) 11%, transparent) 0%,
      transparent 60%);
}
/* at night the paper is the lit thing in a dark room, so the light only ADDS */
[data-theme="dark"] .gx-stage:not([data-gram]) .gx:not(.gx-done) {
  background-image:
    radial-gradient(ellipse 150% 130%
      at calc(var(--pl-lx, 50%) + var(--pl-hx, 0) * 90%)
         calc(var(--pl-ly, 30%) + var(--pl-hy, 0) * 70%),
      color-mix(in srgb, var(--pl-lit) 11%, transparent) 0%,
      transparent 64%);
}

/* ---- THE LEADING EDGE. The wind reaches the paper before it reaches the
   desk, so the edge it arrives at lifts first and catches the light. It rides
   on the sheet's OWN box-shadow rather than a pseudo-element, for the same
   reason the light does — an inset shadow on the element is drawn under its
   content, an inset shadow on a pseudo is drawn over it. ---- */

/* ---------------------------------------------------------------------------
   §6 · THE RUN, IN THE ROOM.

   The lesson already computes a run and already spends it on the ink band and
   on lifting the sky (ui/atmos.js, THE HEAT). The ROOM spends it too, in one
   direction only: as the run grows the corners open, the light warms and
   comes up, and the paper is held a little more proud of the boards.

   Nothing goes the other way. A miss returns the room to what the book says
   it is, which is where it started and where it was always going to be. The
   clearing is the reward; the scene's own weather is the neutral.
   --------------------------------------------------------------------------- */
/* `--pl-lift` is written on the STAGE by place.js and inherits down to the
   sheet. It must not be re-declared here: a `--pl-lift: 0` on `.gx` would
   shadow the inherited value and pin the run's whole expression at zero,
   which is the quietest possible way to ship a dead feature. */
.gx-stage:not([data-gram]) .gx:not(.gx-done):not(.gx-sealed) {
  /* THE LIFT IS IN THE TRANSFORM, NOT IN THE LAYOUT.

     This was `margin-top: calc(var(--pl-lift) * -3px)` with an 1800ms
     transition on it, and it is the reason the room shook after an answer.
     `--pl-lift` is rewritten by the loop every frame; margin is a LAYOUT
     property, so every frame reflowed the sheet AND restarted an 1800ms
     transition toward a target that had already moved. A transition fighting
     a per-frame value does not smooth it — it lags it, and the lag is visible
     as a stutter exactly when the run changes, which is on every answer.

     Nothing that changes per frame may touch layout. The lift is folded into
     the same transform the lean and the hand already ride (place.js writes
     `--gx-dy`), which is compositor-only: no reflow, no competing
     transition, and the same three-pixel rise. */
}

/* ---------------------------------------------------------------------------
   §7 · THE ROOM ARRIVES.

   A lesson opens on a place, and a place should come up like a light being
   found rather than a panel being switched on. Two seconds, decelerating, and
   only the ground does it — the question is legible from the first frame,
   which is the rule that outranks every other rule in this sheet.
   --------------------------------------------------------------------------- */
/* THE FADE BELONGS TO THE CONTAINER, NOT TO EACH LAYER — and putting it on
   the layers cost the room its entire response to the reader's run.

   A CSS animation with `fill: both` keeps applying its final value after it
   ends, and animations sit ABOVE inline styles in the cascade. `pl-arrive`
   ended at `opacity: 1` on every `.pl > i`, so `layers.deep.style.opacity`
   and `layers.lamp.style.opacity` — which place.js writes every frame, and
   which are the whole of how the corners open and the light comes up as the
   run grows — were silently discarded for the life of the lesson. Measured:
   `--pl-lift` climbed correctly to .77 at a run of nine while the corner
   layer sat at opacity 1 and did not move.

   One fade on `.pl` and the layers keep their own opacity. */
.gx-stage .pl { animation: pl-arrive 1800ms var(--settle, ease) both; }
@keyframes pl-arrive { from { opacity: 0; } to { opacity: 1; } }

/* the door beat is an arrival and keeps the light it always had, on top of
   the room rather than instead of it */
.gx-stage[data-move="door"] .pl-sky { filter: brightness(1.06); }

/* ---- THE HEADER WEARS THE BOOK'S OWN INK (2026-08-25, «the blue kind of
   doesn't match»). The room's floor is a TINT of the chapter's pigment, and
   the header band stayed house navy — a madder-rose room under a Prussian
   band, two colours that were never introduced. Monochromatic by
   construction now: the band is a SHADE of the same ink the floor is a tint
   of, so every room agrees with its own sheet by definition, in all six
   pigments of the case. Measured against the cream lettering: the weakest
   pairing (ochre) stands at 8.1:1 — AAA. The stamp's gold and the wax are
   untouched; they are the accents the law allots.

   NOT AS OVERRIDES BUT AS THE VARIABLES THEMSELVES. The first cut restyled
   the header band and the primary button by selector, and lost to `#gx-next`
   on specificity — and would have kept losing, piece by piece: the sheet's
   spine, the drop cap, the tick rail and the sheet's own cast shadow all
   speak `--play` too. So the stage redefines the two action variables once,
   scoped, and every consumer inside the room — band, buttons, borders,
   shadows — takes the book's shade with no rule fought anywhere. */
.gx-stage[data-place] {
  --play: color-mix(in oklab, var(--ink, #244177) 62%, #14120f);
  --play-deep: color-mix(in oklab, var(--ink, #17213a) 44%, #0d0c0a);
  /* the house's carbon is a BLUE-black (#17213a) — right everywhere else,
     but inside a room inked in madder or verdigris the sheet's own 3px rule
     read as one stray blue line, plainest along the bottom edge. The frame
     is the ink's own black instead: same weight, same law, no blue. */
  --frame: color-mix(in oklab, var(--ink, #17213a) 55%, #0d0c0a);
  /* and the navy's pale companion: every chip, wash and hard offset shadow
     inside the room that spoke #eef3fc — one icy blue hairline under every
     box, in rooms inked madder and verdigris. A tint of the ink on the
     house's paper instead, which is what a pressman's under-card is. */
  --play-tint: color-mix(in srgb, var(--ink, #244177) 11%, #faf6ec);
}
[data-theme="dark"] .gx-stage[data-place] {
  --play-tint: color-mix(in srgb, var(--ink, #244177) 26%, #14120f);
  /* AND THE FRAME, WHICH IS A DARK LINE FOR A LIGHT SHEET. --frame above is
     the masstone walked 55% toward #0d0c0a, and the reasoning holds exactly
     as it is written: on white paper the ink's own black is the honest rule,
     and the house's blue-black read as a stray blue line in a room inked
     madder or verdigris. But #0d0c0a is a LITERAL, so after dark the rule
     stayed black while the ground came up to meet it — the sheet's edge at
     1.33:1 and the sentence card's at 1.39:1, which is a frame a reader
     cannot see the sheet by. Reported from a reader's own screen.

     Same construction, same intent, the other end of the ladder: the ink's
     own LIGHT instead of the ink's own black, so the line still belongs to
     the book and still carries no blue. Measured on the night stage ground
     #232735, across the six masstones: 5.6–7.7:1 (lazur 5.71, zelen 6.40,
     bakan 5.88, sepia 6.23, grafit 5.64, okhra 7.68). That is firmer than
     the 3:1 an edge is held to, and deliberately so — in the light room this
     rule is a firm DARK line on white paper, and the mirror of a firm dark
     line is a firm light one. Each keeps its hue: #93a2b4, #9faf95, #c69893,
     #bba48c, #a0a096, #d1b78b — still lazur, still zelen, still madder. */
  --frame: color-mix(in oklab, var(--ink, var(--carbon)) 45%, var(--carbon));
}
.gx-stage[data-place] .gx-top {
  border-bottom-color: color-mix(in oklab, var(--ink, var(--carbon)) 40%, #14120f) !important;
}
.gx-stage[data-place] .gx { border-color: var(--frame) !important; }
.gx-stage[data-place] .gx-line.gx-study { border-color: var(--frame) !important; }
.gx-stage[data-place] :is(.gx-rail, .gx-rail i, .gx-chbar, .gx-chbar i, .gx-runn) {
  border-color: color-mix(in oklab, var(--ink, #17213a) 42%, #9a948a) !important;
}
/* the translation under the line sat in slate BLUE type on every book;
   it reads in the book's own quiet ink now */
.gx-stage[data-place] .gx-en { color: color-mix(in srgb, var(--ink, #46536f) 58%, #55524c); }
/* — but #55524c is a warm grey LITERAL, and a literal cannot follow the plate.
   The sheet under this line is var(--galley), which goes to #1c2a4c after
   dark; the gloss stayed exactly where it was and measured 1.5:1 there, on a
   line of body text. §11's remedy, and §11's measured 45%: lift the masstone
   toward the room's warm off-white in oklab, so madder still reads as madder.
   On the night galley the six pigments come back to 4.9–6.5:1. */
[data-theme="dark"] .gx-stage[data-place] .gx-en {
  color: color-mix(in oklab, var(--ink, var(--steel)) 45%, var(--carbon));
}

/* ---- THE FRONTISPIECE. The first thing of a lesson is the door card, and
   it used to open like a form: a bold label, a box, two buttons. A chapter
   in a printed book opens with a PLATE — the book's own ornament between two
   hairline rules, the title line centred, the stage direction beneath it in
   italic. Same elements, arranged the way a compositor would arrange them;
   unique of itself, because the ornament, the pigment and the direction all
   belong to this book and this room. ---- */
.gx-stage .gx-front {
  display: flex; align-items: center; gap: 16px;
  margin: 4px 0 -8px;
}
.gx-stage .gx-front i {
  flex: 1; height: 1px;
  background: color-mix(in srgb, var(--ink, var(--carbon)) 32%, transparent);
}
.gx-stage .gx-front b {
  font-weight: 400; font-size: 2rem; line-height: 1;
  color: var(--ink, var(--carbon));
}
.gx-stage[data-move="door"] .gx-mid[data-kind="gram"] .gx-ask { justify-content: center; }
.gx-stage[data-move="door"] .gx-mid[data-kind="gram"] .gx-direction { text-align: center; }

/* ---- AND THE NARRATIVE BEATS GET THE SAME PLATE. «Where you stood» and
   «The page, turned» are the two cards where the reader only reads — 97's
   law that reading wears no instrument stands, but no instrument had become
   NO COMPOSITION: the ask stranded top-left, the line stranded low, and a
   third of the card was void between them (reported, with a screenshot).
   The plate is not an instrument: ornament, centred label, the room's own
   stage direction, and the reading held in the card's middle — a page from
   the book, laid where a page sits. ---- */
.gx-stage .gx-mid[data-beat="ctx"],
.gx-stage .gx-mid[data-beat="rwl"] {
  display: flex; flex-direction: column;
}
.gx-stage .gx-mid[data-beat="ctx"] .gx-ask,
.gx-stage .gx-mid[data-beat="rwl"] .gx-ask { justify-content: center; }
.gx-stage .gx-mid[data-beat="ctx"] .gx-direction,
.gx-stage .gx-mid[data-beat="rwl"] .gx-direction { text-align: center; }
.gx-stage .gx-mid[data-beat="ctx"] .gx-body,
.gx-stage .gx-mid[data-beat="rwl"] .gx-body { margin-block: auto; }
.gx-stage .gx-mid[data-beat="ctx"] .gx-after,
.gx-stage .gx-mid[data-beat="rwl"] .gx-after { margin-top: auto; }
/* the reading IS the card, so it sits like a passage, not a table row:
   the book's own serif, a reading measure, room around it */
.gx-stage .gx-mid[data-beat="ctx"] .gx-ask,
.gx-stage .gx-mid[data-beat="rwl"] .gx-ask { flex-wrap: wrap; }
.gx-stage .gx-mid[data-beat="ctx"] .gx-rwl,
.gx-stage .gx-mid[data-beat="rwl"] .gx-rwl {
  max-width: 62ch;
  margin-inline: auto;
  padding-block: 10px;
}
.gx-stage .gx-mid[data-beat="ctx"] .gx-rwl-ru,
.gx-stage .gx-mid[data-beat="rwl"] .gx-rwl-ru {
  font-family: var(--serif, Georgia, serif);
  font-size: clamp(22px, 2.9vw, 30px);
  line-height: 1.55;
}

/* ---- THE STAGE DIRECTION, on the door card: where the scene stands, printed
   the way a playbill prints one — italic, quiet, in the book's own ink. It is
   derived in ui/course.js from the same weather ledger the room is, so the
   line and the light can never disagree. ---- */
.gx-stage .gx-direction {
  font-family: var(--serif, Georgia, serif);
  font-style: italic;
  font-size: .95em;
  line-height: 1.5;
  letter-spacing: .012em;
  /* 74% TOWARD TRANSPARENT BOUGHT NOTHING AND COST THE FLOOR. Over a
     near-white sheet, thinning a pigment toward transparent barely moves it —
     measured, okhra reads 4.49:1 at 74% and 4.49:1 opaque — so the alpha was
     spending contrast it was not actually buying quiet with.

     And okhra is simply the lightest of the six: on the lamp room's sheet it
     lands at 4.49 against a 4.5 floor, and on daylight's at 4.59. Two of the
     fifteen books («Смерть чиновника», «Спать хочется») therefore had their
     stage direction — which is prose, and held to the body floor — sitting on
     the line or just under it.

     Walked 15% toward the room's ink instead: 5.45 in the lamp room, 5.56 in
     daylight, and every other masstone between 7.3 and 11.2. Fifteen per cent
     is not a colour change anyone will see; it is the difference between
     passing and not. The dark room keeps its own lift (95-coherence §11c),
     which is more specific and unaffected. */
  color: color-mix(in oklab, var(--ink, var(--carbon)) 85%, var(--carbon));
  margin: -12px 0 4px;
}

/* ---------------------------------------------------------------------------
   §8 · WHAT STOPS.

   Every one of these is a real reader, not a hypothetical: the reduced-motion
   setting, the product's own calm tier, and the moment a decision is being
   made. The room does not disappear at any of them — a still room is still a
   room, and the light, the ground and the corners are all facts rather than
   animations. Only the moving parts stop.
   --------------------------------------------------------------------------- */
body.motion-low .gx-stage .pl-air::before,
body.motion-low .gx-stage .pl-air::after,
body.motion-low .gx-stage .pl { animation: none !important; opacity: 1 !important; }
@media (prefers-reduced-motion: reduce) {
  .gx-stage .pl-air::before,
  .gx-stage .pl-air::after,
  .gx-stage .pl { animation: none !important; opacity: 1 !important; }
}
/* A WORD CARD OPEN IS A DECISION BEING MADE, and the charter forbids
   characterful motion at friction. Marked by place.js rather than found with
   `:has()`: this product also ships as one file opened off a disk, sometimes
   by an old browser, and the same reasoning is written out in ui/atmos.js
   where the innermost particle carries a class for exactly this reason. */
.gx-stage.pl-hold .pl-air::before,
.gx-stage.pl-hold .pl-air::after { animation-play-state: paused; }

/* ============================================================================
   §9 · THE ROOM ON A BIG SCREEN.

   Tom, reading on a large monitor: the fonts are occasionally a bit too small;
   the transition is not fully smooth when the next card opens; and there is a
   great deal of background.

   Measured at three widths, and he is right on all three counts. The fluid
   root is `clamp(1rem, 0.7054rem + 0.4286vw, 1.1875rem)`, which reaches its
   19px ceiling at about 1800px and then stops — so 1920 and 2560 render type
   at IDENTICAL sizes (an option 24px, the ask line 22.42, the note 14.44).
   A 2560 monitor gets the 1800px lesson with more empty room around it. And
   the sheet is `min(880px, 96vw)`, so at 2560 it occupies a third of the
   width and the rest is ground.

   THE READING MEASURE LAW DOES NOT APPLY HERE. It governs running prose, and
   a lesson card is not prose: it is one word, one line of a book, and four
   options. Letting it grow past 880 on a wide desk costs nothing and stops
   the room looking like a phone screen dropped into a cinema.
   ========================================================================== */
/* THE LADDER IS NOT RESTATED HERE. The first cut re-declared the nine `--t-*`
   sizes inside the lesson room, and tests/scale.test.js refused it — the
   ladder is declared in exactly two files (head.html in px, 96-scale.css in
   rem) and a third declaration is how a type scale quietly forks. The guard
   was right, and it was also unnecessary: measured, the restatement moved
   almost nothing, because the surfaces a reader actually looks at are stated
   by the arcade sheet in units of their own. Those three are answered
   directly in §11. What is left here is the ROOM's own size, which everything
   set in `em` inherits, and the sheet's width. */
.gx-stage { --gx-w: min(880px, 96vw); }
@media (min-width: 1600px) {
  .gx-stage { font-size: 1.06rem; --gx-w: min(1000px, 92vw); }
  .gx-stage .gx { width: min(1000px, 92vw); }
}
@media (min-width: 2000px) {
  .gx-stage { font-size: 1.13rem; --gx-w: min(1120px, 88vw); }
  .gx-stage .gx { width: min(1120px, 88vw); }
}
@media (min-width: 2400px) {
  .gx-stage { font-size: 1.2rem; --gx-w: min(1220px, 84vw); }
  .gx-stage .gx { width: min(1220px, 84vw); }
}

/* ---------------------------------------------------------------------------
   §9b · THE RESIDENT STANDS IN THE MARGIN, NOT ON THE WORK.

   The medallion is `position: fixed` at `right: clamp(24px, 4.5vw, 64px)`
   (90-chromatic-game.css), which knows nothing about where the sheet's edge
   is. On any window without a generous margin the figure stood ON the paper —
   measured at 982px wide: square over the sheet's own «Again» button, a
   companion covering a control. On a question card the mate is a direct child
   of the stage (ui/place.js lifts it out of the leaning sheet), so the direct-
   child selector reaches exactly the cards where this matters and leaves the
   finish sheet's ceremony and the grammar room's own blocking alone.

   It now stands in the CENTRE of whatever right margin the window actually
   has — the same arithmetic the sheet is sized by — and when there is no
   margin to stand in, it leaves the room rather than standing on the desk.
   --------------------------------------------------------------------------- */
.gx-stage > .gx-mate {
  right: max(14px, calc((100vw - var(--gx-w)) / 4 - 56px));
}
/* the forge leans in close, but never wider than the margin it stands in */
.gx-stage[data-move="forge"] > .gx-mate {
  width: min(clamp(110px, 15vw, 180px), calc((100vw - var(--gx-w)) / 2 - 20px));
  height: min(clamp(110px, 15vw, 180px), calc((100vw - var(--gx-w)) / 2 - 20px));
}
@media (max-width: 1150px) {
  .gx-stage:not([data-gram]) > .gx-mate { display: none; }
}

/* ---------------------------------------------------------------------------
   §10 · THE SHEET STOPS RESIZING UNDER THE READER.

   Measured across ten consecutive cards: the sheet's height swung 193px at
   1440, 202px at 1920 and 134px at 2560, because each question shape is a
   different height and the sheet is sized by its contents. Every card that
   changed shape snapped the whole page — the options jumped, the companion
   jumped, and on a large screen the eye has to re-find the answers each time.
   That is the "not fully smooth when the next card opens".

   A floor under the question block absorbs almost all of it: the short shapes
   stop collapsing and the tall ones still grow when they genuinely must. The
   floor is in `em`, so it grows with the type above and holds on a phone. The
   room keeps ONE shape for a whole sitting, which is what a room is.
   --------------------------------------------------------------------------- */
/* THE FLOOR IS UNDER THE QUESTION BLOCK — EXCEPT WHERE THE BLOCK IS AN OBJECT.

   `.gx-mid` is the right place to absorb the swing: it is the thing whose
   height varies, and a floor there stopped 202px of jump at 1920. But it is
   not ONLY a layout box — on some cards it is the object itself. The rare
   pressman's proof (`data-rare="proof"`, 90-chromatic-game.css) is a dark
   plate drawn ON `.gx-mid`, so the floor stopped being white space and became
   a navy expanse with the tiles at the top of it and a hand's depth of dead
   plate below. Tom photographed it.

   Moving the floor up to `.gx` fixed the plate and gave the swing straight
   back — measured 193/184/127px, because a floor on the sheet only catches
   cards SHORTER than it and the tall ones set the swing anyway.

   So the floor stays where it works and steps aside for any card that paints
   its own ground. `[data-rare]` is the attribute those cards already carry;
   a future plate gets the same exemption by wearing the same mark. */
.gx-stage:not([data-gram]) .gx-mid:not([data-rare]) {
  min-height: 21em;
  display: flex;
  flex-direction: column;
}
/* the answers sit at the foot of that floor rather than in the middle of it,
   so they are in the SAME PLACE on every card — the one thing a reader builds
   muscle memory for */
.gx-stage:not([data-gram]) .gx-mid:not([data-rare]) > .gx-body { margin-top: auto; }
@media (max-width: 700px) {
  .gx-stage:not([data-gram]) .gx-mid:not([data-rare]) { min-height: 0; display: block; }
}

/* ---------------------------------------------------------------------------
   §11 · AND THE THREE THAT ANSWER TO SOMETHING ELSE.

   Restating the nine sizes inside the room moved a good deal but not the two
   surfaces a reader actually looks at, and DevTools says why: the arcade sheet
   states these three itself, in units that do not listen to the room —

     .gx-stage .gx-opt   clamp(20px, 2.5vw, 26px)   capped at 26px, for ever
     .gx-stage .gx-ask   1.18rem                    the ROOT, which stops at 19px
     .gx-stage .gx-again  .76rem                    likewise

   `rem` answers to the document root, and the fluid root reaches its ceiling
   at about 1800px. So on a 2560 monitor the ask line and the note render at
   exactly the sizes they take on a 1400 laptop.

   Restated in `em`, which answers to the ROOM. The option's clamp keeps its
   floor and loses its ceiling.
   --------------------------------------------------------------------------- */
@media (min-width: 1600px) {
  .gx-stage .gx-opt,
  .gx-stage .gx-body button { font-size: clamp(20px, 1.28em, 34px); }
  .gx-stage .gx-ask   { font-size: 1.18em; }
  .gx-stage .gx-again { font-size: .78em; }
  .gx-stage .gx-count,
  .gx-stage .gx-topic em { font-size: .82em; }
  /* the word being asked is the one thing on the card the reader is here for */
  .gx-stage .gx-head,
  .gx-stage .gx-word { font-size: clamp(28px, 2.1em, 62px); }
}

/* ============================================================================
   §12 · GOLD IS NOT A HOLE.

   26-games.css:33 draws every cloze blank in the sacred colour:
   `color: var(--signal-ink); border-bottom: 2px solid var(--signal)`. That is
   #f6c445 — the gold — used as a SEMANTIC marker for "a word is missing here",
   on gap, form and write, several times a minute, for the whole of a sitting.

   The palette law is one sentence long and this breaks it: gold is celebration
   and illumination ONLY, never semantic. Worse, it is spent against itself —
   the reward at the end of the card is drawn in the same colour as the hole at
   the start of it, so the one thing the reader earns arrives wearing the
   livery of the thing they were missing.

   A blank in a printed line is not gold. It is the RULE the compositor leaves
   when a sort has been lifted out of the forme — the book's own ink, at the
   weight of a rule.
   ========================================================================== */
.gx-stage .gx-blank {
  color: color-mix(in srgb, var(--ink, var(--carbon)) 62%, transparent);
  border-bottom: 2px solid color-mix(in srgb, var(--ink, var(--carbon)) 48%, transparent);
}
[data-theme="dark"] .gx-stage .gx-blank {
  color: color-mix(in srgb, var(--carbon) 55%, transparent);
  border-bottom-color: color-mix(in srgb, var(--carbon) 42%, transparent);
}
/* and when it is FILLED, the gold is earned and may be spent — that moment is
   the one this law exists to protect */
.gx-stage .gx-blank.gx-filled {
  color: var(--signal-ink);
  border-bottom-color: var(--signal);
}

/* ============================================================================
   §13 · TEN ACTS, AND SIX OF THEM WERE ONE BOARD.

   90-chromatic-game.css:1296 puts word, meaning, listen, stress, form and gap
   on the same `grid-template-columns: repeat(2, 1fr)`. Measured live, the
   computed value on every one of them is `408px 408px`. 26-games.css had
   given each its own silhouette; 90 sorts after it and flattened them, and 97
   — the only sheet later still — never restated them.

   So the room dresses each shape in a costume (a type case, a ruled line,
   concentric arcs) and then stands all of them on an identical 2×2 plate. The
   costume is doing the work of a wallpaper while the SHAPE of the thing the
   reader touches, which is what they actually navigate by, says «the same
   question again» six times out of ten.

   The silhouette is the difference between a set of exercises and a set of
   acts. Each one below is the shape of its own physical object.
   ========================================================================== */

/* ---- SINGLE SORTS, standing in the compositor's case. Four short words go
   four across; long ones fall to two. The row of sorts is the object. ---- */
.gx-stage .gx-mid[data-kind="word"] .gx-opts,
.gx-stage .gx-mid[data-kind="listen"] .gx-opts {
  grid-template-columns: repeat(auto-fit, minmax(9.5em, 1fr));
}

/* ---- THE WORD-CASE DRAWER. A meaning is a definition, and definitions are
   read down a column, not across a spread. One per line, the way a dictionary
   sets them — and it stops «and, but (conj.)» and «and (conj.)» sitting side
   by side pretending to be different answers. ---- */
.gx-stage .gx-mid[data-kind="meaning"] .gx-opts {
  grid-template-columns: 1fr;
  gap: 8px;
}
.gx-stage .gx-mid[data-kind="meaning"] .gx-opt { text-align: left; }

/* ---- PUTTING THE WORD BACK IN THE LINE. The candidates are sorts waiting at
   the side of the forme, not a menu: they sit in a row, tight, in the order a
   hand would reach for them. ---- */
.gx-stage .gx-mid[data-kind="gap"] .gx-opts {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 10px;
  grid-template-columns: none;
}
.gx-stage .gx-mid[data-kind="gap"] .gx-opt { flex: 0 1 auto; padding-inline: 1.4em; }

/* ---- WHICH ENDING. `form` was indistinguishable from `gap` — same board,
   same watermark, same blank — and it is a different question: not «which
   word» but «which ENDING». Three forms of one stem belong in one line, side
   by side, so the eye compares the tails. ---- */
.gx-stage .gx-mid[data-kind="form"] .gx-opts {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;
  grid-template-columns: none;
}
.gx-stage .gx-mid[data-kind="form"] .gx-opt { flex: 1 1 7em; max-width: 14em; }

/* ---- THE STRESS. Not options at all — the same word, several times, with
   the weight in a different place. They belong in one line so the reader hears
   the difference by looking along it. ---- */
.gx-stage .gx-mid[data-kind="stress"] .gx-opts {
  display: flex; flex-wrap: wrap; justify-content: center; gap: 14px;
  grid-template-columns: none;
}

/* the fold keeps its own counsel: below 620 everything is one column anyway,
   and 90 already says so */
@media (max-width: 620px) {
  .gx-stage .gx-mid[data-kind="word"] .gx-opts,
  .gx-stage .gx-mid[data-kind="listen"] .gx-opts,
  .gx-stage .gx-mid[data-kind="gap"] .gx-opts,
  .gx-stage .gx-mid[data-kind="form"] .gx-opts,
  .gx-stage .gx-mid[data-kind="stress"] .gx-opts {
    display: grid; grid-template-columns: 1fr;
  }
}

/* Browsing views inherit the shared solid ground. Keep their surfaces
   transparent and retire the full-window washes that tinted each section
   differently; story colour belongs to the book and its illustration. */
#v-review.show, #v-words.show, #v-journey.show, #v-course.show { background: none; }
#v-review.show::before, #v-words.show::before,
#v-journey.show::before, #v-course.show::before { content: none; }


/* ===== 99-experience.css ===== */
/* The journey surfaces share paper, ink, the resident and the printed seal.
   This final sheet owns navigation layout, not reading-page typography.
   Motion: a chosen stop lifts on hover/focus (interaction tier); no loop,
   no reward animation, and the same states remain in reduced motion. */

/* A desk with one lead, three useful doors and a record to open on purpose. */
#v-home.desk-focused > .masthead { box-shadow: 4px 4px 0 var(--rule-room); }
#v-home.desk-focused #room-name { font-size: clamp(32px, 4vw, 48px); max-width: 14ch; line-height: .98; }
#v-home.desk-focused #almanac { display: none; }
#v-home.desk-focused .desk-cameos { display: none; }
#desk-focus, #desk-account, #desk-details { max-width: var(--desk-w); margin: var(--breath) auto; }
#desk-account:empty { display: none; }
.desk-small-record { display: flex; flex-wrap: wrap; gap: var(--seam); margin-bottom: var(--seam); font: .875rem var(--sans); }
.desk-small-record span { padding-right: var(--seam); border-right: 1px solid var(--rule-room); }
.desk-small-record span:last-child { border: 0; }
.desk-discoveries { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--seam); }
.discovery { display: grid; grid-template-columns: 1fr auto; gap: var(--seam); text-align: left; padding: var(--gutter); color: var(--carbon); background: var(--paper); border: 1px solid var(--rule-room); border-bottom: 3px solid var(--carbon); cursor: pointer; }
.discovery-icon { font: 2rem var(--serif); grid-column: 1 / -1; }
.discovery-icon svg { width: 32px; height: 32px; }
.discovery strong { font: 1.25rem/1.2 var(--bill); grid-column: 1 / -1; }
.discovery > span:not(.discovery-icon) { font: .875rem/1.5 var(--sans); }
.discovery > i { align-self: end; font-style: normal; }
.discovery:hover { background: var(--paper-2); }
#desk-details { border-block: 1px solid var(--rule-room); }
#desk-details > summary, .course-explorer > summary { min-height: 44px; padding: var(--seam); cursor: pointer; font: 1.125rem var(--bill); }
#desk-details > summary span, .course-explorer > summary span { display: block; font: .875rem/1.5 var(--sans); margin-top: .4em; }
#desk-details > .desk-sect, #desk-details > .desk-spread { margin: var(--breath) 0; }
#desk-details #desk-hud { border: 1px solid var(--rule-room); }
#desk-details #standing-line { display: none !important; }
#desk-account .words-keep { margin: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--seam); border: 1px solid var(--rule-room); padding: var(--seam) var(--gutter); }
#desk-account .words-keep > p { margin: 0; }
#desk-account .wk-row { flex-shrink: 0; }

/* The trail is made of actual lesson doors. Read order and keyboard order
   follow the same path, including the folded earlier and later stretches. */
.course-trail { max-width: 760px; margin: var(--breath) auto; }
.lesson-preview { margin-block: var(--seam); }
.lesson-preview > summary { min-height: 44px; padding: var(--seam); font: var(--type-control)/1.4 var(--sans); cursor: pointer; white-space:normal; }
.trail-heading { max-width: 48ch; margin-bottom: var(--breath); }
.trail-heading h2 { font: clamp(1.6rem, 3vw, 2.4rem)/1.1 var(--bill); margin: .3em 0; }
.trail-heading p, .trail-destination p { font: 1rem/1.5 var(--sans); }
.learning-trail { list-style: none; padding: 0; margin: 0; }
.trail-stop { position: relative; padding: 0 0 var(--breath); margin-inline: var(--gutter) 0; }
.trail-stop:nth-child(even) { margin-inline: calc(var(--gutter) * 2) 0; }
.trail-stop:not(:last-child)::after { content: ''; position: absolute; top: 78px; left: 36px; height: var(--breath); border-left: 2px dashed var(--rule-room); transform: rotate(-25deg); transform-origin: top; }
.trail-stop:nth-child(even)::after { transform: rotate(25deg); }
.trail-go { display: grid; grid-template-columns: 76px minmax(0, 1fr) auto; gap: var(--seam); align-items: center; width: 100%; padding: var(--seam); border: 1px solid transparent; background: transparent; text-align: left; color: var(--carbon); cursor: pointer; }
.trail-medallion { width: 72px; height: 72px; display: grid; place-items: center; border: 2px solid var(--carbon); border-radius: 50%; background: var(--paper); box-shadow: 0 5px 0 var(--rule-room); font: 1.8rem var(--serif); }
.trail-medallion svg { width: 42px; height: 42px; }
.trail-copy { display: grid; gap: .25rem; font: var(--type-meta)/1.4 var(--sans); }
.trail-copy strong { font: 1.3rem/1.2 var(--bill); }
.trail-label { font-size: .75rem; letter-spacing: .04em; text-transform: uppercase; }
.trail-current .trail-go { background: var(--paper); border-color: var(--carbon); box-shadow: 4px 4px 0 var(--rule-room); }
.trail-current .trail-medallion { border-width: 4px; outline: 1px solid var(--carbon); outline-offset: 5px; }
.trail-done .trail-medallion { border-style: double; border-width: 5px; }
.trail-go:hover .trail-medallion { transform: translateY(-2px); }
.trail-go:focus-visible, .discovery:focus-visible { outline: 3px solid var(--carbon); outline-offset: 4px; }
.trail-arrow { font-size: 1.3rem; }
.trail-arrow svg, .discovery > i svg { width: 24px; height: 24px; }
.trail-destination > span svg, .word-empty > span svg { width: 48px; height: 48px; }
.trail-fold { margin-block: var(--seam); }
.trail-fold > summary { padding: var(--seam); min-height: 44px; font: var(--type-control)/1.4 var(--sans); cursor: pointer; white-space:normal; }
.trail-destination { padding: var(--gutter); margin-top: var(--breath); border: 1px solid var(--carbon); background: var(--paper); text-align: center; }
.trail-destination > span { display: block; font: 2.4rem var(--serif); margin-bottom: var(--seam); }
.trail-destination > strong { font: 1.5rem var(--bill); }
.course-explorer { border-block: 1px solid var(--rule-room); margin-block: var(--band); }
.course-explorer .syl { margin-top: var(--breath); }
#v-course .cs-wing-books .rd { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--gutter); padding-left: 0; }
#v-course .cs-wing-books .rd::before { display: none; }
#v-course .cs-wing-books .rd > li:not(.rd-book) { grid-column: 1 / -1; }
#v-course .cs-wing-books .rd-book { margin: 0; border: 1px solid var(--rule-room); border-top: 5px solid var(--ink, var(--carbon)); background: var(--paper); padding: var(--seam); min-width: 0; }
#v-course .cs-wing-books .rd-book.rd-open { grid-column: 1 / -1; }
#v-course .cs-wing-books .rd-book .rd-hit { padding: var(--seam); }
#v-course .cs-wing-books .rd-book .rd-next { display: none; }
#v-course .cs-wing-books .rd-book .rd-hit { display: grid; grid-template-columns: 84px minmax(0,1fr); align-items: center; gap: var(--seam); width: 100%; min-height: 200px; }
.rd-volume-art { position: relative; display: grid; place-items: center; width: 76px; min-height: 112px; padding: var(--seam); color: var(--ink,var(--carbon)); background: var(--paper); border: 1px solid currentColor; border-left: 5px double currentColor; border-radius: 0 10px 2px 0; box-shadow: 3px 3px 0 var(--rule-room); }
.rd-volume-art svg { width: 48px; height: 48px; }
.rd-volume-art b { font: .75rem var(--sans); margin-top: var(--seam); }
.rd-kind { display: inline-block; font: .75rem var(--sans); padding-bottom: .5em; }
.rd-ask-orn svg { width: 32px; height: 32px; }
#v-course .cs-wing-books .rd-book .rd-body { min-width: 0; }

/* Practice has an entrance, an exercise, and a completion. The entry does
   not change the scheduler or consume a word from the queue. */
#v-review.practice-lobby #rev-card, #v-review.practice-lobby #rev-btns,
#v-review.practice-lobby #rev-attr, #v-review.practice-lobby #rev-note,
#v-review.practice-lobby #sess-ring, #v-review.practice-lobby #review-guide,
#v-review.practice-lobby #btn-rev-exit { display: none; }
#v-review.practice-lobby #practice-position, #practice-position:empty { display: none; }
#practice-position { text-align: center; font: .875rem var(--sans); margin-block: var(--seam); }
.practice-instruction { font: var(--type-body)/1.5 var(--sans); margin-block: var(--seam); }
#practice-welcome { max-width: 640px; margin: var(--breath) auto; }
.practice-invitation { background: var(--paper); border: 1px solid var(--carbon); border-top: 4px solid var(--carbon); box-shadow: 4px 4px 0 var(--rule-room); padding: var(--gutter); text-align: center; }
.practice-portrait { width: 72px; margin: 0 auto var(--seam); }
.practice-portrait svg { width: 100%; height: auto; }
.practice-invitation h2 { font: clamp(1.7rem, 3vw, 2.4rem)/1.1 var(--bill); margin: .5em 0; }
.practice-invitation p { max-width: 46ch; margin: var(--seam) auto; font: 1rem/1.6 var(--sans); }
.practice-start-actions { display: flex; justify-content: center; flex-wrap: wrap; gap: var(--seam); margin-top: var(--breath); }
.practice-word-peek { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--seam); }
.practice-word-peek span { border-bottom: 1px solid var(--rule-room); padding: .4em; font: 1.125rem var(--serif); }
.practice-invitation .practice-promise { font-size: .875rem; }
.practice-mode-label { text-align: center; font: .875rem var(--sans); margin-block: var(--seam); }
#v-review.practice-empty .rev-dial, #v-review.practice-empty .practice-mode-label { display: none; }
.trail-heading h2, .practice-invitation h2, .discovery strong, .trail-copy strong, .trail-destination > strong { font-weight: 700; text-transform: none; }
#v-review .rev-dial { max-width: 640px; margin-inline: auto; }
#v-review .rev-dial button { min-height: 44px; white-space: normal; }
#v-review .rev-dial button[aria-pressed="true"] { outline: 2px solid var(--carbon); outline-offset: 2px; }
#v-review #rev-note:empty { display: none; }

/* The collection offers a real action on each saved word, with large,
   keyboard-operable audio and practice buttons. */
#v-review.practice-finished:not(.practice-lobby) #review-guide,
#v-review.practice-finished:not(.practice-lobby) .rev-dial,
#v-review.practice-finished:not(.practice-lobby) .practice-mode-label { display: none; }
.rev-up-next { margin-block: var(--seam); text-align: left; }
.rev-up-next summary { cursor: pointer; padding-block: var(--seam); }
.rev-up-next .ru-line { font-family: var(--serif); line-height: 1.6; }
#v-words { max-width: 1120px; margin-inline: auto; }
#v-words .room-head { text-align: left; }
#v-words .filters { flex-wrap: wrap; gap: var(--seam); }
#v-words .filters button { min-height: 44px; }
#v-words #wsearch { min-height: 44px; flex: 1 1 220px; }
#v-words #wordlist > li:not(.wl-cap):not(.word-empty) { display: grid; grid-template-columns: minmax(100px, .7fr) minmax(0, 1fr) auto auto; gap: var(--seam); align-items: center; padding: var(--seam); }
#v-words .wf { min-height: 44px; padding: 0; text-align: left; border: 0; background: transparent; cursor: pointer; color: inherit; font: var(--type-prompt)/1.4 var(--serif); min-width:0; overflow-wrap:anywhere; }
#v-words .word-revisit { min-height: 44px; padding: .5em .8em; border: 1px solid var(--carbon); background: var(--paper); color: var(--carbon); font: var(--type-control)/1.4 var(--sans); cursor: pointer; white-space:normal; }
#v-words .word-empty { display: block; text-align: center; padding: var(--gutter); border: 1px solid var(--rule-room); background: var(--paper); }
.word-empty > span { font-size: 2rem; }
.word-empty p { max-width: 44ch; margin: var(--seam) auto; }

@media (max-width: 820px) {
  #desk-account .words-keep { display: block; }
  #desk-account .wk-row { margin-top: var(--seam); }
  .discovery { padding: var(--seam); }
  #v-course .cs-wing-books .rd { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  #v-home.desk-focused #room-name { font-size: 32px; max-width: none; }
  .desk-discoveries { grid-template-columns: 1fr; }
  .discovery { grid-template-columns: 36px minmax(0, 1fr) auto; gap: .3rem var(--seam); }
  .discovery-icon { grid-column: 1; grid-row: 1 / 3; align-self: center; }
  .discovery strong { grid-column: 2; }
  .discovery > span:not(.discovery-icon) { grid-column: 2; }
  .discovery > i { grid-column: 3; grid-row: 1 / 3; align-self: center; }
  .trail-stop { margin-inline: 0; }
  .trail-stop:nth-child(even) { margin-inline: var(--seam) 0; }
  .trail-go { grid-template-columns: 56px minmax(0, 1fr) auto; padding: var(--seam) .3rem; }
  .trail-medallion { width: 48px; height: 48px; font-size: 1.4rem; }
  .trail-medallion svg { width: 30px; height: 30px; }
  .trail-stop:not(:last-child)::after { top: 68px; left: 27px; }
  .trail-copy strong { font-size: 1.125rem; }
  .practice-start-actions { flex-direction: column; }
  #v-words #wordlist > li:not(.wl-cap):not(.word-empty) { grid-template-columns: minmax(0, 1fr) auto; }
  #v-words .wg { grid-column: 1; grid-row: 2; }
  #v-words .word-revisit { grid-column: 2; grid-row: 1 / 3; }
  #v-words .warmbar { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .trail-go:hover .trail-medallion { transform: none; }
}
body.motion-low .trail-go:hover .trail-medallion { transform: none; }
#v-course .cs-wt-dev svg { width: 28px; height: 28px; }
#v-course .room-head:not([data-orn])::after { display: none; }

/* THE FRONT COVER. The only moving detail is the book's ink changing on a
   deliberate reveal (interaction tier). The resident is an authored static
   woodcut; a discovery never mints progress or runs an ambient animation. */
#v-home.arrival-active > :not(#arrival-front) { display: none !important; }
#arrival-front { display: grid; grid-template-columns: 1.1fr 1fr; gap: var(--band) var(--gutter); align-items: center; max-width: 1120px; margin: var(--breath) auto; color: var(--carbon); }
.arrival-eyebrow, .arrival-page-label { font: .75rem/1.5 var(--sans); text-transform: uppercase; letter-spacing: .12em; }
#arrival-title { font: 700 clamp(2.5rem, 4.5vw, 4.5rem)/1.04 var(--bill); letter-spacing: -.02em; text-transform: none; margin: var(--seam) 0; }
#arrival-title em { display: inline-block; font-family: var(--serif); font-size: .78em; font-weight: 400; line-height: 1.15; }
.arrival-lede { max-width: 39ch; font: 1.125rem/1.6 var(--sans); margin-block: var(--breath); }
.arrival-choices { display: grid; gap: var(--seam); }
.arrival-choice { display: grid; grid-template-columns: 32px minmax(0,1fr) 24px; gap: var(--seam); align-items: center; padding: var(--seam); min-height: 76px; border: 1px solid var(--carbon); color: var(--carbon); background: var(--paper); text-align: left; cursor: pointer; box-shadow: 3px 3px 0 var(--rule-room); }
.arrival-choice svg { width: 100%; height: 30px; }
.arrival-choice b, .arrival-choice small { display: block; }
.arrival-choice b { font: 700 1.125rem/1.3 var(--sans); }
.arrival-choice small { font: .875rem/1.5 var(--sans); margin-top: .2em; }
.arrival-primary { background: var(--carbon); color: var(--paper); }
.arrival-choice:hover { box-shadow: 5px 5px 0 var(--rule-room); }
.arrival-free, .arrival-rooms { font: .875rem/1.5 var(--sans); margin-top: var(--seam); }
.arrival-rooms a { color: inherit; text-decoration: underline; text-underline-offset: 3px; }
.arrival-book { position: relative; isolation: isolate; background: var(--paper); border: 1px solid var(--carbon); border-left: 10px double var(--carbon); border-radius: 2px 28px 28px 2px; padding: var(--gutter); padding-top: var(--breath); min-height: 390px; box-shadow: 6px 6px 0 var(--paper-2), 7px 7px 0 var(--carbon), 12px 12px 0 var(--paper-2), 13px 13px 0 var(--carbon); transform: rotate(2deg); }
.arrival-book-ribbon { position: absolute; right: 30px; top: -1px; width: 30px; height: 52px; background: var(--carbon); clip-path: polygon(0 0,100% 0,100% 100%,50% 77%,0 100%); }
.arrival-resident { width: 100px; height: 100px; margin-bottom: var(--seam); }
.arrival-resident svg { width: 100%; height: 100%; }
.arrival-book blockquote { margin: var(--breath) 0; font: clamp(1.7rem, 3vw, 2.6rem)/1.3 var(--serif); }
.arrival-reveal { display: flex; align-items: center; gap: var(--seam); min-height: 44px; padding: .4em 0; color: var(--carbon); background: transparent; border: 0; border-bottom: 1px solid var(--carbon); cursor: pointer; font: .875rem var(--sans); }
.arrival-reveal svg { width: 24px; height: 24px; }
#arrival-meaning { margin-block: var(--seam); border-left: 3px solid var(--carbon); padding-left: var(--seam); }
#arrival-meaning p { font: 1.25rem/1.5 var(--serif); margin: 0; }
#arrival-meaning span { display: block; font: .875rem/1.5 var(--sans); margin-top: var(--seam); }
.arrival-page-foot { display: flex; align-items: center; gap: var(--seam); margin-top: var(--breath); font: .75rem/1.5 var(--sans); }
.arrival-page-foot svg { width: 40px; height: 40px; }
.arrival-method { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: var(--gutter); list-style: none; margin: 0; padding: var(--gutter) 0; border-top: 1px solid var(--rule-room); }
.arrival-method li { display: flex; align-items: start; gap: var(--seam); }
.arrival-method svg { width: 36px; height: 36px; flex-shrink: 0; }
.arrival-method strong, .arrival-method span { display: block; }
.arrival-method strong { font: 700 1rem/1.4 var(--sans); }
.arrival-method span { font: .875rem/1.5 var(--sans); margin-top: .4em; }

/* THE DISCOVERY BOOK. Each real lesson is a leaf. Folios follow DOM order;
   the thread joins the two-page spreads, never a second progress system. */
.course-trail { max-width: 1000px; }
#v-course > .masthead { display: flex; align-items: center; gap: var(--seam); padding-block: 0; min-height: 0; }
#v-course > .masthead .rh-ru, #v-course > .masthead .cs-standing { display: none; }
#v-course > .masthead .rh-device { position: static; margin: 0; }
#v-course > .masthead h1 { margin: 0; font-size: 2rem; }
.trail-heading { display: flex; align-items: center; gap: var(--gutter); max-width: none; }
.trail-heading > div { max-width: 560px; }
.trail-heading h2 { font-size: clamp(2rem, 4vw, 3rem); line-height: 1.05; }
.trail-heading-art { margin-left: auto; }
.trail-heading-art svg { width: 110px; height: 110px; transform: rotate(-9deg); }
.learning-trail { position: relative; display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--gutter); padding: var(--seam) 0; max-width: none; }
.learning-trail::before { content: ''; position: absolute; top: var(--breath); bottom: var(--breath); left: 50%; border-left: 2px dashed var(--rule-room); }
.trail-stop, .trail-stop:nth-child(even) { position: relative; margin: 0; min-width: 0; }
.trail-stop:nth-child(even) { padding-top: var(--breath); }
.trail-stop:not(:last-child)::after { display: none; }
.trail-go { position: relative; height: 100%; min-height: 240px; display: grid; grid-template-columns: 90px minmax(0,1fr) 24px; align-items: center; gap: var(--seam); padding: var(--gutter); background: var(--paper); border: 1px solid var(--carbon); border-left: 6px double var(--carbon); border-radius: 0 24px 3px 0; box-shadow: 4px 4px 0 var(--paper-2),5px 5px 0 var(--rule-room); }
.trail-go::before { content: ''; position: absolute; inset: var(--seam); border-bottom: 1px solid var(--rule-room); pointer-events: none; }
.trail-folio { position: absolute; top: var(--seam); right: var(--seam); font: .875rem var(--serif); color: var(--muted, var(--carbon)); }
.trail-medallion { width: 84px; height: 84px; border: 0; border-radius: 0; background: transparent; box-shadow: none; }
.trail-medallion svg { width: 76px; height: 76px; }
.trail-copy strong { display: block; margin-block: var(--seam); font-size: 1.5rem; line-height: 1.15; }
.trail-copy > span:last-child { font: .875rem/1.5 var(--sans); }
.trail-label { font-size: .6875rem; letter-spacing: .08em; }
.trail-current .trail-go { border-top: 5px solid var(--carbon); background: var(--paper); }
.trail-current .trail-medallion { outline: 0; border: 0; }
.trail-current .trail-label { display: inline-block; color: var(--paper); background: var(--carbon); padding: .35em .6em; }
.trail-done .trail-medallion { border: 3px double var(--carbon); border-radius: 50%; width: 64px; height: 64px; }
.trail-done .trail-medallion svg { width: 42px; height: 42px; }
.trail-go { transition: box-shadow 160ms ease-out; }
.trail-go:hover, .trail-go:focus-visible { box-shadow: 6px 6px 0 var(--paper-2),7px 7px 0 var(--carbon); }
.arrival-discovered #arrival-meaning { animation: discovery-ink 180ms ease-out; }
@keyframes discovery-ink { from { opacity: .3; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
  .trail-go { transition: none; }
  .arrival-discovered #arrival-meaning { animation: none; }
}
body.motion-low .trail-go { transition: none; }
body.motion-low .arrival-discovered #arrival-meaning { animation: none; }
.trail-destination { display: grid; grid-template-columns: 72px minmax(0,1fr); gap: var(--seam) var(--gutter); text-align: left; border-left: 8px double var(--carbon); }
.trail-destination > span { grid-row: 1 / 4; margin: 0; align-self: center; }
.trail-destination > p { margin: 0; }
.trail-destination > button { justify-self: start; }
.arrival-choice:focus-visible, .arrival-reveal:focus-visible { outline: 3px solid var(--carbon); outline-offset: 5px; }

@media (max-width: 820px) {
  #arrival-front { grid-template-columns: 1fr; gap: var(--breath); }
  .arrival-book { transform: none; max-width: 640px; width: 100%; justify-self: center; min-height: 0; }
  .arrival-copy { max-width: 640px; justify-self: center; }
  .arrival-method { grid-template-columns: 1fr; }
  .trail-go { grid-template-columns: 60px minmax(0,1fr); min-height: 230px; }
  .trail-arrow { display: none; }
  .trail-medallion, .trail-medallion svg { width: 56px; height: 56px; }
}
@media (max-width: 560px) {
  #arrival-front { margin-top: var(--seam); }
  #arrival-front .arrival-copy { display: contents; }
  #arrival-front .arrival-eyebrow { order: 0; margin: 0; }
  #arrival-title { order: 1; }
  #arrival-front .arrival-lede { order: 2; }
  #arrival-front .arrival-book { order: 3; }
  #arrival-front .arrival-choices { order: 4; }
  #arrival-front .arrival-free { order: 5; }
  #arrival-front .arrival-rooms { order: 6; }
  #arrival-front .arrival-method { order: 7; }
  #arrival-title { font-size: 2.5rem; }
  .arrival-lede { margin-block: var(--seam); font-size: 1rem; }
  .arrival-book { padding: var(--seam) var(--gutter); margin-right: 8px; width: calc(100% - 8px); }
  .arrival-resident { width: 64px; height: 64px; }
  .arrival-book blockquote { margin-block: var(--seam); }
  .trail-heading-art { display: none; }
  .learning-trail { grid-template-columns: 1fr; gap: var(--breath); padding-inline: var(--seam); }
  .learning-trail::before { left: 30px; }
  .trail-stop:nth-child(even) { padding-top: 0; margin-left: var(--seam); }
  .trail-go { min-height: 160px; padding: var(--gutter) var(--seam); }
  .trail-copy strong { font-size: 1.25rem; }
  .trail-destination { grid-template-columns: 42px minmax(0,1fr); padding: var(--seam); }
  .trail-destination > span svg { width: 40px; height: 40px; }
}

.counter-heading { display: flex; gap: var(--seam); align-items: center; }
#key-panel:has(.counter-offer) { width: min(720px, calc(100vw - 32px)); max-width: none; }
.counter-close { margin-left: auto; align-self: start; min-width: 44px; min-height: 44px; padding: var(--seam); border: 0; background: transparent; color: var(--carbon); cursor: pointer; }
.counter-close svg { width: 20px; height: 20px; }
.counter-heading > span svg { width: 56px; height: 56px; }
.counter-heading .smallcaps { margin: 0 0 .5em; }
.counter-heading h3 { margin: 0; }
.counter-offer .plans { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: var(--seam); margin-block: var(--seam); }
.counter-offer .plan { display: flex; flex-direction: column; align-items: start; gap: .5em; text-align: left; min-width: 0; padding: var(--seam); border: 1px solid var(--carbon); border-top: 4px solid var(--carbon); background: var(--paper); color: var(--carbon); }
.counter-offer .plan::before, .counter-offer .plan::after { display: none; }
.counter-offer .p-title { font: 700 .875rem/1.4 var(--sans); }
.counter-offer .p-amt { font: 2rem/1.1 var(--bill); }
.counter-offer .p-per, .counter-offer .p-note { font: .875rem/1.5 var(--sans); }
.counter-offer .plan.best { border-color: var(--carbon); background: var(--paper); }
.counter-offer .plan > span { color: inherit; text-transform: none; letter-spacing: normal; }
.counter-offer .plan.best .p-per, .counter-offer .plan.best .p-note { color: var(--carbon); }
.counter-offer .p-action { display: flex; gap: var(--seam); align-items: center; justify-content: space-between; margin-top: auto; padding-top: var(--seam); font: 700 .875rem/1.4 var(--sans); width: 100%; border-top: 1px solid var(--rule-room); }
.p-action svg { width: 24px; height: 24px; flex-shrink: 0; }
.counter-offer .plan-trial .p-action { color: var(--paper); background: var(--carbon); padding: .6em; }
.counter-inside { border-block: 1px solid var(--rule-room); margin-block: var(--seam); }
.counter-inside summary { min-height: 44px; padding-block: var(--seam); cursor: pointer; font: .875rem var(--sans); }
.counter-offer .k-terms { line-height: 1.5; }
#key-panel .k-field-label { display: block; font: .875rem var(--sans); margin-top: var(--seam); }
#key-panel .k-field-label + input { margin-top: .4em; }
#key-panel:has(#k-name) > h3 { display: block; font: 700 1.75rem/1.15 var(--bill); text-transform: none; margin: 0 0 var(--seam); }
#key-panel:has(#k-name) > .k-plate { display: none; }
#key-panel:has(.counter-offer) .counter-heading h3 { display: block; font: 700 1.75rem/1.15 var(--bill); text-transform: none; }
#key-panel .k-orn svg { width: 28px; height: 28px; }
.k-account-promise { font: .875rem/1.5 var(--sans); padding-block: var(--seam); border-block: 1px solid var(--rule-room); }
#desk-membership { max-width: var(--desk-w); margin: var(--breath) auto; }
#desk-membership:empty { display: none; }
.membership-invitation { display: grid; grid-template-columns: 56px minmax(0,1fr) auto; align-items: center; gap: var(--seam); padding: var(--gutter); background: var(--paper); border: 1px solid var(--carbon); border-left: 6px double var(--carbon); }
.membership-invitation > span svg { width: 48px; height: 48px; }
.membership-invitation h2 { font: 700 1.5rem/1.2 var(--bill); text-transform: none; margin: .3em 0; }
.membership-invitation p { font: .875rem/1.5 var(--sans); margin: .4em 0; }
.membership-price { grid-column: 2 / -1; }
@media (max-width: 560px) {
  .counter-offer .plans { grid-template-columns: 1fr; }
  .counter-offer .plan { display: grid; grid-template-columns: minmax(0,1fr) auto; }
  .counter-offer .p-per, .counter-offer .p-note, .counter-offer .p-action { grid-column: 1 / -1; }
  .membership-invitation { grid-template-columns: 40px minmax(0,1fr); padding: var(--seam); }
  .membership-invitation > span svg { width: 36px; height: 36px; }
  .membership-invitation > button { grid-column: 2; justify-self: start; }
}

/* ILLUSTRATED ROOMS. The reader asked for colour, touch and a living cast.
   A shared palette and repeated shapes connect the objects to the stories. */
:root { --world-deep: #35568c; --world-tint: #e6eaf6; --world-mid: #919dc4; --world-gold: #efb94e; --world-shadow: #ccd4e4; }
body[data-room="de"] { --world-deep: #335f55; --world-tint: #e1eddd; --world-mid: #71967a; --world-shadow: #c2d5c1; }
body[data-room="es"] { --world-deep: #246d69; --world-tint: #dff0e9; --world-mid: #82b9aa; --world-shadow: #bbdcd3; }
/* the French room: lavender, and the violet of Blanquette's mountain at dusk */
body[data-room="fr"] { --world-deep: #5b4a86; --world-tint: #ece8f5; --world-mid: #a497c9; --world-shadow: #d4cce6; }
.brand-art { --art-ground: var(--world-tint); overflow: visible; }
.art-object, .scene-book, .scene-leaf { transform-origin: center; transition: transform 240ms cubic-bezier(.2,.7,.3,1); }
button:hover .art-object, button:focus-visible .art-object { transform: translateY(-3px) rotate(-4deg); }
button:active .art-object { transform: translateY(1px) scale(.96); }
.story-scene { display: block; width: 100%; height: auto; overflow: hidden; border-radius: 22px; }
.story-scene .cast-portrait { overflow: visible; }
button:hover .scene-book, button:focus-visible .scene-book { transform: translateY(-3px); }
button:hover .scene-leaf { transform: rotate(-5deg); }
.arrival-book { transform: none; border: 1px solid var(--world-shadow); border-left: 8px solid var(--world-deep); border-radius: 12px 28px 28px 12px; padding: var(--gutter); box-shadow: 0 12px 0 -5px var(--world-shadow), 0 24px 45px -25px var(--world-mid); }
.arrival-book-ribbon { background: var(--world-gold); z-index: 2; }
.arrival-world { position: relative; margin: calc(-1 * var(--gutter)) calc(-1 * var(--gutter)) var(--gutter); border-radius: 0 28px 0 0; overflow: hidden; background: var(--world-tint); }
.arrival-world > .story-scene { border-radius: 0; }
.arrival-resident { display: none; position: absolute; width: 30%; height: auto; aspect-ratio: 1; left: 36.7%; top: 28.6%; margin: 0; }
.arrival-alive .arrival-resident { display: block; }
.arrival-alive .arrival-world > .story-scene > .cast-portrait { visibility: hidden; }
.arrival-alive .arrival-resident > svg { width: 100%; height: 100%; }
.arrival-page-label { color: var(--world-text, var(--world-deep)); font-weight: 700; }
.arrival-book blockquote { margin-block: var(--seam); }
.arrival-primary { background: var(--world-deep); color: #fffdf5; border-color: var(--world-text, var(--world-deep)); box-shadow: 0 4px 0 color-mix(in srgb,var(--world-deep),#17213a 25%); }
.arrival-choice { border-radius: 14px; transition: transform 160ms ease-out; }
.arrival-choice:hover { transform: translateY(-2px); }
.arrival-primary:hover { box-shadow: 0 6px 0 color-mix(in srgb,var(--world-deep),#17213a 25%); }
.arrival-reveal { border: 0; border-radius: 10px; padding: .6em 1em; color: var(--world-text, var(--world-deep)); background: var(--world-tint); font-weight: 700; }
#arrival-meaning { border-left-color: var(--world-gold); }
.arrival-discovered .arrival-book { border-color: var(--world-mid); }
.arrival-method svg { width: 64px; height: 64px; }
.discovery { border: 1px solid var(--world-shadow); border-bottom: 4px solid var(--world-shadow); border-radius: 18px; gap: .6rem; }
.discovery:hover { background: var(--world-tint); }
.discovery-icon svg { width: 72px; height: 72px; }
.discovery-icon { line-height: 0; }
.practice-invitation { border: 1px solid var(--world-shadow); border-radius: 24px; border-top: 0; background: linear-gradient(var(--world-tint) 0 112px,var(--paper) 112px); box-shadow: 0 6px 0 var(--world-shadow); }
.practice-portrait { width: 112px; height: 112px; }
.practice-word-peek span { background: var(--world-tint); border: 0; border-radius: 10px; padding: .5em .7em; }
.trail-heading-art svg { transform: none; }
.learning-trail { gap: var(--gutter); padding: 0; }
.learning-trail::before { display: none; }
.trail-stop, .trail-stop:nth-child(even) { padding: 0; }
.trail-go { display: grid; grid-template-columns: minmax(0,1fr) 28px; gap: var(--seam); min-height: 0; padding: 0 0 var(--gutter); border: 1px solid var(--world-shadow); border-left: 1px solid var(--world-shadow); border-radius: 22px; box-shadow: 0 5px 0 var(--world-shadow); overflow: hidden; transition: transform 200ms ease-out; }
.trail-go::before { display: none; }
.trail-landscape { display: block; grid-column: 1 / -1; width: 100%; }
.trail-landscape > .story-scene { border-radius: 0; }
.trail-copy { padding-left: var(--gutter); gap: .4em; }
.trail-copy strong { font-size: 1.4rem; }
.trail-arrow { display: block; padding-right: var(--seam); }
.trail-go .trail-medallion { display: none; }
.trail-folio { top: var(--seam); left: var(--seam); right: auto; z-index: 1; color: var(--world-text, var(--world-deep)); background: #fffdf5; border-radius: 50%; width: 32px; height: 32px; display: grid; place-items: center; font: 700 .875rem var(--sans); }
.trail-current .trail-go { border: 2px solid var(--world-deep); box-shadow: 0 5px 0 var(--world-deep); }
.trail-current .trail-label { color: var(--world-text, var(--world-deep)); font-weight: 700; }
.trail-done .trail-go .trail-medallion { position: absolute; display: grid; top: var(--seam); right: var(--seam); width: 36px; height: 36px; border: 0; border-radius: 50%; color: #fffdf5; background: #287555; box-shadow: none; }
.trail-done .trail-medallion svg { width: 24px; height: 24px; }
.trail-go:hover, .trail-go:focus-visible { transform: translateY(-3px); box-shadow: 0 8px 0 var(--world-shadow); }
.trail-current .trail-go:hover { box-shadow: 0 8px 0 var(--world-deep); }
.trail-destination { border: 1px solid var(--world-shadow); background: var(--world-tint); border-radius: 20px; }
#v-course .cs-wing-books .rd-book { border: 1px solid var(--world-shadow); border-radius: 20px; background: var(--paper); box-shadow: 0 4px 0 var(--world-shadow); }
.rd-volume-art { width: 90px; min-height: 126px; border: 0; border-left: 5px solid var(--ink,var(--world-deep)); border-radius: 3px 14px 14px 3px; background: var(--world-tint); box-shadow: 3px 3px 0 var(--world-shadow); }
.rd-volume-art > svg { width: 72px; height: 72px; }
.counter-offer .plan { border: 1px solid var(--world-shadow); border-radius: 16px; }
.counter-offer .plan.best { border: 2px solid var(--world-deep); background: var(--world-tint); }
.counter-offer .p-action { color: var(--world-text, var(--world-deep)); }
@media (max-width: 820px) {
  .trail-go { grid-template-columns: minmax(0,1fr) 28px; }
  .arrival-world { max-height: 260px; }
}
@media (max-width: 560px) {
  .discovery { grid-template-columns: 64px minmax(0,1fr) 24px; padding: var(--seam); }
  .discovery-icon svg { width: 64px; height: 64px; }
  .arrival-book { padding: var(--gutter); margin: 0; width: 100%; }
  .arrival-world { max-height: none; }
  .arrival-world > svg { max-height: 180px; }
  .arrival-resident { width: 96px; left: calc(50% - 48px); top: 28%; }
  .arrival-method svg { width: 56px; height: 56px; }
  .learning-trail { padding: 0; gap: var(--gutter); }
  .trail-stop:nth-child(even) { margin-left: 0; }
  .trail-go { padding: 0 0 var(--seam); grid-template-columns: minmax(0,1fr) 28px; min-height: 0; }
  .trail-landscape > svg { max-height: 176px; background: var(--world-tint); }
  .trail-copy strong { font-size: 1.25rem; }
}
@media (prefers-reduced-motion: reduce) {
  .art-object, .scene-book, .scene-leaf, .arrival-choice, .trail-go { transition: none; }
  button:hover .art-object, button:focus-visible .art-object, button:hover .scene-book, button:focus-visible .scene-book, button:hover .scene-leaf, .arrival-choice:hover, .trail-go:hover, .trail-go:focus-visible { transform: none; }
}
body.motion-low .art-object, body.motion-low .scene-book, body.motion-low .scene-leaf, body.motion-low .arrival-choice, body.motion-low .trail-go { transition: none; transform: none; }

/* Grammar: notice, retrieve, receive useful feedback, carry the pattern on. */
#v-lesson { max-width: 1040px; margin-inline: auto; }
#v-lesson .gles-head { align-items: center; margin-block: var(--gutter); }
#v-lesson .gles-who { width: 96px; height: 96px; flex-shrink: 0; }
#v-lesson #lesson-title { font-size: clamp(1.6rem,3vw,2.3rem); line-height: 1.15; text-transform: none; }
#v-lesson #lesson-body { display: block; max-width: none; }
.grammar-discovery { max-width: 800px; margin-inline: auto; padding: var(--gutter); border: 1px solid var(--world-shadow); border-radius: 24px; background: var(--paper); box-shadow: 0 5px 0 var(--world-shadow); }
.discovery-progress { display: flex; align-items: center; gap: 8px; margin-bottom: var(--gutter); }
.discovery-progress span { display: grid; place-items: center; width: 32px; height: 32px; border-radius: 50%; background: var(--world-tint); color: var(--world-text, var(--world-deep)); font: 700 .875rem var(--sans); }
.discovery-progress .now { background: var(--world-deep); color: #fffdf5; outline: 2px solid var(--world-mid); outline-offset: 3px; }
.discovery-progress .done { background: #d8ecdf; color: #246348; }
.g-discovery-heading { display: flex; flex-wrap: wrap; gap: var(--seam); align-items: center; justify-content: space-between; font: var(--type-meta)/1.4 var(--sans); }
.g-discovery-heading .smallcaps { color: var(--world-text, var(--world-deep)); }
.g-discovery-instruction { font: var(--type-body)/1.5 var(--sans); margin-block: var(--seam); }
.g-discovery-line { margin: var(--gutter) 0; font: var(--type-prompt)/1.55 var(--serif); overflow-wrap: anywhere; }
.g-discovery-line mark { color: var(--carbon); background: #fce6ad; border-radius: 4px; padding-inline: .1em; }
.g-discovery-tools { display: flex; gap: var(--seam); align-items: start; flex-wrap: wrap; }
.g-discovery-tools[hidden] { display: none; }
.g-discovery-tools .btn { display: inline-flex; align-items: center; gap: .5em; }
.g-discovery-tools svg { width: 24px; height: 24px; }
.g-discovery-tools details { flex: 1 1 240px; }
.g-discovery-tools summary { cursor: pointer; min-height: 44px; display: flex; align-items: center; text-decoration: underline; }
.g-discovery-note { padding: var(--seam); border-left: 4px solid var(--world-mid); background: var(--world-tint); border-radius: 0 12px 12px 0; margin-block: var(--gutter) !important; font: 1rem/1.65 var(--sans); }
#m .grammar-discovery h3, .grammar-discovery h3 { text-transform: none; font: 700 1.25rem/1.4 var(--sans); }
.g-discovery-options { display: grid; gap: var(--seam); margin-block: var(--seam); }
.g-discovery-options .btn { width: 100%; min-height: 52px; text-align: left; white-space: normal; padding: .8em 1em; border-radius: 12px; border-color: var(--world-shadow); font: 1rem/1.5 var(--sans); text-transform: none; }
.g-discovery-options .btn:hover { border-color: var(--world-text, var(--world-deep)); background: var(--world-tint); }
#g-discovery-feedback:not(:empty) { padding: var(--seam); border-radius: 12px; background: #fff0cd; margin-block: var(--gutter); font: 1rem/1.6 var(--sans); }
.discovery-right #g-discovery-feedback:not(:empty) { background: #dff1e4; color: #214f3c; }
.g-recall-gap { display: inline-block; min-width: 4em; border-bottom: 3px solid var(--world-deep); text-align: center; }
#g-discovery-answer form { display: flex; gap: var(--seam); flex-wrap: wrap; margin-block: var(--gutter); }
#g-discovery-answer label { flex-basis: 100%; font: 700 1rem var(--sans); }
#g-recall-input { flex: 1 1 200px; min-width: 0; min-height: 48px; border: 2px solid var(--world-mid); border-radius: 12px; padding: var(--seam); font: 1.125rem var(--sans); }
.grammar-reference { max-width: 800px; margin: var(--breath) auto; border-block: 1px solid var(--world-shadow); }
.grammar-reference > summary { min-height: 48px; cursor: pointer; padding: var(--seam); font: 700 1rem/1.5 var(--sans); }
#lesson-body .grammar-reference .g-prose, #lesson-body .grammar-reference .g-rail { display: block; padding: var(--gutter); max-width: none; }
.grammar-finish-art { width: min(100%,320px); margin: 0 auto var(--gutter); }
.grammar-discovery:has(.grammar-finish-art) { text-align: center; }
.grammar-discovery:has(.grammar-finish-art) .btnrow { justify-content: center; }
.glesson-card.g-marked { border: 1px solid var(--world-shadow); border-radius: 18px; box-shadow: 0 4px 0 var(--world-shadow); background: var(--paper); padding-right: 110px; }
.glesson-card::after { display: none; }
.glesson-card .g-stampmark { opacity: 1; width: 88px; height: 88px; right: 12px; top: 20px; transform: none; border-radius: 50%; background: var(--world-tint); }
.glesson-card .g-stampmark svg { width: 88px; height: 88px; }
.glesson-card:hover { background: var(--world-tint); }
.glesson-card .g-mark svg { width: 24px; height: 24px; }
@media (max-width: 560px) {
  .grammar-discovery { padding: var(--seam); }
  .g-discovery-line { font-size: var(--type-prompt); }
  #v-lesson .gles-who { width: 64px; height: 64px; }
  #v-lesson .gles-head { gap: var(--seam); }
  .glesson-card.g-marked { padding-right: 84px; }
  .glesson-card .g-stampmark, .glesson-card .g-stampmark svg { width: 64px; height: 64px; }
  .g-discovery-options .btn { font-size: 1rem; }
}
.course-trail { margin-top: var(--gutter); }
.trail-heading { margin-bottom: var(--gutter); }
.trail-heading h2 { font-size: clamp(1.75rem,3vw,2.4rem); }
.trail-heading p { margin: var(--seam) 0 0; }
@media (min-width: 1000px) { .learning-trail { grid-template-columns: repeat(3,minmax(0,1fr)); } }
.grammar-beat-depth { margin-top: var(--gutter); padding-top: var(--seam); border-top: 1px solid var(--world-shadow); font-size: .9em; }
.grammar-beat-depth > summary { cursor: pointer; color: var(--world-text, var(--world-deep)); font: 700 1rem/1.5 var(--sans); min-height: 44px; }
.gx-stage[data-gram]::before, .gx-stage[data-gram]::after { content: none; }
@media (max-width: 560px) {
  #arrival-front .arrival-choices { order: 3; }
  #arrival-front .arrival-free { order: 4; }
  #arrival-front .arrival-book { order: 5; }
}
.trail-earned { display: flex; align-items: center; flex-wrap: wrap; gap: var(--seam); margin-top: var(--seam); color: var(--world-text, var(--world-deep)); font: 700 .875rem/1.4 var(--sans); }
.trail-earned progress { max-width: 180px; height: 10px; accent-color: var(--world-text, var(--world-deep)); }
.arrival-lede, .trail-heading p, .arrival-method span { font-weight: 400; }
#v-course .cs-wing-grammar { display: none; }
#v-course[data-wing="grammar"] .cs-wing-course, #v-course[data-wing="grammar"] .cs-wing-books { display: none; }
#v-course[data-wing="grammar"] .cs-wing-grammar { display: block; margin-block: var(--gutter); }
#v-course .cs-wings { grid-template-columns: repeat(3,minmax(0,1fr)); margin-top: var(--seam); margin-bottom: var(--gutter); padding-top: 0; border-top: 0; }
#v-course .cs-wing-tab { border: 1px solid var(--world-shadow); border-radius: 14px; box-shadow: none; }
#v-course .cs-wing-tab[aria-selected="true"] { background: var(--world-tint); border-bottom: 4px solid var(--world-deep); }
#v-course .cs-wt-dev > svg { width: 44px; height: 44px; }
#v-course > .masthead { border-bottom: 0; padding-bottom: 0; }
#v-course > .masthead h1 { font-size: 1.5rem; }
.trail-current .trail-label { background: var(--world-tint); color: var(--world-text, var(--world-deep)); border-radius: 6px; padding: .3em .6em; justify-self: start; }
@media (max-width: 820px) {
  #v-course .cs-wing-tab { padding: var(--seam); }
  #v-course .cs-wt-dev { display: none; }
  #v-course .cs-wt-mid b { font: 700 .875rem/1.3 var(--sans); }
  #v-course .cs-wt-mid em { display: none; }
  #v-course .cs-wings { gap: 6px; }
}
#v-course .cs-wing-grammar .glist, #v-grammar .glist { gap: var(--gutter); border: 0; background: transparent; }
.glist .glesson-card.g-marked { border: 1px solid var(--world-shadow); min-height: 156px; }
.glesson-card .gru { font: 700 1.125rem/1.35 var(--sans); text-transform: none; letter-spacing: normal; }
.glesson-card .g-from { line-height: 1.45; }
.grammar-discovery [hidden] { display: none !important; }
#v-lesson .gles-head { background: transparent; border: 0; gap: var(--seam); margin: var(--seam) 0; padding: 0; }
#v-lesson #lesson-title { letter-spacing: normal; margin: 0 0 .3em; }
#v-lesson .gles-say { margin: .3em 0; }
#v-lesson .gles-who { width: 72px; height: 72px; }
.grammar-discovery { padding: 24px; }
.discovery-progress { margin-bottom: 20px; }
.g-discovery-line { margin-block: 20px; }
.g-discovery-note { margin-block: 20px !important; }
@media (max-width: 560px) { .grammar-discovery { padding: 16px; } }
/* Returning readers land on the next real sentence, beside their companion. */
#v-home.desk-focused > .masthead { padding: 0; border: 0; background: transparent; color: var(--carbon); box-shadow: none; }
#v-home.desk-focused > .masthead::before, #v-home.desk-focused > .masthead::after { display: none; }
#v-home.desk-focused .desk-intro { display: block; min-height: 0; }
#v-home.desk-focused .desk-lockup { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px 32px; margin-bottom: 24px; padding-bottom:20px; border-bottom:1px solid var(--rule); }
#v-home.desk-focused .desk-lockup .wordmark, #v-home.desk-focused #room-line { display: none; }
#v-home.desk-focused #room-name { color: var(--carbon); font-size: 30px; margin: 0; max-width: none; line-height: 1.1; letter-spacing: normal; }
#v-home.desk-focused #room-door { display:flex;flex-wrap:wrap;gap:4px;margin:0;text-align:left; }
/* The same language shelf belongs at the first entrance and the returning desk. */
:is(#room-door,.arrival-rooms) .room-language { display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-height:44px;padding:8px 12px;margin:0;border:1px solid transparent;border-radius:4px;background:transparent;color:var(--steel);font:500 var(--type-meta)/1.4 var(--sans);letter-spacing:0;text-transform:none;text-decoration:none;opacity:1; }
:is(#room-door,.arrival-rooms) .room-language.is-current { color:var(--carbon);background:var(--galley);border-color:var(--rule);box-shadow:0 2px 0 var(--rule); }
:is(#room-door,.arrival-rooms) a.room-language:hover { color:var(--carbon);background:var(--case);border-color:var(--rule); }
:is(#room-door,.arrival-rooms) a.room-language:focus-visible { outline:3px solid var(--signal-ink);outline-offset:2px; }
#v-home.desk-focused .desk-host { grid-template-columns: minmax(0,1fr) minmax(180px,260px); gap:32px; min-height:0; padding:clamp(24px,3.5vw,40px); border:1px solid var(--rule);border-top:3px solid var(--pine);border-radius:var(--learning-radius,8px);background:var(--galley);box-shadow:var(--learning-shadow,0 3px 0 var(--rule)); }
#v-home.desk-focused .desk-host-bubble { border: 0; box-shadow: none; background: transparent; padding: 0; gap: var(--seam); }
#v-home.desk-focused .desk-host-bubble::after { display: none; }
#v-home.desk-focused .desk-host-kicker { display:block;padding:0;border:0;box-shadow:none;background:transparent;color:var(--steel);font:600 var(--type-meta)/1.5 var(--sans);letter-spacing:.12em;text-transform:uppercase; }
#v-home.desk-focused #desk-host-line { font:500 clamp(1.8rem,3vw,2.75rem)/1.16 var(--serif);letter-spacing:-.025em;text-transform:none;margin:0;max-width:24ch; }
#v-home.desk-focused #desk-host-meta { font:var(--type-body)/1.65 var(--sans);color:var(--steel);max-width:56ch; }
#v-home.desk-focused .desk-host-btn { align-self:start;justify-self:start;width:auto;min-height:48px;padding:12px 24px;background:var(--signal);color:var(--on-signal);border:1px solid var(--signal-ink);border-radius:var(--learning-control-radius,5px);box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent);font:600 var(--type-control)/1.5 var(--sans);text-transform:none;letter-spacing:0; }
#v-home.desk-focused .desk-host-btn:hover { background:color-mix(in srgb,var(--signal) 90%,var(--sheet));transform:none; }
#v-home.desk-focused .desk-host-btn:active { transform:none;box-shadow:0 1px 0 var(--signal-ink); }
#v-home.desk-focused .desk-cast-wrap { display:flex;position:relative;isolation:isolate;align-self:center;justify-self:center;width:100%;height:210px;border-left:1px solid var(--rule); }
#v-home.desk-focused .desk-cast { position:relative;z-index:1;width:150px;height:160px;margin:0;align-self:end;border:0;border-radius:0;background:transparent;padding:0;box-shadow:none;cursor:pointer; }
.desk-host-scene { position:absolute;inset:20px 0 0 16px;display:grid;place-items:center;overflow:hidden;pointer-events:none; }
.desk-host-scene>svg { width:100%;height:100%;opacity:.42; }
#v-home.desk-focused .desk-cast-pedestal { display:none; }
#v-home.desk-focused .desk-host-track { max-width:420px;gap:12px;margin:0; }
.desk-host-progress[hidden],.desk-host-source[hidden] { display:none!important; }
#v-home.desk-focused .desk-host-progress { height:5px;border:0;border-radius:1px;background:var(--rule); }
#v-home.desk-focused .desk-host-progress i { background:var(--pine); }
.desk-host-source { margin:0;color:var(--carbon);font:500 var(--type-meta)/1.5 var(--serif); }
#v-home.desk-focused .desk-host-count { font:var(--type-meta)/1.5 var(--sans);color:var(--steel); }
#v-home.desk-focused .desk-cast:focus-visible { outline:3px solid var(--signal-ink);outline-offset:4px; }
@media (max-width: 560px) {
  #v-home.desk-focused #room-name { font-size: 26px; }
  #v-home.desk-focused .desk-lockup { margin-bottom: var(--seam); }
  #v-home.desk-focused .desk-host { display:block;padding:22px 20px; }
  #v-home.desk-focused .desk-cast-wrap { position:absolute;right:12px;top:14px;width:72px;height:80px;border:0; }
  #v-home.desk-focused .desk-cast { width:72px;height:80px; }
  #v-home.desk-focused .desk-host-scene { display:none; }
  #v-home.desk-focused .desk-cast-pedestal { display: none; }
  #v-home.desk-focused #desk-host-kicker, #v-home.desk-focused #desk-host-line { padding-right: 66px; }
  #v-home.desk-focused #desk-host-line { font-size:1.8rem; }
  #v-home.desk-focused #desk-host-meta { font-size:var(--type-body); }
  #v-home.desk-focused .desk-host-btn { width:100%; }
}
.grammar-search, .grammar-results { grid-column: 1 / -1; }
.grammar-search { display: grid; gap: .5em; font: 700 1rem/1.5 var(--sans); }
.grammar-search input { min-height: 48px; padding: .6em .9em; border-radius: 12px; border: 1px solid var(--world-mid); color: var(--carbon); background: var(--paper); font: 1rem var(--sans); }
.grammar-results { font: var(--type-meta)/1.5 var(--sans); }
.grammar-results:empty { display: none; }
.glesson-card[hidden] { display: none !important; }
.grammar-practice-door { max-width: 800px; margin: var(--gutter) auto; padding: var(--seam); border-radius: 16px; background: var(--world-tint); }
.glist .glesson-card.g-marked { padding: 20px; }
.glesson-card .g-no { position: static; display: block; margin-bottom: 8px; font: 700 var(--type-meta)/1.4 var(--sans); color: var(--world-text, var(--world-deep)); }
.glesson-card .gru { padding-right: 80px; }
@media (max-width: 560px) { .glesson-card .gru { padding-right: 60px; } }

/* Night keeps saturated illustrations while lifting text off dark paper. */
[data-theme="dark"] body { --world-tint: #26354d; --world-shadow: #3b4961; --world-text: #b9d3ff; }
[data-theme="dark"] body[data-room="de"] { --world-tint: #243c34; --world-shadow: #3a584a; --world-text: #bce2ca; }
[data-theme="dark"] body[data-room="es"] { --world-tint: #213c3e; --world-shadow: #39595a; --world-text: #ade0d7; }
[data-theme="dark"] body[data-room="fr"] { --world-tint: #2f2944; --world-shadow: #4a4163; --world-text: #d3c8f2; }

/* A ROOM WITH NO NARRATOR YET SHOWS NO EAR (app.js ROOM_NARRATED). Every
   control that could only answer «Recording unavailable» comes off the
   page; the text controls beside them keep their places. */
body[data-silent] :is(#btn-listen, #p-say, #p-saysent, button.gx-say, .answer-listen,
  #prep-say, #g-discovery-hear, #rev-hear, #rev-hear-b) { display: none !important; }
[data-theme="dark"] .g-discovery-line mark { color: #17213a; }
#g-discovery-feedback:not(:empty) { color: #513b18; }
.discovery-right #g-discovery-feedback:not(:empty) { color: #214f3c; }
.trail-folio { color: #213451; }
.glesson-card[aria-busy="true"] { opacity: .7; cursor: progress; }
.glesson-card.g-read .g-stampmark { opacity: 1; }
.glesson-card.g-read .gru { color: var(--carbon); }
.glesson-card .g-from { color: var(--world-text,var(--world-deep)); }
.px-verb > .ic { width: 20px; height: 20px; vertical-align: middle; margin-right: .3em; }
/* ONE SIZE OF NAME, AND A CARD WIDE ENOUGH FOR IT. The picker was a line
   of names before it became this case of cards, and the line's type
   specimen came along (65-home: the first name at 1.45rem, the fifth at
   1.05rem). The Russian room's names are one word each and cannot wrap:
   on a desk «Клякса», «Собачка», «Самовар» and «Шинель» ran out past the
   right edge of their 105px cards. Every name now takes one size, and the
   narrowest card holds the longest of them. */
.px-case:not([hidden]) { display: grid; grid-template-columns: repeat(auto-fit,minmax(112px,1fr)); gap: 12px; }
.px-case > button { display: grid; justify-items: center; gap: 6px; border: 1px solid var(--world-shadow); border-radius: 16px; padding: 12px 8px; background: var(--paper); color: var(--carbon); }
.px-case > button, .px-case > button:nth-child(n) { font-size: 1.05rem; }
.px-case > button > svg { width: 64px; height: 64px; transition: transform 180ms ease-out; }
.px-case > button.active { background: var(--world-tint); color: var(--carbon); border-color: var(--world-text,var(--world-deep)); }
.px-case > button:hover > svg { transform: translateY(-3px) rotate(-3deg); }
@media (prefers-reduced-motion: reduce) { .px-case > button > svg { transition: none; } .px-case > button:hover > svg { transform: none; } }
body.motion-low .px-case > button > svg { transition: none; transform: none; }
.arrival-first-friend { display: none; }
@media (max-width: 560px) {
  .arrival-eyebrow { display: flex; align-items: center; gap: 12px; justify-content: space-between; }
  .arrival-first-friend { display: grid; place-items: center; width: 64px; height: 64px; flex: 0 0 64px; border-radius: 50%; background: var(--world-tint); }
  .arrival-first-friend > svg { width: 64px; height: 64px; }
}

/* A story door offers reading first; vocabulary preparation is optional. */
.continue-card { display: grid; grid-template-columns: minmax(130px, .65fr) minmax(0, 1.8fr); gap: 24px; align-items: center; max-width: 880px; border-radius: 24px; padding: 28px; background: var(--paper); box-shadow: 0 9px 0 var(--world-shadow, var(--rule-room)); }
.reading-door-art { overflow: hidden; border-radius: 18px; background: var(--world-tint, var(--paper-2)); aspect-ratio: 1 / 1.1; display: grid; align-items: center; }
.reading-door-art svg { width: 180%; max-width: none; margin-left: -40%; }
.reading-door-copy { min-width: 0; }
.continue-card .sc-title { margin: 8px 0; font-size: clamp(1.6rem, 3vw, 2rem); }
.reading-door-help { font: 1rem/1.65 var(--sans); margin: 16px 0; }
.reading-door-optional { font: .8125rem/1.5 var(--sans); color: var(--steel); margin: 14px 0 0; }
.reading-door-copy .btnrow { flex-wrap: wrap; gap: 12px; }
.grammar-membership-preview { padding: clamp(20px, 4vw, 40px); border-radius: 24px; background: var(--world-tint, var(--paper-2)); border: 1px solid var(--rule-room); font: 1.05rem/1.65 var(--sans); }
.grammar-membership-preview .btnrow { margin-top: 24px; }
.g-preview-label { display: inline-block; font: .75rem/1.4 var(--sans); border-radius: 12px; padding: 4px 9px; margin-top: 12px; background: var(--paper-2); }
#nav-profile { display: inline-flex; align-items: center; gap: 8px; font: .75rem var(--sans); }
#nav-prof-cls:not(:empty)::before { content: '· '; }
#nav-prof-cls:empty { display: none; }
@media (max-width: 560px) {
  .continue-card { grid-template-columns: 1fr; gap: 18px; padding: 20px; border-radius: 20px; }
  .reading-door-art { aspect-ratio: 3.5 / 1; }
  .reading-door-art svg { width: 100%; margin-left: 0; }
  .reading-door-copy .btn { width: 100%; }
  #nav-prof-cls, #nav-prof-init { display: none; }
}

#tip:not(.show) { display: none; }
#tip { max-width: min(320px, calc(100vw - 16px)); overflow-wrap: anywhere; }

/* The preferences sheet uses the same comfortable proportions as the account dialog. */
#ov-settings #settings-panel { width: min(560px, calc(100vw - 32px)); }
#settings-panel .k-plate h3 { display: block; margin: 0 0 8px; font: 700 1.6rem/1.2 var(--serif); }
#settings-panel .set-save { gap: 12px; background: var(--sheet); }
#settings-panel .theme-dots button { min-width: 36px; min-height: 36px; }
#key-panel .key-dialog-close { position: absolute; top: 14px; right: 18px; min-height: 36px; padding: 6px 10px; font-size: .8rem; }
#key-panel:has(#key-dialog-close) > h3 { padding-right: 74px; }
@media (max-width: 560px) {
  #settings-panel .set-row { flex-wrap: wrap; gap: 12px; }
  #settings-panel .set-row > .lab { flex: 1 1 180px; }
  /* A STACKED ROW RUNS DOWN, SO ITS BASIS IS A HEIGHT. The 180px above is a
     width in a side-by-side row; in a .set-col row the main axis is vertical,
     and the same basis made «How much do you already read?» and «Soften
     unknown words» 180px tall with ~170px of blank under a one-line label. */
  #settings-panel .set-row.set-col > .lab { flex: 0 0 auto; }
  #settings-panel .set-row select { max-width: 100%; }
}
/* side by side, the label gives the room and a row's own button keeps its
   words on one line — «Show them all again» and «Grant it» were squeezed
   into two-line stacks beside their long notes (the phone stacks the row
   instead, above) */
@media (min-width: 561px) {
  #settings-panel .set-row > .btn { flex: none; }
}
/* the reading level as two answers, one per line (openSettings: a select
   cannot wrap, and clipped «translation o…» on a phone) — each a whole-line
   hit with the house's own square radio */
#settings-panel .set-choices { display: grid; gap: 6px; }
#settings-panel .set-choices label {
  display: flex; align-items: center; gap: 12px; min-height: 44px;
  padding: 2px 12px 2px 14px; border: 1px solid var(--rule);
  background: var(--sheet); cursor: pointer; line-height: 1.4;
}
#settings-panel .set-choices label:has(input:checked) { border-color: var(--carbon); }
#settings-panel .set-choices label span { min-width: 0; overflow-wrap: anywhere; }
#settings-panel .set-choices input:focus-visible { outline: 2px solid var(--carbon); outline-offset: -8px; }


/* THE ARRIVAL PLATE — above the first page of an opened instalment (6 Sep 2026) */
.arrival-plate { margin: 10px auto 22px; max-width: 42rem; padding: 14px 18px 12px;
  border-top: 3px solid var(--ink, var(--carbon)); border-bottom: 1px solid var(--rule, #d2d9e8);
  background: color-mix(in srgb, var(--sheet, #fffef9) 92%, var(--ink, var(--carbon)) 8%); }
.arrival-plate .plate-kicker { font-family: var(--bill); font-weight: 700; font-size: 12px;
  letter-spacing: .18em; text-transform: uppercase; color: var(--steel, #46536f); margin: 0 0 8px; }
.arrival-plate .plate-line { font-family: var(--serif); font-size: 1.22em; line-height: 1.5; margin: 0 0 6px; }
.arrival-plate .plate-line .w { cursor: pointer; border-bottom: 1px dotted var(--rule, #d2d9e8); }
.arrival-plate .plate-en { font-family: var(--serif); font-style: italic; color: var(--steel, #46536f); margin: 0 0 8px; }
.arrival-plate .plate-note { font-family: var(--serif); font-style: italic; font-size: .92em; margin: 0; color: var(--steel, #46536f); }
/* THE DOORS TO /how-it-works from inside the room — the plate's «How reading
   works» and the empty Words, Practice and Learn. A quiet link in the ink of
   whatever it stands in (so every light and skin carries it, as the arrival
   rooms' links above), never a button competing with the one act the screen
   offers, and tall enough to take a thumb (44px). */
.arrival-plate .plate-howto { font-style: normal; margin-top: 4px; }
.plate-howto a, .empty-howto a { display: inline-flex; align-items: center; min-height: 44px;
  color: inherit; text-decoration: underline; text-underline-offset: 3px;
  /* on a phone the arrow was left alone on a second line */
  text-wrap: balance; }
/* Named past the copy rules of the rooms it stands in: 107 and 110 set
   `#v-review .daily-copy>p` and `#v-course .course-continuation-copy>p` to
   the body size later in the cascade, so the link came out at 17.3px under a
   13px note (measured 26 Sep 2026). It is the smallest thing on the card,
   the size of the note beside it, never larger than the lede it follows.
   Only the block margins are set: a centred card centres its paragraphs with
   auto inline margins, and the link must stand where its neighbours stand. */
.empty-howto,
:is(#v-review, #v-course, #v-words) :is(.daily-copy, .course-continuation-copy, .practice-invitation, .word-empty) > p.empty-howto {
  margin-block: 10px 0; font: 400 .875rem/1.5 var(--sans); text-align: inherit; }
/* AN <a> AMONG THE ACCOUNT PAGE'S VERBS. A button centres its label in the
   36px the row gives it and a link does not, so «How it works» and the three
   room doors stood 10px above the buttons beside them (measured 26 Sep 2026,
   390px). As a flex box the link centres like its neighbours. */
.pxx-utils a.px-verb { display: inline-flex; align-items: center; column-gap: .25em; }
.foot-ask { margin: 14px 0 18px; }

/* a line quoted from the book on the desk's host card: the serif, never the bill face */
#desk-host-line.is-quote, #v-home.desk-focused #desk-host-line.is-quote {
  font: 400 clamp(1.15rem, 2.2vw, 1.55rem)/1.4 var(--serif); letter-spacing: normal; }

/* First encounter: the title, one invitation, and the actual sentence fit
   together. The existing circle supplies the teaching gesture. */
#v-read.first-reading #scene-head { margin: 12px 0; padding: 0; }
#v-read.first-reading #scene-head .orn,
#v-read.first-reading #scene-head .margin-note,
#v-read.first-reading #scene-head .sh-stamp,
#v-read.first-reading #guide { display: none; }
#v-read.first-reading .arrival-plate { margin: 8px auto 14px; padding: 10px 14px; }
body:has(#v-read.show.first-reading) #nav-counter,
body:has(#v-read.show.first-reading) #nav-signup { display: none; }

#v-read.first-reading .plate-promise { font: 600 .875rem/1.5 var(--sans); margin: 8px 0 0; }

/* ============================================================================
   PRACTICE: THE CHARACTER BESIDE THE QUESTION, THE DOOR UNDER THE ANSWER
   (26 Sep 2026). The owner, with two screenshots: after a right answer «Next»
   waited at the bottom left, the Blot sat under the card half off the page,
   and on the finish sheet the Blot and its marks lay across the book's margin
   note. Two causes, both specificity:
   1. the sitting's stable-work-area rule (100-lessons: #v-review:not(…):not(…)
      #practice-workbench — two ids, two classes) outranks the companion grid
      (111-study-delight: #v-review #practice-workbench.sd-with-companion — two
      ids, one class), so during a round the grid never formed: the rail, moved
      into the workbench for its column, fell below the answer tray instead;
   2. .margin-note's wide-screen marginalia (29-alive) belongs to the READER's
      scene head — the practice finish card reuses the class, and from 1280px
      the note was thrown into the companion's column, under the sticky rail.
   ========================================================================== */
@media (min-width: 1200px) {
  #v-review:not(.practice-lobby):not(.practice-finished) #practice-workbench.sd-with-companion {
    display: grid; max-width: 1040px; grid-template-columns: minmax(0, 1fr) 196px;
    column-gap: 32px; align-items: start; align-content: start;
  }
}
/* the way on sits under the answer, centred — never pinned to the left edge */
#v-review:not(.practice-lobby) #rev-btns { justify-content: center; flex-wrap: wrap; gap: 10px; }
#v-review:not(.practice-lobby) #rev-btns > .primary:only-child { min-width: min(100%, 320px); }
/* in practice the book's note stays in the card, under what it comments on */
#v-review .margin-note {
  position: static; left: auto; top: auto; width: auto; max-width: 34em;
  margin: 18px auto 0; text-align: left;
}


/* ===== 100-lessons.css ===== */
/* Lesson interiors: an illustrated destination, supported study, then real
   attempts. The visible progress uses the existing learning record. Motion
   is confined to the touched control; every state also works without it. */
.gx-stage.gx-adventure {
  --play: var(--world-deep); --play-deep: var(--world-deep);
  --lesson-peach: #f5ded0;
  --lesson-lilac: #e7e3f4;
  --lesson-mint: #dfede4;
  padding: 20px max(14px, env(safe-area-inset-right)) 32px max(14px, env(safe-area-inset-left));
}
.gx-stage.gx-adventure .gx {
  width: min(820px, 100%); min-width: 0; padding: 0 0 20px;
  border: 0; background: transparent !important; box-shadow: none !important; gap: 16px;
  transform: none !important;
  overflow: visible;
}
.gx-adventure .gx-top {
  padding: 0 !important; background: transparent !important; border: 0 !important;
  box-shadow: none; position: static; margin: 0 !important;
}
.gx-adventure .gx-top :is(.gx-topic, .gx-topic *, .gx-count, .gx-quit) { color: var(--carbon) !important; }
.gx-adventure .gx-top .gx-topic { font: 700 .875rem/1.4 var(--sans); letter-spacing: 0; }
.gx-adventure .gx-top .gx-topic :is(i, em),
.gx-adventure .gx-top :is(.gx-runn, .gx-count),
.gx-adventure .gx > :is(.gx-rail, .gx-chbar, .gx-chbar-say, .gx-arcplate, .gx-arc-epi, .gx-arc-wm),
.gx-adventure > .gx-margin { display: none; }
.gx-adventure .gx-top .gx-quit {
  min-height: 44px; min-width: 64px; padding: 8px 14px !important;
  background: var(--paper) !important; border: 1px solid var(--world-shadow) !important;
  border-radius: 12px; font: 700 .875rem/1.3 var(--sans); letter-spacing: 0; text-transform: none;
}
.gx-adventure .gx-top .gx-quit:hover { background: var(--world-tint) !important; }
.lesson-compass {
  display: grid; grid-template-columns: 210px minmax(0, 1fr); align-items: center; gap: 20px;
  border: 1px solid var(--world-shadow); border-radius: 24px; background: var(--world-tint);
  padding: 12px 24px 12px 12px; color: var(--carbon); overflow: hidden;
}
.lesson-window { position: relative; border-radius: 18px; overflow: hidden; min-width: 0; }
.lesson-window > .story-scene { border-radius: 0; }
.lesson-window:has(#gx-who) > .story-scene > .cast-portrait { visibility: hidden; }
.lesson-compass-copy { min-width: 0; display: grid; gap: 8px; }
.lesson-phase, .lesson-small-label { display: block; color: var(--world-text, var(--world-deep)); font: 700 .75rem/1.45 var(--sans); letter-spacing: .06em; }
.lesson-compass h2 { font: 700 clamp(1.4rem, 2.5vw, 1.875rem)/1.15 var(--bill); color: var(--carbon); margin: 0; overflow-wrap: anywhere; text-transform: none; letter-spacing: 0; }
.lesson-progress { display: grid; gap: 8px; max-width: 360px; font: .8125rem/1.35 var(--sans); }
.lesson-progress progress { width: 100%; height: 8px; border: 0; overflow: hidden; border-radius: 99px; background: var(--world-shadow); accent-color: var(--world-deep); }
.lesson-progress progress::-webkit-progress-bar { background: var(--world-shadow); }
.lesson-progress progress::-webkit-progress-value { background: var(--world-deep); border-radius: 99px; }
.lesson-progress progress::-moz-progress-bar { background: var(--world-deep); border-radius: 99px; }
.gx-adventure .gx .lesson-window .gx-mate {
  display: grid; position: absolute; left: 36.7%; top: 28.6%; right: auto; bottom: auto;
  width: 30%; height: auto; aspect-ratio: 1; margin: 0; transform: none; cursor: default;
}
.gx-adventure .lesson-window .gx-mate::before { display: none; }
.gx-adventure .lesson-window .gx-mate > svg { width: 100%; height: 100%; }
.gx-adventure .lesson-window :is(.gx-mate-line, .gx-slip) { display: none; }
.gx-stage.gx-adventure .gx-mid {
  min-width: 0; padding: 28px; border-radius: 24px;
  background: var(--paper); border: 1px solid var(--world-shadow);
  box-shadow: 0 5px 0 var(--world-shadow); gap: 18px;
}
.gx-adventure .gx-mid[data-kind="teach"] { padding: 0; overflow: hidden; }
.gx-adventure .gx-mid[data-kind="teach"] > :is(.gx-head, .gx-direction) { display: none; }
.gx-adventure .gx-mid[data-kind="teach"] > .gx-after { margin: 0; padding: 0 28px 24px; }
.gx-adventure .gx-mid[data-kind="listen"],
.gx-adventure .gx-mid[data-kind="hearline"] { border-top: 7px solid var(--world-mid); background-image: none; }
.gx-adventure .gx-mid[data-kind="write"],
.gx-adventure .gx-mid[data-kind="order"] { border-top: 7px solid #b2a4ce; background-image: none; }
.gx-adventure .gx-mid[data-kind="gap"],
.gx-adventure .gx-mid[data-kind="form"] { border-top: 7px solid #d2a48c; background-image: none; }
.gx-adventure .gx-mid[data-kind="gram"],
.gx-adventure .gx-mid[data-kind="meaning"],
.gx-adventure .gx-mid[data-kind="word"] { background-image: none; }
.gx-adventure .gx-mid .gx-ask { font: 700 1.05rem/1.5 var(--sans); letter-spacing: 0; margin: 0; flex-wrap: wrap; text-transform: none; }
.gx-adventure .gx-ask .gx-kind { background: var(--world-tint); border: 0; box-shadow: none; border-radius: 10px; color: var(--world-text, var(--world-deep)); }
.gx-adventure .gx-ask .gx-kind > svg { display: block; width: 36px; height: 36px; }
.gx-adventure .gx-ask .gx-again { display: block; flex-basis: 100%; margin: 0; font-size: .8125rem; letter-spacing: 0; text-transform: none; }
.gx-adventure .gx-line { font-style: normal; overflow-wrap: anywhere; }
.gx-adventure .gx-opt { min-height: 58px; border: 1px solid var(--world-shadow); border-radius: 14px; box-shadow: 0 4px 0 var(--world-shadow); background: var(--paper); color: var(--carbon); font-weight: 600; transition: transform 160ms ease, box-shadow 160ms ease; }
.gx-adventure .gx-opt::before { display: none; }
/* Short recognition choices fit beside one another on a wide screen.
   Long sentence exercises retain their reading order in a single column. */
@media (min-width: 700px) {
  .gx-adventure .gx-mid[data-kind="meaning"] .gx-opts,
  .gx-adventure .gx-mid[data-kind="word"] .gx-opts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
.gx-adventure .gx-opt:not(:disabled):hover { background: var(--world-tint); transform: translateY(-2px); box-shadow: 0 6px 0 var(--world-shadow); }
.gx-adventure .gx-opt:not(:disabled):active { transform: translateY(2px); box-shadow: 0 1px 0 var(--world-shadow); }
.gx-adventure .gx-opt.gx-right { background: #e0efe6; color: #214f3c; border-color: #54856b; box-shadow: 0 4px 0 #b4cdbd; }
.gx-adventure .gx-opt.gx-wrong { background: #f2e8dd; color: #6b4932; border-color: #ba9274; box-shadow: none; }
.gx-adventure :is(.gx-pw, .gx-pg, .gx-tile, .gx-set) {
  min-height: 48px; border: 1px solid var(--world-shadow); border-radius: 12px;
  box-shadow: 0 3px 0 var(--world-shadow); padding: 10px 12px;
  color: var(--carbon); background: var(--world-tint); font-style: normal;
}
.gx-adventure :is(.gx-pg, .gx-tile) { background: var(--paper); }
.gx-adventure .gx-pg { font: 500 .9375rem/1.5 var(--sans); }
.gx-adventure .gx-pairs { gap: 12px; }
.gx-adventure .gx-pcol { gap: 12px; min-width: 0; }
.gx-adventure .gx-p-sel { outline: 2px solid var(--world-text, var(--world-deep)); outline-offset: 2px; }
.gx-adventure :is(.gx-pw, .gx-pg).gx-p-done { background: #e0efe6; color: #214f3c; border-color: #54856b; box-shadow: none; }
.gx-adventure .gx-after { display: grid; gap: 14px; }
.gx-adventure .gx-after:empty { display: none; }
.gx-adventure .gx-verdict { font: 700 1.05rem/1.5 var(--sans); padding: 16px; border-radius: 16px; background: var(--world-tint); color: var(--carbon); border: 0; box-shadow: none; }
.gx-adventure .gx-v-say { font-size: 1.25rem; }
.gx-adventure .gx-v-gloss { font: .875rem/1.45 var(--sans); letter-spacing: 0; margin-top: 5px; }
.gx-adventure .gx-src { padding: 16px; border: 0; border-left: 4px solid var(--world-mid); background: transparent; }
.gx-adventure :is(.gx-eye, .gx-mate-note) > i > svg { display: block; width: 20px; height: 20px; }
.gx-adventure .gx-mid .btn { border-radius: 12px; min-height: 44px; font: 700 .9375rem/1.4 var(--sans); letter-spacing: 0; text-transform: none; }
.gx-adventure .gx-mid .btn.primary { background: var(--world-deep); color: #fffdf5; border: 0; box-shadow: 0 4px 0 color-mix(in srgb, var(--world-deep), #17213a 25%); }
.gx-stage.gx-adventure #gx-next { background: var(--world-deep) !important; border-color: var(--world-deep); }
.gx-adventure .gx-mid .btn.primary:hover { transform: translateY(-1px); }
.gx-adventure .gx-mid .gx-say { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 10px 14px; }
.gx-adventure .gx-say svg { width: 22px; height: 22px; flex-shrink: 0; }
.gx-adventure :is(button, summary, input):focus-visible { outline: 3px solid var(--world-text, var(--world-deep)); outline-offset: 4px; }
.gx-adventure [hidden] { display: none !important; }
.lesson-discovery { display: grid; gap: 0; }
.lesson-idea { padding: 24px 28px; background: var(--world-tint); }
.lesson-idea > p { margin: 8px 0 0; font: 450 1.05rem/1.65 var(--sans); color: var(--carbon); }
.lesson-note-depth { margin-top: 12px; }
.lesson-note-depth summary, .lesson-translation summary, .gx-adventure .grammar-beat-depth summary { min-height: 44px; display: list-item; padding: 10px 0; cursor: pointer; font: 700 .875rem/1.5 var(--sans); color: var(--world-text, var(--world-deep)); }
.lesson-note-depth p, .lesson-translation p { font: 1rem/1.6 var(--sans); margin: 0 0 10px; }
.lesson-specimen { margin: 0; padding: 24px 28px; border-bottom: 1px solid var(--world-shadow); }
.lesson-specimen .gx-line { margin: 12px 0; font-weight: 600; }
.lesson-line-tools { display: flex; gap: 12px; align-items: start; flex-wrap: wrap; }
.lesson-translation { flex: 1 1 170px; }
.lesson-word-discovery { padding: 20px 28px; }
.lesson-word-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.lesson-word-chips button { min-height: 44px; border: 1px solid var(--world-shadow); border-radius: 12px; background: var(--lesson-lilac); color: #423e60; padding: 8px 14px; font: 600 1.0625rem/1.4 var(--serif); cursor: pointer; transition: transform 160ms ease; }
.lesson-word-chips button:nth-child(3n + 2) { background: var(--lesson-peach); color: #64412d; }
.lesson-word-chips button:nth-child(3n) { background: var(--lesson-mint); color: #284e3e; }
.lesson-word-chips button[aria-pressed="true"] { outline: 2px solid var(--world-text, var(--world-deep)); outline-offset: 2px; }
.lesson-word-chips button:hover { transform: translateY(-2px); }
#lesson-word-meaning { margin: 12px 0 0; min-height: 1.6em; font: 1rem/1.6 var(--sans); }
/* THE SHEET IS THE MEETING (12 Sep 2026): every word wears its meaning on the
   chip, so no second «meet the word» card is owed per word */
.lesson-word-pairs button { display: grid; gap: 2px; text-align: left; padding: 8px 14px 9px; }
.lesson-word-pairs button b { font: 600 1.0625rem/1.3 var(--serif); }
.lesson-word-pairs button small { display: block; font: 400 .8125rem/1.35 var(--sans); opacity: .78; max-width: 16ch; }
/* EAR FIRST: the line waits behind a veil until it has been heard once */
.lesson-specimen[data-veiled="1"] .gx-line, .lesson-specimen[data-veiled="1"] .lesson-given-meaning { filter: blur(7px); user-select: none; transition: filter 420ms var(--e-out, ease); }
.lesson-specimen .gx-line, .lesson-specimen .lesson-given-meaning { transition: filter 420ms var(--e-out, ease); }
.lesson-unveil { background: none; border: 0; padding: 4px 0; font: 500 .875rem/1.4 var(--sans); color: var(--steel); text-decoration: underline; cursor: pointer; }
.lesson-specimen:not([data-veiled="1"]) .lesson-unveil { display: none; }
/* THE SCENE (Wave 2): the lesson's dialogue on the sheet, names in the margin,
   meanings beside the lines; veiled until heard, revealed line by line */
.lesson-scene .scene-lines { list-style: none; margin: 8px 0 0; padding: 0; display: grid; gap: 10px; }
.scene-line { display: grid; grid-template-columns: minmax(3.2em, auto) minmax(0, 1fr); column-gap: 14px; row-gap: 2px; align-items: baseline; }
.scene-line .scene-name { font: 600 .7rem/1.3 var(--sans); letter-spacing: .1em; text-transform: uppercase; color: var(--steel); padding-top: .45em; }
.scene-line .scene-t { font: 400 1.2rem/1.45 var(--serif); }
.scene-line .scene-en { grid-column: 2; font: 400 .875rem/1.45 var(--sans); opacity: .75; }
.scene-line .scene-t, .scene-line .scene-en { transition: filter 420ms var(--e-out, ease); }
.lesson-scene[data-veiled="1"] .scene-line:not([data-said="1"]) .scene-t,
.lesson-scene[data-veiled="1"] .scene-line:not([data-said="1"]) .scene-en { filter: blur(7px); user-select: none; }
.scene-line.is-saying .scene-t { text-decoration: underline; text-decoration-color: var(--g-gold, #b08a2e); text-decoration-thickness: 2px; text-underline-offset: 4px; }
.lesson-scene .lesson-line-tools { margin-top: 14px; }
.scene-context { display: grid; grid-template-columns: minmax(3.2em, auto) minmax(0, 1fr); column-gap: 14px; align-items: baseline; padding: 10px 14px; border-left: 3px solid var(--rule-room, var(--world-shadow)); margin: 6px 0 12px; }
.scene-context .scene-name { font: 600 .7rem/1.3 var(--sans); letter-spacing: .1em; text-transform: uppercase; color: var(--steel); }
.scene-context .scene-t { font: 400 1.1rem/1.45 var(--serif); }
.scene-context .scene-en { grid-column: 2; font: .85rem/1.4 var(--sans); opacity: .75; }
.scene-cue { margin: 0 0 8px; font: 400 1rem/1.5 var(--sans); }
.scene-cue .scene-name { font: 600 .7rem/1.3 var(--sans); letter-spacing: .1em; text-transform: uppercase; color: var(--steel); margin-right: 6px; }
.scene-cue b { font-weight: 600; }
.lesson-scene-replay { margin-bottom: 14px; }
@media (max-width: 560px) { .scene-line, .scene-context { grid-template-columns: 1fr; } .scene-line .scene-en, .scene-context .scene-en { grid-column: 1; } }
/* the ladder's clue, on the card itself */
.gx-clue-line { margin: 10px 0 0; padding: 10px 14px; border-left: 3px solid var(--g-gold, #b08a2e); background: color-mix(in srgb, var(--paper) 70%, transparent); font: 400 .95rem/1.5 var(--sans); }
@media (prefers-reduced-motion: reduce) { .lesson-specimen .gx-line, .lesson-specimen .lesson-given-meaning { transition: none; } }
.gx-adventure .gx-gr-body { font: 1.05rem/1.8 var(--sans); }
.gx-adventure .gx-gr-body > .lesson-small-label { margin-bottom: 12px; }
.gx-adventure .gx-gr-body .gx-gr-ru { font-style: normal; }
.gx-adventure .gx-gr-note { font: 1rem/1.65 var(--sans); }
.gx-stage.gx-adventure .gx.gx-done { padding: 0 0 24px; margin: 0; }
.gx-adventure .gx.gx-done > :is(.gx-print, .gx-facts, .gx-night) { margin-inline: 0; }
.gx-adventure .gx.gx-done > :is(.gx-print, .gx-cols, .gx-facts) { border-radius: 20px; }
.gx-adventure .gx.gx-done .gx-print { padding: 28px; background: var(--world-tint); border: 1px solid var(--world-shadow); }
.gx-adventure .gx-print-ru { font-size: clamp(1.5rem, 3vw, 2rem); }
.gx-adventure .gx.gx-done .lesson-compass { grid-column: 1 / -1; }
.gx-adventure .gx-front, .gx-adventure .gx-gr-hint { display: none; }
.gx-adventure .pl { opacity: .15; }
@media (max-width: 560px) {
  .gx-stage.gx-adventure { padding-top: 12px; }
  .gx-stage.gx-adventure .gx { gap: 12px; }
  .lesson-compass { grid-template-columns: 92px minmax(0, 1fr); padding: 10px; gap: 12px; border-radius: 18px; }
  .lesson-window { border-radius: 12px; }
  .lesson-window > .story-scene { width: 142px; max-width: none; margin-left: -25px; }
  .gx-adventure .gx .lesson-window .gx-mate { width: 45px; left: 28px; top: 24px; }
  .lesson-compass h2 { font-size: 1.25rem; }
  .lesson-phase { font-size: .6875rem; letter-spacing: .025em; }
  .lesson-progress { font-size: .75rem; }
  .gx-stage.gx-adventure .gx-mid { padding: 20px 16px; border-radius: 18px; }
  .gx-stage.gx-adventure .gx-mid[data-kind="teach"] { padding: 0; }
  .lesson-idea, .lesson-specimen, .lesson-word-discovery { padding: 20px 16px; }
  .gx-adventure .gx-mid[data-kind="teach"] > .gx-after { padding: 0 16px 20px; }
  .gx-adventure .gx-mid .gx-opts { grid-template-columns: 1fr; gap: 12px; }
  .gx-adventure .gx-opt { min-height: 50px; padding: 12px 16px; font-size: 1rem; }
  .gx-adventure .gx-mid .gx-ask { font-size: 1rem; }
  .gx-adventure .gx-top .gx-topic { font-size: .8125rem; }
  .gx-adventure .gx-mid .btn.primary { width: 100%; }
  .gx-adventure .gx-facts { flex-wrap: wrap; }
}
[data-theme="dark"] .gx-stage.gx-adventure { --lesson-peach: #41332f; --lesson-lilac: #38354e; --lesson-mint: #2b4238; }
[data-theme="dark"] .lesson-word-chips button { color: #e2ddfa; }
[data-theme="dark"] .lesson-word-chips button:nth-child(3n + 2) { color: #f2d9c8; }
[data-theme="dark"] .lesson-word-chips button:nth-child(3n) { color: #d1ebdc; }
@media (prefers-reduced-motion: reduce) {
  .gx-adventure *, .gx-adventure *::before, .gx-adventure *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .gx-adventure .gx-opt:not(:disabled):hover, .gx-adventure .gx-mid .btn.primary:hover, .lesson-word-chips button:hover { transform: none; }
}
body.motion-low .gx-adventure *, body.motion-low .gx-adventure *::before, body.motion-low .gx-adventure *::after { animation: none !important; transition: none !important; }
body.motion-low .gx-adventure .gx-opt:hover, body.motion-low .lesson-word-chips button:hover { transform: none; }


/* A sitting has a stable work area. Long material can still use ordinary
   page scrolling; the action rail keeps its place at the viewport edge. */
#v-review:not(.practice-lobby):not(.practice-finished) #practice-workbench {
  display: flex; flex-direction: column;
  min-height: calc(100svh - var(--practice-top, 110px) - 12px);
  overflow-anchor: none;
}
#v-review:not(.practice-lobby):not(.practice-finished) #rev-card { flex: 1; }
#v-review:not(.practice-lobby):not(.practice-finished) #rev-btns {
  display: flex; flex: none; min-height: 76px; box-sizing: border-box;
  margin-top: 12px; align-items: center;
}
#v-review #rev-btns:empty { visibility: hidden; pointer-events: none; }
#rev-card:focus { outline: none; }
#v-review #rev-btns button { min-height: 48px; }
.gx-stage .gx-mid { overflow-anchor: none; }
/* THE DOOR OUT OF A BEAT MUST STAY IN THE ROOM, and for a while it did not.
   `position: sticky` was set on the BUTTON, whose containing block is
   .gx-after — a box exactly one button tall. Sticky can only travel inside its
   containing block, so there was nowhere for it to travel and the rule did
   nothing at all. On a long beat the whole sheet is taller than the window:
   the reader met a line of the translation sliced through the middle by the
   fold with a half-drawn button under it, and read that as a page that had
   been cut off rather than a page that scrolls — reported from the German
   room on „Weiße Nächte“, 19 Sep 2026.

   The bar sticks instead. Its containing block is .gx-mid, which IS taller
   than the window whenever this matters, so it holds at the foot of the room
   at every scroll position; the prose passes under it on paper, with a soft
   edge above so it reads as a bar laid over the page rather than the end of
   it. :not(:empty) because a beat with no door must lay no bar.

   SCOPED TO THE TWO READING BEATS on purpose. On those, .gx-after holds one
   button and nothing else (nextDoor writes exactly that), so a bar the height
   of a button is all this can ever be. The answering beats put their verdict
   in the same box, and a tall verdict pinned to the foot of the window would
   be a different thing entirely — it can have this when someone has looked at
   what it does there. */
.gx-adventure .gx-mid[data-beat="ctx"] .gx-after:not(:empty),
.gx-adventure .gx-mid[data-beat="rwl"] .gx-after:not(:empty) {
  position: sticky; bottom: 0; z-index: 6;
  padding-block: 14px max(14px, env(safe-area-inset-bottom, 0px));
  background: var(--paper);
  box-shadow: 0 -10px 14px -8px color-mix(in srgb, var(--carbon) 22%, transparent);
}
.gx-stage.gx-adventure .gx-mid[data-beat="ctx"] .gx-after > #gx-next,
.gx-stage.gx-adventure .gx-mid[data-beat="rwl"] .gx-after > #gx-next {
  position: static;
  box-shadow: 0 4px 0 color-mix(in srgb, var(--world-deep), #17213a 25%);
}


/* The prose and controls stay still while the illustrated scene is alive. */
.gx-stage.gx-adventure .gx-mid,
.gx-stage.gx-adventure .gx-mid > :is(.gx-ask, .gx-head, .gx-body) {
  animation: none; transform: none;
}
.gx-stage.gx-adventure { scrollbar-gutter: stable; }
/* The retired left margin still reserved a grid column in grammar sessions.
   Its furniture is now in the lesson header: give the working page that space. */
.gx-stage.gx-adventure[data-gram="on"] { grid-template-columns: minmax(0,1fr); column-gap: 0; }
.gx-stage.gx-adventure[data-gram="on"] > .gx { grid-column: 1; width: min(920px,100%); margin-inline: auto; }
.gx-stage.gx-adventure[data-gram="on"] .gx-gr-body { max-width: 68ch; }
.g-recall-built, .g-recall-tiles { display: flex; flex-wrap: wrap; gap: 10px; margin-block: 16px; }
.g-recall-built { min-height: 62px; padding: 10px; border: 2px dashed var(--world-shadow); border-radius: 14px; background: var(--world-tint); }
.g-recall-built .btn, .g-recall-tiles .btn { min-height: 46px; min-width: 52px; padding: 10px 14px; border-radius: 10px; font: 500 1.15rem/1.35 var(--serif); text-transform: none; letter-spacing: 0; }
.g-recall-tiles .btn:disabled { opacity: .35; cursor: default; }
.grammar-discovery .btnrow { flex-wrap: wrap; }
.grammar-discovery .btnrow .btn { white-space: normal; }
#g-recall-check:disabled { opacity: .45; cursor: default; }
.g-recall-label { font: 500 .9375rem/1.5 var(--sans); }
.grammar-discovery .btn:focus-visible { outline: 3px solid var(--world-text, var(--carbon)); outline-offset: 4px; }
.lesson-task-hint { font: .9375rem/1.55 var(--sans); color: var(--carbon); margin: 0 0 14px; }
.lesson-source { font: .8125rem/1.4 var(--serif); color: var(--carbon); }
.gx-stage.gx-adventure .gx-mid[data-kind="order"] .gx-stick { min-height: 100px; }
.gx-stage.gx-adventure .gx-mid[data-kind="order"] .gx-tile:disabled { visibility: hidden; }

/* The reader's progress is an invitation back into the books. */
#v-profile .room-head::after { display: none; }
.profile-play { border: 1px solid var(--rule-room); border-radius: 24px; overflow: hidden; margin-block: 24px; background: var(--paper); }
.profile-level { display: grid; grid-template-columns: 180px minmax(0,1fr); gap: 24px; align-items: center; padding: 24px; background: color-mix(in srgb, var(--paper) 85%, #a59ad4); }
.profile-level-art { border-radius: 18px; overflow: hidden; }
.profile-level h2 { margin: 6px 0; font: 700 clamp(1.6rem,3vw,2.25rem)/1.2 var(--bill); text-transform: none; letter-spacing: 0; }
.profile-level h2 span { font: 500 1rem/1.4 var(--serif); }
.profile-level p, .profile-week p { margin: 8px 0; font: .9375rem/1.5 var(--sans); }
.profile-play progress { display: block; width: 100%; height: 10px; border: 0; border-radius: 99px; overflow: hidden; accent-color: var(--world-deep, #405b92); background: var(--rule-room); }
.profile-play progress::-webkit-progress-bar { background: var(--rule-room); }
.profile-play progress::-webkit-progress-value { background: var(--world-deep, #405b92); border-radius: 99px; }
.profile-level-distance { display: block; margin-top: 8px; font: .8125rem/1.45 var(--sans); }
.profile-week, .profile-quests { padding: 24px; }
.profile-play h3 { margin: 0; font: 700 1.3rem/1.3 var(--serif); }
.profile-week-head { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; }
.profile-week-head label { display: flex; align-items: center; gap: 10px; font: .8125rem var(--sans); }
.profile-week-head select { min-height: 44px; min-width: 64px; border: 1px solid var(--rule-room); background: var(--paper); color: var(--carbon); border-radius: 10px; padding: 8px; }
.profile-week-pages { display: grid; grid-template-columns: repeat(7,minmax(0,1fr)); gap: 8px; margin-top: 18px; }
.profile-day { display: grid; place-items: center; gap: 8px; padding: 12px 4px; border: 1px solid var(--rule-room); border-radius: 8px 14px 14px 8px; font: .75rem var(--sans); background: var(--paper); }
.profile-day b { width: 24px; height: 24px; }
.profile-day b svg { width: 100%; height: 100%; }
.profile-day.is-earned { background: #dfede4; color: #284e3e; border-color: #96b5a1; box-shadow: 0 3px 0 #b4cdbd; }
.profile-day.is-today { outline: 2px solid var(--world-text, var(--carbon)); outline-offset: 2px; }
.profile-day.is-future { color: var(--steel); }
.profile-week .profile-week-note { margin-top: 16px; font-size: .8125rem; }
/* MY GOALS (27 Sep 2026): the day's sentences, what the reader is reading
   toward and their standing agreement, ruled as rows under the week. The
   agreement keeps its words visible here even when empty — elsewhere the
   empty line folds to its button (109-rank-design), which in a list of goals
   would leave a door with no name. */
#my-goals { scroll-margin-top: 88px; }   /* every goals door lands below the sticky rail */
#my-goals .profile-goals { margin-top: 18px; border-top: 1px solid var(--rule-room); }
#my-goals .profile-goals .pg-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: 8px 16px; margin: 0; padding: 12px 0; border-bottom: 1px solid var(--rule-room); font: .9375rem/1.45 var(--sans); }
#my-goals .profile-goals select { min-height: 44px; min-width: 64px; border: 1px solid var(--rule-room); background: var(--paper);
  color: var(--carbon); border-radius: 10px; padding: 8px; }
#my-goals .profile-goals .pg-aim b { flex: 1 1 12em; font-weight: 600; }
#my-goals .profile-goals .pxx-if, #my-goals .profile-goals .pxx-if.is-empty { font: italic .9375rem/1.5 var(--serif); color: inherit; }
#my-goals .profile-goals .pxx-if.is-empty { color: var(--steel); }
#my-goals .profile-goals .pxx-if button { font: .8125rem/1.5 var(--sans); }
#my-goals .profile-goals .plan-edit { flex: 1 1 100%; }
.desk-goals-door { margin: 12px 0 0; font: .875rem/1.5 var(--sans); }
.profile-quests { border-top: 1px solid var(--rule-room); }
.profile-quest-grid { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 14px; margin-top: 18px; }
.profile-quest { display: flex; flex-direction: column; align-items: flex-start; text-align: left; gap: 10px; min-width: 0; padding: 18px; border: 1px solid var(--rule-room); border-radius: 18px; background: color-mix(in srgb, var(--paper) 92%, #b2a4ce); color: var(--carbon); font: .875rem/1.45 var(--sans); cursor: pointer; transition: transform 160ms ease, box-shadow 160ms ease; }
.profile-quest:nth-child(2) { background: color-mix(in srgb, var(--paper) 88%, #d9a477); }
.profile-quest:nth-child(3) { background: color-mix(in srgb, var(--paper) 88%, #7faa91); }
.profile-quest strong { font: 700 1.125rem/1.3 var(--serif); }
.profile-quest-art { width: 62px; height: 62px; }
.profile-quest-art svg { width: 100%; height: 100%; }
.profile-quest-progress { margin-top: auto; font-weight: 700; padding-top: 6px; }
.profile-quest:hover { transform: translateY(-3px); box-shadow: 0 6px 0 var(--rule-room); }
.profile-quest:active { transform: translateY(1px); }
.profile-quest.is-earned { border-color: #83a58f; }
.profile-keep { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 20px 24px; border-top: 1px solid var(--rule-room); font: .9375rem/1.5 var(--sans); }
.profile-play :is(button,select):focus-visible { outline: 3px solid var(--carbon); outline-offset: 4px; }
@media (max-width: 600px) {
  .profile-level { grid-template-columns: 92px minmax(0,1fr); gap: 14px; padding: 18px; }
  .profile-level h2 span { display: block; }
  .profile-week, .profile-quests { padding: 18px; }
  .profile-week-pages { gap: 5px; }
  .profile-day { font-size: .75rem; min-width: 0; }
  .profile-day > span:last-child { display: none; }
  .profile-quest-grid { grid-template-columns: 1fr; }
  .profile-quest { display: grid; grid-template-columns: 60px minmax(0,1fr); gap: 6px 14px; }
  .profile-quest-art { grid-row: span 4; align-self: center; }
  .profile-quest > :not(.profile-quest-art) { grid-column: 2; }
}
@media (prefers-reduced-motion: reduce) { .profile-quest { transition: none; } .profile-quest:hover { transform: none; } }

/* Chapters open into passage cards; unread quotations stay in the book. */
#v-course .rd-lessons { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 12px; border: 0; margin-block: 20px; }
#v-course .rd-lessons > .rd-gram { grid-column: 1 / -1; }
#v-course .rd-lessons > .rd-lesson { grid-template-columns: 30px minmax(0,1fr); align-content: start; gap: 10px; padding: 18px; border: 1px solid var(--rule-room); border-radius: 16px; background: var(--paper); box-shadow: 0 4px 0 var(--rule-room); }
#v-course .rd-lessons > .rd-l-here { background: color-mix(in srgb, var(--paper) 86%, var(--ink)); border-color: var(--ink); }
#v-course .rd-lessons .rd-l-state { grid-column: 2; text-align: left; font: 700 .875rem/1.4 var(--sans); color: var(--carbon); }
#v-course .rd-l-state small { display: block; margin-top: 4px; font-weight: 400; }
#v-course .rd-lessons .rd-l-ru { font-size: 1.05rem; line-height: 1.5; }
#v-course .rd-lessons .rd-hand { display: inline-block; width: 16px; height: 16px; }
#v-course .rd-lessons .rd-hand svg { width: 100%; height: 100%; }
@media (max-width: 600px) { #v-course .rd-lessons { grid-template-columns: 1fr; } }

#app-update { position: fixed; z-index: 180; bottom: max(16px,env(safe-area-inset-bottom)); left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 12px; width: max-content; max-width: calc(100vw - 32px); padding: 12px 16px; border: 1px solid var(--carbon); border-radius: 16px; background: var(--paper); color: var(--carbon); box-shadow: 0 6px 24px #17213a22; font: .875rem/1.4 var(--sans); box-sizing: border-box; }
#app-update .btn { flex-shrink: 0; }
body.gx-on #app-update,
body:has(#v-read.show) #app-update,
body:has(.overlay.show) #app-update,
body:has(#v-review.show:not(.practice-lobby):not(.practice-finished)) #app-update { display: none; }
@media (max-width: 600px) { #app-update { flex-wrap: wrap; } #app-update > span { flex: 1 1 100%; } }
.profile-ticket { margin-block: 20px; }
.profile-ticket > summary { min-height: 44px; padding: 12px; cursor: pointer; font: 700 1rem/1.4 var(--sans); border-bottom: 1px solid var(--rule-room); }

/* The curriculum has one entrance: a visible choice of levels, then one
   focused chapter. Existing lesson addresses and records remain the source. */
.course-atlas { margin: 24px 0; }
.atlas-heading { margin-bottom: 22px; }
.atlas-heading h2, .grammar-map-head h2 { font: 700 clamp(1.65rem,3vw,2.3rem)/1.15 var(--serif); margin: 8px 0 12px; text-transform: none; letter-spacing: -.02em; }
.atlas-heading p, .grammar-map-head p { font: 1rem/1.6 var(--sans); margin: 0; color: var(--steel); }
.level-tabs { display: grid; grid-template-columns: repeat(4,minmax(0,1fr)); gap: 14px; }
.level-tab, .grammar-family { --atlas-tint: #e3eee6; --atlas-ink: #345b50; }
:is(.level-tab,.grammar-family)[data-color="1"] { --atlas-tint: #e4eaf7; --atlas-ink: #405888; }
:is(.level-tab,.grammar-family)[data-color="2"] { --atlas-tint: #f5e8d7; --atlas-ink: #795738; }
:is(.level-tab,.grammar-family)[data-color="3"] { --atlas-tint: #ede4f2; --atlas-ink: #6b4f7b; }
/* The level's pigment is a narrow edge on shared paper. Selection also has
   an outline and signal tint, so colour alone does not carry the state. */
.level-tab { position: relative; display: flex; flex-direction: column; align-items: flex-start; gap: 9px; min-width: 0; padding: 20px; border: 2px solid var(--rule); border-top: 3px solid var(--atlas-ink); border-radius: 22px 22px 22px 6px; background: var(--learning-surface,var(--galley)); color: var(--carbon); box-shadow: none; text-align: left; font: .8125rem/1.5 var(--sans); cursor: pointer; transition: transform 160ms ease,box-shadow 160ms ease; }
.level-tab-code { font: 700 1.75rem/1 var(--bill); }
.level-tab strong { font: 700 1rem/1.35 var(--sans); }
.level-tab > span:last-of-type { margin-top: auto; }
.level-tab[aria-selected="true"] { border-color: var(--pine); border-top-color: var(--atlas-ink); background: color-mix(in srgb,var(--signal) 10%,var(--learning-surface,var(--galley))); box-shadow: 0 3px 0 var(--rule); }
.level-tab:hover { transform: translateY(-3px); }
.level-tab progress { width: 100%; height: 5px; accent-color: var(--atlas-ink); border: 0; border-radius: 9px; overflow: hidden; }
.level-tab progress::-webkit-progress-bar { background: color-mix(in srgb,var(--atlas-ink) 18%,transparent); }
.level-tab progress::-webkit-progress-value { background: var(--atlas-ink); }
.level-caption { font: .8125rem/1.5 var(--sans); color: var(--steel); margin: 16px 0 28px; }
.level-panel[hidden], .grammar-family[hidden] { display: none !important; }
.level-intro { display: grid; grid-template-columns: minmax(0,1fr) 180px; align-items: center; gap: 24px; padding: 24px; border-radius: 24px; background: var(--paper); border: 1px solid var(--rule-room); }
.level-intro h2 { font: 700 1.7rem/1.2 var(--serif); margin: 8px 0; }
.level-intro p { font: .9375rem/1.6 var(--sans); margin: 0; color: var(--steel); }
.level-intro-art { display: block; overflow: hidden; border-radius: 18px; }
.level-intro-art svg { display: block; width: 100%; height: auto; }
.level-section-title { margin: 32px 0 10px; font: 700 1.45rem/1.3 var(--serif); }
.level-section-note { font: .9375rem/1.5 var(--sans); color: var(--steel); }
#v-course .course-atlas .syl-arcs { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 18px; padding: 0; border: 0; margin-top: 20px; }
#v-course .course-atlas .syl-arc { display: flex; align-items: center; gap: 18px; min-width: 0; padding: 22px; border: 1px solid var(--rule-room); border-radius: 22px; background: color-mix(in srgb,#e2ece5 65%,var(--paper)); color: var(--carbon); box-shadow: 0 5px 0 var(--rule-room); transition: transform 160ms ease; }
#v-course .course-atlas .syl-arc:nth-child(3n+2) { background: color-mix(in srgb,#e7e2f0 65%,var(--paper)); }
#v-course .course-atlas .syl-arc:nth-child(3n) { background: color-mix(in srgb,#f2e4d3 65%,var(--paper)); }
#v-course .course-atlas .syl-arc:hover { transform: translateY(-3px); }
#v-course .course-atlas .syl-mark { flex: 0 0 64px; width: 64px; height: 64px; background: none; border: 0; }
.syl-mark svg { width: 100%; height: 100%; }
#v-course .course-atlas .syl-arc-mid b { font: 700 1.2rem/1.3 var(--serif); }
#v-course .course-atlas .syl-arc-mid em { font: .8125rem/1.5 var(--sans); margin-top: 6px; }
#v-course .course-atlas .syl-card { border-radius: 18px; border: 1px solid var(--rule-room); padding: 20px; background: var(--paper); }
.level-extras { margin-top: 24px; border-top: 1px solid var(--rule-room); }
.level-extras > summary { padding: 18px 0; min-height: 44px; cursor: pointer; font: 700 .9375rem/1.5 var(--sans); }
.level-coming { padding: 28px; margin: 20px 0; border: 1px dashed var(--rule-room); border-radius: 20px; background: var(--paper); }
.level-coming strong { font: 700 1.3rem var(--serif); }
.level-coming p { font: 1rem/1.6 var(--sans); max-width: 60ch; }
.course-atlas .course-trail { margin-top: 24px; }
.course-atlas .trail-heading-art { display: none; }
.course-atlas .trail-heading h2 { font-size: 1.5rem; }
.grammar-map-head { grid-column: 1/-1; display: flex; justify-content: space-between; gap: 24px; align-items: center; margin-bottom: 10px; }
.grammar-map-head > span { flex: 0 0 84px; }
.grammar-map-head > span svg { display: block; width: 100%; height: auto; }
.grammar-family { grid-column: 1/-1; min-width: 0; border: 1px solid var(--rule-room); border-radius: 22px; background: var(--paper); overflow: clip; margin: 6px 0; }
.grammar-family > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 20px; padding: 22px; background: var(--atlas-tint); color: var(--atlas-ink); }
.grammar-family > summary::-webkit-details-marker { display: none; }
.grammar-family-art { flex: 0 0 68px; }
.grammar-family-art svg { display: block; width: 100%; height: auto; transition: transform 180ms ease; }
.grammar-family > summary:hover .grammar-family-art svg { transform: rotate(-5deg) translateY(-2px); }
.grammar-family-copy { display: grid; gap: 6px; min-width: 0; flex: 1; }
.grammar-family-copy strong { font: 700 1.4rem/1.25 var(--serif); }
.grammar-family-copy :is(span,small) { font: var(--type-body)/1.5 var(--sans); overflow-wrap:anywhere; }
.grammar-family-meter { border: 1px solid currentColor; border-radius: 50%; min-width: 3em; min-height: 3em; padding:.35em; display: grid; place-content: center; font: 700 var(--type-meta)/1.4 var(--sans); }
.grammar-family-lessons { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 16px; padding: 20px; }
.glist .grammar-family .glesson-card { min-width: 0; border: 1px solid var(--rule-room); border-radius: 16px; padding: 20px; background: var(--paper); min-height: 190px; box-shadow: 0 3px 0 var(--rule-room); }
.g-open-label { display: flex; align-items: center; gap: 10px; margin-top: 20px; color: var(--world-text,var(--steel)); font: 700 .8125rem/1.5 var(--sans); }
.g-open-label svg { width: 20px; height: 20px; }
:is(.level-tab,.grammar-family > summary):focus-visible { outline: 3px solid var(--carbon); outline-offset: 4px; }
[data-theme="dark"] :is(.level-tab,.grammar-family) { --atlas-tint: #293f3d; --atlas-ink: #d0e9db; }
[data-theme="dark"] :is(.level-tab,.grammar-family)[data-color="1"] { --atlas-tint: #303d59; --atlas-ink: #d8e3fc; }
[data-theme="dark"] :is(.level-tab,.grammar-family)[data-color="2"] { --atlas-tint: #4a3c31; --atlas-ink: #f5dfc2; }
[data-theme="dark"] :is(.level-tab,.grammar-family)[data-color="3"] { --atlas-tint: #44374e; --atlas-ink: #ebdbf5; }
[data-theme="dark"] #v-course .course-atlas .syl-arc { background: #293f3d; }
[data-theme="dark"] #v-course .course-atlas .syl-arc:nth-child(3n+2) { background: #44374e; }
[data-theme="dark"] #v-course .course-atlas .syl-arc:nth-child(3n) { background: #4a3c31; }
@media (max-width: 700px) {
  .level-tabs { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 12px; }
  .level-tab { padding: 16px; }
  .level-intro { grid-template-columns: minmax(0,1fr) 92px; padding: 18px; gap: 12px; }
  .level-intro h2 { font-size: 1.4rem; }
  .level-intro p { font-size: .875rem; }
  #v-course .course-atlas .syl-arcs, .grammar-family-lessons { grid-template-columns: 1fr; }
  .grammar-family > summary { padding: 16px; gap: 12px; }
  .grammar-family-art { flex-basis: 48px; }
  .grammar-family-copy strong { font-size: 1.2rem; }
  .grammar-family-meter { min-width: 3em; min-height: 3em; font-size: var(--type-meta); }
  .grammar-family-lessons { padding: 14px; }
  .grammar-map-head > span { flex-basis: 56px; }
}
@media (prefers-reduced-motion: reduce) {
  .level-tab, #v-course .course-atlas .syl-arc, .grammar-family-art svg { transition: none; transform: none !important; }
}
body.motion-low :is(.level-tab,.syl-arc,.grammar-family-art svg) { transition: none; transform: none !important; }
/* Section headers do not inherit the site's sticky masthead furniture. */
:is(.atlas-heading,.level-intro,.grammar-map-head) { position: static; top: auto; z-index: auto; box-shadow: none; }
.atlas-heading, .grammar-map-head { background: transparent; border: 0; padding: 0; }
.level-intro h2, .level-section-title { text-transform: none; letter-spacing: normal; }
.grammar-family:not([open]) { grid-column: auto; }
@media (max-width: 700px) { .grammar-family:not([open]) { grid-column: 1/-1; } }
@media (max-width: 700px) {
  .atlas-heading > :is(.smallcaps,p) { display: none; }
  .atlas-heading h2 { font-size: 1.65rem; margin: 0 0 18px; }
  .level-tab { padding: 14px; gap: 8px; }
}

/* Authored discoveries: a colourful shelf, then one quiet, stable work area. */
.workshop-shelf { margin: 32px 0; }
.workshop-return { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; width: 100%; padding: 18px 22px; margin: 20px 0; border: 1px solid var(--world-shadow); border-radius: 18px; background: var(--world-tint); color: var(--carbon); text-align: left; cursor: pointer; }
.workshop-return > span:first-child { font: 600 .8125rem/1.4 var(--sans); }
.workshop-return strong { flex: 1; font: 600 1.1rem/1.4 var(--serif); }
.workshop-return svg { width: 24px; height: 24px; }
.workshop-cards { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 16px; }
.workshop-card {
  --wk-tint: #e2eee6; --wk-edge: #a9c8b7;
  display: grid; grid-template-columns: 72px minmax(0,1fr) 24px; align-items: center;
  gap: 16px; min-height: 164px; padding: 20px; text-align: left;
  border: 1px solid var(--wk-edge); border-radius: 22px; background: var(--paper);
  color: var(--carbon); box-shadow: 0 5px 0 var(--wk-tint); cursor: pointer;
  transition: transform 160ms ease, box-shadow 160ms ease; overflow-wrap: anywhere;
}
.workshop-card[data-color="1"] { --wk-tint: #e1e8f7; --wk-edge: #b1bfdc; }
.workshop-card[data-color="2"] { --wk-tint: #f5e8d4; --wk-edge: #dcc199; }
.workshop-card[data-color="3"] { --wk-tint: #ebe3f3; --wk-edge: #c8b6db; }
.workshop-card-art { --art-ground: var(--wk-tint); display: block; width: 72px; }
.workshop-card-art svg { display: block; width: 100%; height: auto; }
.workshop-card-copy { display: grid; gap: 7px; min-width: 0; }
.workshop-card-copy .smallcaps { font: 700 .75rem/1.4 var(--sans); letter-spacing: .06em; }
.workshop-card-copy strong { font: 600 1.35rem/1.18 var(--serif); }
.workshop-card-copy > span:not(.smallcaps) { font: 400 .875rem/1.45 var(--sans); }
.workshop-card-copy em { font: 500 .8125rem/1.4 var(--sans); }
.workshop-card-go svg { width: 22px; height: 22px; }
.workshop-card:hover { transform: translateY(-3px); box-shadow: 0 8px 0 var(--wk-tint); }
.workshop-card:hover .art-object { transform: translateY(-3px) rotate(-3deg); transform-origin: center; }
.workshop-card:active { transform: translateY(2px); box-shadow: 0 2px 0 var(--wk-tint); }
.workshop-card:focus-visible { outline: 3px solid var(--world-deep); outline-offset: 4px; }
.workshop-complete .workshop-card-go { color: #3c7658; }
.gx-stage.wk-stage { overflow-y: auto; scroll-padding-block: 20px 110px; }
.wk-stage .wk-sheet { max-width: 820px; margin: auto; }
.wk-stage .lesson-compass { min-height: 0; }
.wk-stage .lesson-window { max-height: 132px; }
.wk-stage .lesson-window > svg { height: 132px; object-fit: contain; }
.wk-stage .wk-body { display: flex; flex-direction: column; gap: 16px; text-align: left; outline: none; }
.wk-body p { margin: 0; }
.wk-body :is(h3,h4) { text-transform: none; letter-spacing: 0; }
.wk-body .wk-goal { font: 600 1.1rem/1.5 var(--sans); }
.wk-body .wk-line { font: 500 clamp(1.35rem,3vw,1.85rem)/1.5 var(--serif); color: var(--carbon); overflow-wrap: anywhere; }
.wk-line mark { padding: 2px 5px; border-radius: 6px; background: var(--world-tint); color: inherit; }
.wk-blank { display: inline-block; min-width: 50px; padding: 0 10px; border-bottom: 2px solid var(--world-deep); text-align: center; }
.wk-body .wk-meaning { font: 500 1rem/1.5 var(--sans); color: var(--carbon); }
.wk-body .wk-insight { padding: 16px; border-radius: 14px; background: var(--world-tint); font: 400 1rem/1.6 var(--sans); }
.wk-reference, .wk-hint { border-top: 1px solid var(--world-shadow); padding-top: 12px; }
.wk-reference summary, .wk-hint summary { cursor: pointer; font: 600 .875rem/1.5 var(--sans); min-height: 36px; }
.wk-reference p, .wk-hint p { margin: 10px 0 0; font: 400 1rem/1.7 var(--sans); }
.wk-body .wk-note { font: 400 .8125rem/1.5 var(--sans); opacity: .8; }
.wk-task-heading { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.wk-revisit { font: 600 .8125rem/1.4 var(--sans); color: var(--world-text, var(--carbon)); }
.wk-options { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 12px; }
.wk-options .gx-opt { padding: 14px 12px; overflow-wrap: anywhere; font: 600 1.15rem/1.4 var(--serif); text-align: center; }
.wk-options .gx-opt::after { content: none !important; }
.wk-footer { position: sticky; bottom: 0; z-index: 4; display: flex; gap: 12px; justify-content: flex-end; align-items: center; min-height: 76px; padding: 14px 0 max(14px, env(safe-area-inset-bottom)); background: var(--paper); border-top: 1px solid var(--world-shadow); }
.wk-footer .btn { min-height: 48px; border-radius: 12px; font: 600 .9375rem/1.3 var(--sans); text-transform: none; letter-spacing: 0; }
.wk-footer .btn[hidden] { display: none; }
.wk-footer .btn:disabled { opacity: .5; cursor: default; }
.wk-stage :is(.wk-input,textarea) { display: block; width: 100%; min-width: 0; box-sizing: border-box; padding: 14px; border: 2px solid var(--world-shadow); border-radius: 12px; background: var(--paper); color: var(--carbon); font: 500 1.15rem/1.5 var(--sans); }
.wk-stage :is(.wk-input,textarea):focus { outline: 2px solid var(--world-deep); outline-offset: 2px; }
.wk-stage label { display: block; font: 600 .875rem/1.5 var(--sans); margin-bottom: 8px; }
/* the keyboard slot is a BLOCK: its toggle sits above the keys, never beside
   them. As a wrapping flex row it seated the toggle next to the key rows and
   squeezed every key to 11px (the owner's screenshot, 13 Sep 2026); the key
   sizes live once, in 104-daily-adventure.css, for every pad in the house. */
#wk-keys { margin-top: 12px; display: block; }
.wk-key { min-width: 40px; min-height: 42px; padding: 8px; border: 1px solid var(--world-shadow); border-radius: 8px; background: var(--world-tint); color: var(--carbon); font-size: 1rem; }
.wk-tiles, .wk-assembled { display: flex; flex-wrap: wrap; gap: 8px; }
.wk-assembled { min-height: 58px; padding: 12px; border-radius: 14px; background: var(--world-tint); align-items: center; }
.wk-assembled > span { font: 400 .875rem/1.4 var(--sans); }
.wk-token { padding: 10px 12px; min-height: 44px; border: 1px solid var(--world-shadow); border-radius: 10px; background: var(--paper); color: var(--carbon); font: 500 1rem/1.5 var(--sans); cursor: pointer; }
.wk-token:disabled { opacity: .3; cursor: default; }
.wk-token:focus-visible, .wk-key:focus-visible { outline: 2px solid var(--world-deep); outline-offset: 2px; }
.wk-verdict { padding: 16px; border-radius: 14px; background: var(--world-tint); font: 400 1rem/1.5 var(--sans); }
.wk-verdict:empty { display: none; }
.wk-verdict strong { display: block; margin-bottom: 8px; }
.wk-verdict p + p { margin-top: 8px; }
.wk-verdict small { display: block; margin-top: 8px; font-size: .875rem; }
.wk-correct { background: #dfede4; color: #264d3b; animation: wk-settle 180ms ease-out; }
.wk-correction { background: #f5e8d4; color: #614623; }
.wk-completion-art { width: 88px; align-self: center; }
.wk-transfer { margin-top: 12px; display: grid; gap: 12px; }
.wk-transfer h4 { margin: 0; font: 600 1.2rem/1.4 var(--serif); }
.wk-transfer label { margin: 0; }
.wk-review-date { font: 600 .875rem/1.5 var(--sans); }
#wk-note-status { font-size: .8125rem; min-height: 1.5em; }
@keyframes wk-settle { from { transform: translateY(3px); } to { transform: translateY(0); } }
[data-theme="dark"] .workshop-card { --wk-tint: #293f3d; --wk-edge: #53746b; }
[data-theme="dark"] .workshop-card[data-color="1"] { --wk-tint: #303d59; --wk-edge: #637495; }
[data-theme="dark"] .workshop-card[data-color="2"] { --wk-tint: #4a3c31; --wk-edge: #907b60; }
[data-theme="dark"] .workshop-card[data-color="3"] { --wk-tint: #44374e; --wk-edge: #8a739e; }
[data-theme="dark"] .wk-correct { background: #294538; color: #e1f2e6; }
[data-theme="dark"] .wk-correction { background: #4a3c31; color: #f5dfc2; }
[data-theme="dark"] .workshop-complete .workshop-card-go { color: #a7d6ba; }
@media (max-width:700px) {
  .workshop-cards { grid-template-columns: 1fr; }
  .workshop-card { min-height: 144px; padding: 16px; grid-template-columns: 64px minmax(0,1fr) 20px; gap: 12px; }
  .workshop-card-art { width: 64px; }
  .wk-stage .lesson-compass { grid-template-columns: 72px minmax(0,1fr); gap: 14px; }
  .wk-stage .lesson-window, .wk-stage .lesson-window > svg { height: 72px; }
  .wk-stage .lesson-compass h2 { font-size: 1.25rem; }
  .wk-stage .lesson-compass-copy { gap: 6px; }
  .wk-stage .gx-mid.wk-body { padding: 20px; }
  .wk-options { grid-template-columns: 1fr; gap: 9px; }
  .wk-options .gx-opt { min-height: 48px; padding: 10px; }
  .wk-footer .btn { flex: 1; }
}
@media (prefers-reduced-motion:reduce) {
  .workshop-card, .workshop-card .art-object { transition: none; }
  .workshop-card:hover, .workshop-card:hover .art-object, .workshop-card:active { transform: none; }
  .wk-correct { animation: none; }
}

/* The restoration desk: one scene in focus, a page built by recall. */
.wk-stage { background: var(--paper); }
.wk-stage .wk-sheet { max-width: 940px; padding: clamp(16px,4vw,40px); }
.wk-stage .lesson-compass { border: 0; padding: 0 0 18px; grid-template-columns: 88px minmax(0,1fr); }
.wk-stage .lesson-window, .wk-stage .lesson-window > svg { height: 88px; }
.wk-stage .lesson-compass h2 { font: 500 clamp(1.4rem,3vw,2rem)/1.2 var(--serif); }
.wk-stage .ink-challenge { position: relative; border-radius: 14px; padding: 12px 18px; background: var(--world-tint); }
.wk-stage .ink-challenge-heading { gap: 12px; }
.wk-stage .ink-message { margin: 5px 0; }
.wk-stage .ink-rules { font-size: .78rem; }
.wk-page-slots { display: flex; gap: 8px; margin: 20px 0; }
.wk-page-slots > span { flex: 1; min-width: 0; padding: 9px; border: 1px solid var(--world-shadow); border-radius: 3px 3px 9px 3px; opacity: .5; transition: background .25s, opacity .25s, transform .25s; }
.wk-page-slots b { font: 600 .65rem var(--sans); }
.wk-page-slots i { display: block; height: 2px; margin-top: 5px; background: currentColor; opacity: .3; }
.wk-page-slots i:last-child { width: 60%; }
.wk-page-slots .is-restored { opacity: 1; background: var(--world-tint); color: var(--world-text,var(--world-deep)); transform: translateY(-3px); }
.wk-opening { padding: clamp(12px,3vw,30px) 0; }
.wk-opening h3 { max-width: 540px; margin: 16px 0; font: 500 clamp(2rem,5vw,3.3rem)/1.12 var(--serif); }
.wk-opening .wk-goal { max-width: 580px; font-size: 1.05rem; line-height: 1.6; }
.wk-route { display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 12px; margin: 28px 0; }
.wk-route span { padding: 16px; border-top: 2px solid var(--world-shadow); font: 600 .88rem/1.4 var(--sans); }
.wk-route b { display: block; margin-bottom: 10px; font: 500 1.6rem var(--serif); color: var(--world-text,var(--world-deep)); }
.wk-stage .wk-body { gap: 20px; }
.wk-stage .wk-body > .wk-line { padding: clamp(20px,4vw,38px); border: 1px solid var(--world-shadow); border-left: 4px solid var(--world-deep); border-radius: 4px 18px 18px 4px; background: var(--paper); margin: 0; font-size: clamp(1.5rem,3.5vw,2rem); box-shadow: 0 8px 24px #00000006; }
.wk-stage[data-task="meaning"] .wk-options { display: grid; grid-template-columns: 1fr; }
.wk-stage[data-task="meaning"] .wk-options button { text-align: left; font: 500 1rem/1.5 var(--sans); padding: 18px 22px; }
.wk-stage[data-task="repair"] .wk-error { text-decoration: underline wavy; text-underline-offset: 5px; }
.wk-stage .wk-task-heading { font-size: .85rem; color: var(--world-text,var(--world-deep)); }
.wk-stage .wk-verdict { border-radius: 14px; padding: 20px; }
.wk-stage .wk-footer { gap: 12px; padding-top: 20px; }
.wk-stage .wk-footer .primary { min-height: 52px; }
@media (max-width: 560px) {
  .wk-stage .wk-sheet { padding: 14px; }
  .wk-page-slots { gap: 5px; margin: 12px 0; }
  .wk-page-slots > span { padding: 6px; }
  .wk-route { gap: 4px; }
  .wk-route span { padding: 10px 4px; font-size: .78rem; }
  .wk-stage .lesson-compass { grid-template-columns: 60px minmax(0,1fr); gap: 10px; }
  .wk-stage .lesson-window, .wk-stage .lesson-window > svg { height: 60px; }
}
@media (prefers-reduced-motion: reduce) {
  .wk-page-slots > span { transition: none; transform: none; }
}
.gx-stage.wk-stage .wk-sheet .lesson-compass { min-height: 0; margin: 4px 0 12px; padding: 12px; border-radius: 14px; }
.gx-stage.wk-stage .wk-sheet .ink-challenge { margin: 0; padding: 10px 16px; display: grid; grid-template-columns: 1fr auto; gap: 4px 16px; }
.gx-stage.wk-stage .wk-sheet .ink-challenge-heading { grid-row: 1; grid-column: 1 / -1; }
.gx-stage.wk-stage .wk-sheet .ink-message { margin: 0; font-size: .78rem; }
.gx-stage.wk-stage .wk-sheet .ink-tally { font-size: .72rem; }
.gx-stage.wk-stage .wk-sheet .ink-rules { grid-column: 1 / -1; margin: 0; font-size: .72rem; }
.gx-stage.wk-stage .wk-sheet .wk-page-slots { margin: 12px 0; }
.gx-stage.wk-stage .wk-sheet .wk-page-slots > span { padding: 4px 8px; }
.gx-stage.wk-stage .wk-sheet .wk-page-slots i { margin-top: 3px; }
.gx-stage.wk-stage .wk-sheet .wk-page-slots i:last-child { display: none; }
.gx-stage.wk-stage .wk-sheet .wk-body { padding: clamp(16px,3vw,28px); margin-top: 0; }
.gx-stage.wk-stage .wk-sheet .wk-options button { min-height: 52px; padding: 14px; }
.gx-stage.wk-stage .wk-sheet .wk-body > .wk-line { padding: 20px; }
@media (max-width: 560px) {
  .gx-stage.wk-stage .wk-sheet .ink-tally { grid-column: 1 / -1; }
  .gx-stage.wk-stage .wk-sheet .lesson-compass h2 { font-size: 1.15rem; }
  .gx-stage.wk-stage .wk-sheet .wk-body { padding: 16px; gap: 14px; }
}

/* A visible, earned progression through the literary house. */
.reader-passport { display:grid; grid-template-columns:100px minmax(0,1fr); gap:16px 24px; padding:28px; margin:20px 0 30px; border:1px solid var(--world-shadow,#cdc5b7); border-radius:8px 24px 24px 8px; background:var(--paper,#fffaf0); color:var(--carbon,#252c37); box-shadow:0 6px 0 var(--world-tint,#e5e7ef); }
.passport-emblem { position:relative; display:grid; place-items:center; width:96px; height:112px; border-radius:48px 48px 10px 10px; background:var(--world-tint,#e5e7ef); color:var(--world-deep,#394b74); border:2px solid currentColor; }
.passport-emblem > svg { width:70px; height:70px; }
.passport-emblem b { position:absolute; bottom:-9px; padding:4px 14px; border-radius:20px; background:var(--world-deep,#394b74); color:var(--paper,#fffaf0); font:700 1rem var(--sans); }
.passport-copy h3 { font:500 clamp(1.6rem,3vw,2.2rem)/1.2 var(--serif); margin:6px 0 10px; }
.passport-copy p { margin:7px 0 0; font:500 .82rem/1.4 var(--sans); }
.passport-copy progress { display:block; width:100%; height:9px; margin-top:12px; accent-color:var(--world-deep,#394b74); }
.passport-points { display:flex; flex-wrap:wrap; align-items:center; gap:12px; font:600 .86rem var(--sans); }
.passport-gain { color:var(--world-text,var(--world-deep,#394b74)); font:700 1.4rem var(--sans); }
.passport-promotion { color:var(--carbon); padding:5px 10px; border-radius:5px; background:#edcf83; }
.passport-collection { grid-column:1 / -1; padding-top:14px; border-top:1px solid var(--world-shadow,#cdc5b7); }
.passport-collection summary { cursor:pointer; font:600 .88rem var(--sans); min-height:32px; }
.passport-collection > p { max-width:680px; font:400 .8rem/1.5 var(--sans); margin-top:16px; }
.passport-seals { display:grid; grid-template-columns:repeat(auto-fit,minmax(115px,1fr)); gap:12px; padding:14px 0; }
.passport-seal { display:flex; flex-direction:column; align-items:center; text-align:center; gap:8px; padding:14px 8px; border:1px dashed var(--world-shadow,#cdc5b7); border-radius:12px; }
.passport-seal > div { display:grid; place-items:center; width:68px; height:68px; border-radius:50%; background:var(--world-tint,#e5e7ef); filter:grayscale(1); opacity:.6; }
.passport-seal svg { width:48px; height:48px; }
.passport-seal strong { font:600 .85rem/1.3 var(--sans); }
.passport-seal span,.passport-seal small { font:400 .75rem/1.4 var(--sans); }
.passport-seal.is-earned { border:1px solid #b38c42; background:linear-gradient(150deg,#f7e9c833,transparent); }
.passport-seal.is-earned > div { filter:none; opacity:1; background:#efd38f; border:3px double #947030; }
.passport-earned .passport-emblem { animation:passport-stamp 650ms cubic-bezier(.2,.8,.2,1) both; }
.passport-earned .passport-gain { animation:passport-rise 650ms ease-out both; }
.passport-seal.is-new { animation:passport-stamp 800ms 150ms cubic-bezier(.2,.8,.2,1) both; }
@keyframes passport-stamp { 0% { opacity:0; transform:translateY(-12px) scale(1.15) rotate(-6deg); } 65% { opacity:1; transform:translateY(2px) scale(.97) rotate(1deg); } 100% { transform:none; } }
@keyframes passport-rise { from { opacity:0; transform:translateY(10px); } to {opacity:1;transform:none;} }
@media(max-width:560px) { .reader-passport { grid-template-columns:64px minmax(0,1fr); padding:18px; gap:14px; } .passport-emblem {width:60px;height:80px;} .passport-emblem>svg{width:48px;height:48px;} .passport-copy h3{font-size:1.5rem;} .passport-seals{grid-template-columns:repeat(2,minmax(0,1fr));} }
/* 320px, Spanish bridge: «Registrador colegiado» (the rank) and «Un nuevo
   descubrimiento» (a small win) are wider than their column and spilled over
   the neighbouring card edge — measured 9 Sep 2026 on /es and /aleman. */
@media(max-width:380px) { .passport-copy h3, .profile-quest strong { overflow-wrap: anywhere; hyphens: auto; } }
@media(prefers-reduced-motion:reduce) { .passport-earned .passport-emblem,.passport-earned .passport-gain,.passport-seal.is-new {animation:none;} }
.wk-stakes { display:flex; justify-content:space-between; flex-wrap:wrap; gap:6px 12px; margin:12px 0 0; font:500 .78rem/1.4 var(--sans); color:var(--world-deep,#394b74); }
/* Orientation is quiet and optional; active text and questions stay clear. */
.study-orientation { max-width:1100px; margin:16px auto 24px; padding:14px 18px; border:1px solid var(--world-shadow,#d1cabe); border-radius:12px; background:var(--paper,#fffaf0); font-family:var(--sans); animation:study-arrive 180ms ease-out; }
.study-location { display:flex; align-items:baseline; gap:16px; flex-wrap:wrap; }
.study-location .smallcaps { color:var(--world-deep,#394b74); }
.study-location p { margin:0; font-size:.86rem; line-height:1.5; }
.study-route { margin-top:10px; font-size:.82rem; }
.study-route summary { cursor:pointer; min-height:28px; font-weight:600; }
.study-suggestion { display:flex; align-items:center; gap:16px; padding:16px 0; }
.study-suggestion-art { flex:0 0 48px; }
.study-suggestion-art svg { width:48px;height:48px; }
.study-suggestion > div { flex:1; }
.study-suggestion strong,.study-suggestion div>span { display:block; }
.study-suggestion p { margin:6px 0 0; line-height:1.5; }
.study-route nav { display:flex; flex-wrap:wrap; gap:8px; border-top:1px solid var(--world-shadow,#d1cabe); padding-top:10px; }
.study-route nav button { padding:10px 16px; background:transparent; color:var(--carbon); border:1px solid var(--world-shadow,#d1cabe); border-radius:8px; font:600 .82rem var(--sans); cursor:pointer; }
.study-route button:focus-visible,.study-route summary:focus-visible {outline:2px solid var(--world-deep,#394b74);outline-offset:3px;}
@keyframes study-arrive {from{opacity:0;transform:translateY(4px);}to{opacity:1;transform:none;}}
@media(max-width:560px){.study-orientation{margin:10px 0 16px;padding:12px;}.study-suggestion{flex-wrap:wrap;}.study-location{gap:6px;}}
@media(prefers-reduced-motion:reduce){.study-orientation{animation:none;}}
body.motion-low .study-orientation,body.motion-low .passport-earned .passport-emblem,body.motion-low .passport-earned .passport-gain,body.motion-low .passport-seal.is-new {animation:none;}
body.motion-low .wk-page-slots > span {transition:none;transform:none;}


/* The authored tutor is part of the lesson, with the room's own companion. */
.wk-coach-line { border-left:3px solid var(--ink); padding:10px 14px; margin:0 0 18px; font-size:13px; line-height:1.6; background:color-mix(in srgb,var(--ink) 5%,transparent); }
.tutor-panel { margin-top:16px; padding:18px; border:1px solid color-mix(in srgb,var(--ink) 25%,transparent); border-radius:14px; background:var(--paper,#faf6ed); }
.tutor-local-head { display:flex; gap:12px; align-items:flex-start; }
.tutor-local-head p { font-size:14px; line-height:1.6; margin:4px 0 0; }
.tutor-mark { display:grid; place-items:center; width:32px; height:32px; flex-shrink:0; border:1px solid currentColor; border-radius:50%; color:var(--ink); }
.tutor-prompts { display:flex; flex-wrap:wrap; gap:8px; margin:16px 0; }
.tutor-prompts button { background:transparent; border:1px solid color-mix(in srgb,var(--ink) 30%,transparent); border-radius:16px; padding:8px 12px; font:inherit; font-size:12px; color:inherit; cursor:pointer; }
.tutor-prompts button[aria-pressed="true"] { color:var(--paper,#fff);background:var(--ink);border-color:var(--ink); }
.tutor-local-body { font-size:14px; line-height:1.6; animation:tutor-arrival .22s ease-out; overflow-wrap:anywhere; }
.tutor-example { font-family:var(--serif,Georgia,serif); font-size:20px; }
.tutor-local-body mark { background:color-mix(in srgb,var(--ink) 15%,transparent); color:inherit; border-bottom:2px solid var(--ink); padding:1px 3px; border-radius:3px; }
.tutor-contrast { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.tutor-contrast > div { border:1px solid color-mix(in srgb,var(--ink) 18%,transparent);padding:12px;border-radius:8px; }
.tutor-contrast p:first-child { font-family:var(--serif,Georgia,serif);font-size:18px; }
.tutor-check { display:grid;gap:8px; }
.tutor-check button { text-align:left;border:1px solid color-mix(in srgb,var(--ink) 25%,transparent);background:transparent;color:inherit;padding:12px;border-radius:8px;font:inherit;cursor:pointer; }
.tutor-check .coach-right { border-color:#398269;background:#e0efe6; }
.tutor-check .coach-retry { opacity:.55; }
.tutor-check-result { font-weight:600; }
.tutor-panel small { font-size:12px;line-height:1.5; }
@keyframes tutor-arrival { from { opacity:0;transform:translateY(4px); } to { opacity:1;transform:translateY(0); } }
body.motion-low .tutor-local-body { animation:none; }
@media (prefers-reduced-motion:reduce) { .tutor-local-body { animation:none; } }
@media (max-width:540px) { .tutor-contrast {grid-template-columns:1fr;} .tutor-panel {padding:14px;} }

/* Learning overview: one centred measure and a short route to an action.
   Keep rewards and reference material available through native disclosures. */
#v-course.view { max-width: 1160px; margin-inline: auto; }
#v-course > .study-orientation { max-width: none; }
#v-course .cs-wings { margin-bottom: 20px; }
#v-course .cs-wing-tab { min-width: 0; }
#v-course .cs-wt-mid { min-width: 0; }
#v-course .cs-wt-mid b { font-family: var(--sans); font-size: 1rem; }
#v-course .cs-wt-mid em { line-height: 1.45; }
.course-progress { margin: 16px 0; border-block: 1px solid var(--rule-room); }
.course-progress > summary { cursor: pointer; padding: 14px 4px; font: 600 .875rem/1.5 var(--sans); }
.course-progress > summary > span { margin-left: 16px; color: var(--steel); font-weight: 400; }
.course-progress .reader-passport { margin: 0 0 20px; }
.course-atlas .atlas-heading { margin: 20px 0 16px; }
.course-atlas .atlas-heading h2 { font-size: 1.65rem; margin: 0 0 8px; }
.course-atlas .level-tabs { gap: 12px; }
.course-atlas .level-tab { padding: 14px 18px; gap: 5px; }
.course-atlas .level-caption { margin: 12px 0 20px; line-height: 1.5; }
.course-atlas .level-intro { grid-template-columns: minmax(0,1fr) 110px; padding: 18px 22px; }
.course-atlas .level-intro h2 { font-size: 1.5rem; }
.level-outline { font: .875rem/1.5 var(--sans); }
.level-outline summary { cursor: pointer; padding: 6px 0; }
.level-outline p { margin-top: 10px; max-width: 65ch; }
.course-atlas .workshop-shelf { margin-top: 24px; }
.course-atlas .workshop-card { grid-template-columns: 52px minmax(0,1fr) 22px; gap: 16px; min-height: 148px; }
.course-atlas .workshop-card-art { width: 52px; }
.course-atlas .workshop-card-copy strong { font-size: 1.25rem; line-height: 1.3; }
.course-progress > summary:focus-visible, .level-outline summary:focus-visible { outline: 2px solid var(--world-deep); outline-offset: 4px; }
.wk-opening .wk-goal { font: 500 1.15rem/1.55 var(--sans); max-width: 60ch; }
.wk-session-guide .wk-route { margin-top: 18px; }
@media (max-width: 800px) {
  .course-atlas .workshop-cards { grid-template-columns: minmax(0,1fr); }
  .course-atlas .workshop-card { min-height: 0; }
}
@media (max-width: 560px) {
  .course-progress > summary > span { display: block; margin: 4px 0 0 16px; }
  .course-atlas .level-intro { padding: 16px; grid-template-columns: minmax(0,1fr) 72px; gap: 12px; }
  .course-atlas .level-tab { padding: 12px; }
  #v-course .cs-wt-mid b { font-size: .875rem; }
  .course-atlas .workshop-card { padding: 16px; grid-template-columns: 40px minmax(0,1fr) 18px; gap: 12px; }
  .course-atlas .workshop-card-art { width: 40px; }
}
/* The older room heading shorthand reset auto margins on wide screens. */
.view > .room-head { margin-inline: auto !important; }
:is(#v-words,#v-grammar,#v-journey) > .room-head .rh-ru { font-size: .875rem !important; letter-spacing: .12em !important; }
:is(#v-words,#v-grammar,#v-journey) > .room-head h2 { font-size: clamp(1.8rem,3vw,2.6rem) !important; font-style: normal !important; color: var(--carbon) !important; }
.wk-sheet .wk-opening { padding: 8px 0; }
.wk-sheet .wk-opening h3 { font: 600 1rem/1.4 var(--sans); margin: 0 0 12px; }
.wk-opening > .wk-reference { margin-top: 18px; }
@media(min-width:1000px) {
  .study-orientation { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 24px; }
  .study-location { flex: 1; }
  .study-route { margin: 0 0 0 auto; }
  .study-route[open] { flex-basis: 100%; }
}

/* THE PASSPORT STRIP (Wave 4, 12 Sep 2026): the reader's standing, where the
   level tabs ask for it — one line under the atlas heading, the same emblem,
   paper and shadow as the passport it opens. */
#v-course .passport-strip { display:grid; grid-template-columns:auto minmax(0,1fr) auto; align-items:center; gap:14px 18px; width:100%; margin:0 0 22px; padding:14px 18px; border:1px solid var(--world-shadow,#cdc5b7); border-radius:8px 18px 18px 8px; background:var(--paper,#fffaf0); color:var(--carbon,#252c37); box-shadow:0 4px 0 var(--world-tint,#e5e7ef); text-align:left; font:inherit; cursor:pointer; }
#v-course .passport-strip:hover, #v-course .passport-strip:focus-visible { box-shadow:0 4px 0 var(--world-tint,#e5e7ef), 0 0 0 3px var(--world-tint,#e5e7ef); outline:none; }
#v-course .passport-strip-emblem { position:relative; display:grid; place-items:center; width:48px; height:60px; border:1px solid var(--world-shadow,#cdc5b7); border-radius:6px 6px 22px 22px; background:var(--world-tint,#e5e7ef); }
#v-course .passport-strip-emblem > svg { width:26px; height:26px; }
#v-course .passport-strip-emblem > b { position:absolute; right:-8px; bottom:-8px; min-width:22px; height:22px; padding:0 6px; border-radius:11px; background:var(--carbon,#252c37); color:var(--paper,#fffaf0); font-size:.8rem; line-height:22px; text-align:center; }
#v-course .passport-strip-copy { display:grid; gap:2px; min-width:0; }
#v-course .passport-strip-copy > strong { font-family:var(--font-display, inherit); font-size:1.15rem; letter-spacing:.02em; text-transform:uppercase; }
#v-course .passport-strip-copy > span:last-child { font-size:.95rem; opacity:.85; }
#v-course .passport-strip-go { font-size:.9rem; font-weight:600; white-space:nowrap; text-decoration:underline; text-underline-offset:3px; }
@media (max-width:560px) { #v-course .passport-strip { grid-template-columns:auto minmax(0,1fr); } #v-course .passport-strip-go { grid-column:2; justify-self:start; } }


/* ===== 101-arrival.css ===== */
/* The example precedes the routes in both DOM and visual order. Keep this
   final in the cascade: older phone overrides put the encounter last. */
#arrival-front {
  grid-template-areas: "intro encounter" "routes encounter" "method method";
  gap: 24px 40px;
  align-items: start;
}
#arrival-front .arrival-copy { grid-area: intro; display: block; }
#arrival-front .arrival-copy>.arrival-rooms { display:flex;flex-wrap:wrap;gap:4px;margin:0 0 24px;padding:0 0 16px;border-bottom:1px solid var(--rule); }
#arrival-front .arrival-book { grid-area: encounter; }
#arrival-front .arrival-routes { grid-area: routes; min-width: 0; }
#arrival-front .arrival-method { grid-area: method; }
#arrival-front .arrival-lede { margin-bottom: 0; }
.arrival-authors { font: .875rem/1.5 var(--serif); color: var(--world-text, var(--carbon)); margin: 12px 0 0; }
.arrival-access { border-block: 1px solid var(--rule-room); margin-top: 14px; font: .875rem/1.55 var(--sans); }
.arrival-access summary { cursor: pointer; padding-block: 12px; min-height: 44px; font-weight: 600; }
.arrival-access dl { margin: 0; }
.arrival-access dl > div { padding: 12px 0; border-top: 1px solid var(--rule-room); }
.arrival-access dt { font-weight: 700; margin-bottom: 4px; }
.arrival-access dd { margin: 0; }
.arrival-access summary:focus-visible { outline: 3px solid var(--world-text, var(--carbon)); outline-offset: 3px; }
.arrival-account-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 16px; }
.arrival-account-actions button { min-height: 44px; }
#startup-error { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 24px; background: var(--room); color: var(--carbon); overflow: auto; }
#startup-error > div { max-width: 540px; padding: 32px; border: 1px solid var(--rule-room); border-radius: 20px; background: var(--paper); }
#startup-error h1 { font: 700 2rem/1.2 var(--serif); text-transform: none; }
#startup-error p { font: 1rem/1.65 var(--sans); margin-block: 20px; }
#arrival-continue { margin-top: 16px; white-space: normal; text-align: left; }
.arrival-account-actions button:focus-visible, #arrival-continue:focus-visible {
  outline: 3px solid var(--world-text, var(--carbon)); outline-offset: 4px;
}
@media (max-width: 820px) {
  #arrival-front { grid-template-areas: "intro" "encounter" "routes" "method"; gap: 20px; }
  #arrival-front .arrival-copy, #arrival-front .arrival-routes { width: 100%; max-width: 640px; justify-self: center; }
}
@media (max-width: 560px) {
  #arrival-front { margin-block: 16px 32px; gap: 18px; }
  #arrival-title { font-size: clamp(2rem, 8vw, 2.5rem); margin-block: 12px; }
  #arrival-front .arrival-lede { font-size: .95rem; line-height: 1.5; margin-top: 0; }
  #arrival-front .arrival-eyebrow { font-size: var(--type-meta); }
  #arrival-front .arrival-first-friend { width: 44px; height: 44px; flex-shrink: 0; }
  #arrival-front .arrival-book { padding: 16px 20px; }
  #arrival-front .arrival-world { margin: -16px -20px 12px; height: 82px; }
  #arrival-front .arrival-world > svg { height: 100%; max-height: none; width: 100%; object-fit: cover; }
  #arrival-front .arrival-resident { width: 70px; height: 70px; left: calc(50% - 35px); top: 8px; }
  #arrival-front .arrival-book-ribbon { height: 36px; width: 22px; right: 16px; }
  #arrival-front .arrival-page-label { margin-block: 8px; }
  #arrival-front .arrival-book blockquote { font-size: 1.65rem; margin-block: 10px; }
  #arrival-front .arrival-page-foot { margin-top: 12px; }
  #arrival-front .arrival-page-foot svg { width: 24px; height: 24px; }
}


/* ===== 102-reading-challenge.css ===== */
/* The reading room's play language: paper, ink, a living resident and an
   impression earned by answering. State remains legible without motion. */
.gx-stage.gx-adventure {
  --lesson-peach: #f3e0cd; --lesson-lilac: #e8e4da;
}
.gx-adventure .lesson-compass { border-radius: 18px 18px 4px 4px; border-bottom: 3px solid var(--world-deep); }
.gx-adventure .gx-mid { border-radius: 4px 18px 18px 18px !important; }
.gx-adventure .ink-challenge {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 20px;
  padding: 14px 20px; border: 1px solid var(--world-shadow); border-left: 4px solid var(--world-deep);
  border-radius: 4px 12px 12px 4px; color: var(--carbon); background: var(--paper);
  font: .8125rem/1.45 var(--sans); grid-column: 1 / -1;
}
.ink-challenge-heading { display: flex; align-items: center; gap: 18px; grid-column: 1 / -1; justify-content: space-between; font-weight: 750; }
.ink-drops { display: inline-flex; gap: 5px; flex: 0 0 auto; }
.ink-drops svg { width: 18px; height: 25px; overflow: visible; }
.ink-full { fill: var(--world-deep); stroke: var(--world-deep); }
.ink-empty { fill: transparent; stroke: var(--world-shadow); stroke-width: 1.8; }
.ink-shine { fill: none; stroke: var(--paper); stroke-width: 1.7; stroke-linecap: round; }
.ink-empty .ink-shine { display: none; }
.ink-message { margin: 4px 0 6px; grid-column: 1 / -1; }
.ink-tally { display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center; }
.ink-tally b { font-variant-numeric: tabular-nums; }
.ink-combo { color: var(--world-text, var(--world-deep)); font-weight: 700; }
.ink-rules { align-self: start; }
.ink-rules summary { cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.ink-rules[open] { grid-column: 1 / -1; margin-top: 10px; }
.ink-rules p { max-width: 65ch; margin-bottom: 0; }
.ink-challenge[data-ink-state="refilled"] { border-left-color: #996a14; background: #fff5d9; }
.ink-challenge[data-ink-state="supported"] { border-left-color: #996a14; }
.ink-help-label { font: 600 .8125rem/1.5 var(--sans); color: #72551f; }
.ink-clue, .gx-adventure .gx-verdict.gx-supported, .wk-supported { background: #fff3d8; border-left: 3px solid #ad8433; padding: 14px 16px; border-radius: 4px 12px 12px 4px; color: #63481b; }
.gx-adventure .gx-opt.gx-assisted { background: #fff3d8; color: #63481b; border-color: #ad8433; box-shadow: none; }
.gx-adventure .ink-clue p { margin: 4px 0; }
.ink-unsure { background: transparent; }
.audio-button-status { display: block; flex-basis: 100%; margin-top: 6px; font: .8125rem/1.4 var(--sans); color: var(--world-text, var(--world-deep)); }
.audio-button-status:empty { display: none; }
.audio-notice { border-left: 3px solid #ad8433; padding: 10px 14px; background: #fff3d8; font: .875rem/1.5 var(--sans); color: #63481b; }
.audio-text-option { display: block; margin: 12px auto 0; }
[data-audio-state="loading"] { border-style: dashed !important; }
[data-audio-state="playing"] { outline: 3px solid var(--world-mid); outline-offset: 4px; }
[data-audio-state="retry"] { border-color: #a37230 !important; }
.gx-adventure .answer-listen { justify-self: start; min-width: 140px; }
#reading-route { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding: 0 0 16px; margin-bottom: 12px; border-bottom: 1px solid var(--rule); }
#reading-route .btn { min-height: 44px; font: 600 .8125rem/1.4 var(--sans); padding: 10px 14px; }
#reading-route > span { margin-left: auto; font: .75rem/1.4 var(--sans); color: var(--muted); }
.reading-contents { width: min(580px, calc(100vw - 32px)); max-height: min(760px, calc(100dvh - 40px)); box-sizing: border-box; border: 1px solid var(--rule); border-radius: 16px; padding: 24px; background: var(--paper); color: var(--carbon); box-shadow: 0 20px 100px #1e24384d; }
.reading-contents::backdrop { background: #18203499; backdrop-filter: blur(3px); }
.reading-contents header { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.reading-contents h2 { font: 700 2rem/1.2 var(--bill); margin: 18px 0 6px; }
.reading-contents > p { margin: 0 0 20px; color: var(--muted); }
.reading-contents ol { list-style: none; margin: 0; padding: 0; }
.reading-contents li + li { margin-top: 8px; }
.reading-contents [data-chapter] { display: flex; align-items: center; gap: 16px; width: 100%; text-align: left; padding: 14px; border: 1px solid var(--rule); border-radius: 10px; background: transparent; color: var(--carbon); cursor: pointer; }
.reading-contents [data-chapter]:hover { background: var(--world-tint); }
.reading-contents [aria-current="page"] { background: var(--world-tint); border-color: var(--world-deep); }
.reading-contents strong { font: 600 1.0625rem/1.4 var(--serif); display: block; }
.reading-contents small { display: block; margin-top: 4px; font: .75rem/1.4 var(--sans); }
.contents-number { flex: 0 0 28px; font: 700 1.125rem/1 var(--bill); color: var(--world-text, var(--world-deep)); }
.reading-contents footer { padding-top: 20px; }
.reading-contents .btn { min-height: 44px; font: 600 .8125rem/1.4 var(--sans); }
.reading-contents button:focus-visible { outline: 3px solid var(--world-deep); outline-offset: 3px; }
@keyframes ink-recover { 0% { transform: translateY(-6px) scale(.8); } 65% { transform: translateY(2px) scale(1.12); } 100% { transform: none; } }
@media (prefers-reduced-motion: no-preference) {
  .motion-full .ink-challenge[data-ink-state="refilled"] .ink-full { animation: ink-recover 420ms ease-out both; }
}
@media (max-width: 560px) {
  .gx-adventure .ink-challenge { padding: 10px 14px; column-gap: 10px; }
  .ink-rules { grid-column: 1 / -1; margin-top: 7px; }
  .ink-message { margin-top: 2px; }
  .ink-drops svg { width: 16px; height: 22px; }
  .reading-contents { padding: 18px; }
  .reading-contents h2 { font-size: 1.65rem; }
  #reading-route > span { width: 100%; margin-left: 0; }
  #btn-home2 { font-size: .75rem; padding-inline: 7px; }
}
.reading-contents header { position: sticky; top: -24px; padding-block: 12px; margin-top: -12px; background: var(--paper); z-index: 1; }
@media (max-width: 560px) { .reading-contents header { top: -18px; } }
[data-theme="dark"] :is(.ink-clue, .wk-supported, .audio-notice, .gx-adventure .gx-verdict.gx-supported, .gx-adventure .gx-opt.gx-assisted, .ink-challenge[data-ink-state="refilled"]) { background: #3c3323; color: #f1dba8; }
[data-theme="dark"] .ink-help-label { color: #e3c78d; }
.wk-audio { font: .8125rem/1.5 var(--sans); margin: 12px 0; color: var(--world-text, var(--world-deep)); }
.gx-adventure .gx-assisted::after { content: none !important; }
#shelf-controls { display: flex; flex-wrap: wrap; gap: 16px; align-items: end; margin-bottom: 24px; }
#shelf-controls label { display: grid; gap: 8px; flex: 1 1 280px; min-width:0; font: 600 var(--type-control)/1.5 var(--sans); }
#shelf-search { box-sizing:border-box; width:100%; min-width:0; border: 1px solid var(--rule); border-radius: 10px; min-height: 46px; padding: 10px 14px; background: var(--paper); color: var(--carbon); font: var(--type-control)/1.5 var(--sans); }
.shelf-filters { display: flex; flex-wrap: wrap; gap: 8px; }
.shelf-filters button { min-height: 46px; max-width:100%; white-space:normal; overflow-wrap:anywhere; padding: 10px 14px; border: 1px solid var(--rule); border-radius: 10px; background: var(--paper); color: var(--carbon); font: 600 var(--type-control)/1.5 var(--sans); cursor: pointer; }
.shelf-filters button[aria-pressed="true"] { background: var(--play); color: var(--paper); }
#shelf-results { flex-basis: 100%; margin: 0; font: var(--type-meta)/1.5 var(--sans); }
#shelf .book[hidden] { display: none !important; }
.book .b-translation { text-align: center; font: 600 var(--type-body)/1.5 var(--serif); overflow-wrap:anywhere; padding: 10px 6px 0; }

/* Course choices lead to a concrete lesson at the selected level. */
.level-fit { font-weight: 400; line-height: 1.45; }
.level-adventure { display: grid; grid-template-columns: minmax(0, 1fr) 230px; gap: 28px;
  padding: clamp(22px, 3vw, 38px); margin: 22px 0 28px; border: 1px solid var(--rule);
  border-bottom: 5px solid var(--play); border-radius: 20px;
  background: linear-gradient(125deg, var(--paper) 40%, #e4ebf6); color: var(--carbon); }
.level-adventure[data-level="a1"] { background: linear-gradient(125deg, var(--paper) 40%, #e2eee4); }
.level-adventure[data-level="b1"] { background: linear-gradient(125deg, var(--paper) 40%, #f3e6d2); }
.level-adventure[data-level="b2"] { background: linear-gradient(125deg, var(--paper) 40%, #ece2f4); }
.adventure-copy { min-width: 0; }
.adventure-copy h2 { font: 700 clamp(1.5rem, 2.5vw, 2.2rem)/1.2 var(--serif); margin: 12px 0; }
.adventure-copy > p { font: 1rem/1.6 var(--sans); max-width: 55ch; margin: 0 0 18px; }
.adventure-copy > .btn { min-height: 48px; font: 700 .95rem/1.4 var(--sans); text-transform: none; letter-spacing: 0; border-radius: 10px; }
.adventure-meta { display: block; font: .75rem/1.5 var(--sans); margin-top: 10px; }
.adventure-preview { font: .875rem/1.5 var(--sans); margin: 0 0 18px; }
.adventure-preview summary { cursor: pointer; text-decoration: underline; text-underline-offset: 4px; }
.adventure-preview p { font: 1.1rem/1.5 var(--serif); }
.adventure-scene { align-self: center; text-align: center; min-width: 0; }
.adventure-scene > svg { width: 100%; height: auto; }
.adventure-scene > span { display: block; font: 600 .875rem/1.5 var(--sans); margin: 12px 0; }
.level-browse { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; }
.level-browse h3 { font: 700 1.25rem/1.3 var(--serif); margin: 0; }
.level-browse label { display: grid; gap: 6px; font: .75rem/1.3 var(--sans); }
.course-search { min-height: 44px; width: min(320px,100%); padding: 10px 14px; border: 1px solid var(--rule); border-radius: 10px; font: .875rem/1.4 var(--sans); background: var(--paper); color: var(--carbon); }
.course-search-empty { width: 100%; padding: 20px; border: 1px dashed var(--rule); border-radius: 10px; font: .875rem/1.5 var(--sans); }
.course-atlas [hidden], #practice-challenge[hidden] { display: none !important; }
.course-atlas .trail-heading, .course-atlas .trail-destination { display: none; }
.course-atlas .learning-trail { grid-template-columns: repeat(2,minmax(0,1fr)); gap: 14px; }
.course-atlas .trail-go { display: grid; grid-template-columns: 48px minmax(0,1fr) 20px; align-items: center;
  min-height: 112px; padding: 18px; gap: 14px; border-radius: 14px; }
.course-atlas .trail-landscape { display: none; }
.course-atlas .trail-folio { position: static; width: 40px; height: 40px; display: grid; place-items: center; background: var(--paper); }
.course-atlas .trail-copy { padding: 0; min-width: 0; }
.course-atlas .trail-copy > strong { font: 700 1.0625rem/1.4 var(--serif); }
.course-atlas .trail-arrow { position: static; }
.is-searching .level-section-title, .is-searching .level-section-note { display: none; }
.is-searching .syl-cards, .is-searching .workshop-cards, .is-searching .syl-arcs { margin-block: 12px; }
.level-tab:focus-visible, .course-search:focus-visible, .adventure-copy button:focus-visible { outline: 3px solid var(--play); outline-offset: 4px; }

/* Practice is a chosen round with a visible finish, rather than a backlog. */
.practice-rounds { border: 0; padding: 0; margin: 24px auto; max-width: 720px; display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 10px; }
.practice-rounds legend { width: 100%; margin-bottom: 12px; font: 600 1rem/1.5 var(--sans); }
.practice-rounds label { position: relative; cursor: pointer; }
.practice-rounds input { position: absolute; inset: 12px auto auto 12px; accent-color: var(--play); }
.practice-rounds label > span { display: grid; gap: 6px; padding: 18px 12px 18px 34px; min-height: 90px; box-sizing: border-box; border: 1px solid var(--rule); border-radius: 12px; background: var(--paper); text-align: left; font: .875rem/1.4 var(--sans); }
.practice-rounds small { font-size: .75rem; }
.practice-rounds input:checked + span { background: #e5ecf8; border: 2px solid var(--play); padding: 17px 11px 17px 33px; }
.practice-rounds input:focus-visible + span { outline: 3px solid var(--play); outline-offset: 3px; }
#v-review.practice-lobby #practice-challenge { display: none; }
#practice-challenge { max-width: 760px; margin: 0 auto 22px; }
#practice-challenge .ink-challenge { background: var(--paper); padding: 16px; border: 1px solid var(--rule); border-radius: 14px; color: var(--carbon); font: .8125rem/1.4 var(--sans); }
.challenge-stamps { grid-column: 1/-1; display: grid; grid-template-columns: repeat(3,minmax(0,1fr)); gap: 8px; margin-top: 10px; }
.challenge-stamp { display: flex; align-items: center; gap: 8px; padding: 8px; border: 1px dashed var(--rule); border-radius: 10px; font: 600 .75rem/1.3 var(--sans); }
.challenge-stamp > b { flex: 0 0 26px; display: grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; background: var(--paper); border: 1px solid var(--rule); }
.challenge-stamp small { display: block; font-size: .6875rem; font-weight: 400; margin-top: 3px; }
.challenge-stamp.is-earned { background: #f9ebc5; border-style: solid; border-color: #c09a48; color: #63471a; }
.challenge-stamp.is-earned > b { color: #79520c; border-color: #c09a48; }
.challenge-earned { grid-column: 1/-1; display: block; padding: 8px 0 0; font: 700 .8125rem/1.4 var(--sans); color: #79520c; }
#practice-challenge .ink-drops svg { width: 14px; height: 20px; }
#practice-challenge .ink-rules { margin-top: 8px; }
.gx-adventure .wk-stakes { display: none; }
.gx-adventure .wk-page-slots { margin-block: 0; gap: 6px; }
.gx-adventure .wk-page-slots > span { height: 26px; padding: 4px 8px; }
.gx-adventure .wk-page-slots > span > i { display: none; }
.lesson-demonstration { margin-top: 22px; padding: 20px; border-left: 4px solid var(--world-deep); border-radius: 4px 12px 12px 4px; background: var(--world-tint); }
.lesson-demonstration > p:first-of-type { font: 600 1.3rem/1.6 var(--serif); }
.lesson-demonstration > p { margin: 8px 0; }
.lesson-demonstration > div { padding-top: 12px; font: .875rem/1.6 var(--sans); }
.lesson-demonstration mark { color: inherit; background: #f6dfa0; padding: 2px 4px; border-radius: 4px; }
.answer-connection { margin: 12px 0; padding: 16px 20px; background: var(--world-tint); border-radius: 12px; }
.answer-connection p { margin: 6px 0; font: 1.125rem/1.5 var(--serif); }
.answer-connection small { font: .8125rem/1.5 var(--sans); }
#v-review .rev-btns button { font: 700 1rem/1.4 var(--sans); text-transform: none; letter-spacing: 0; min-height: 48px; border-radius: 10px; padding: 12px 20px; }
@keyframes discovery-stamp { from { transform: scale(1.08) rotate(-2deg); } to { transform: none; } }
@keyframes adventure-bob { 0%,100% { transform: translateY(0) rotate(-1deg); } 50% { transform: translateY(-5px) rotate(1deg); } }
@media (prefers-reduced-motion: no-preference) {
  .motion-full .adventure-scene > svg { animation: adventure-bob 5s ease-in-out infinite; }
  .motion-full .challenge-milestone .is-earned { animation: discovery-stamp 450ms cubic-bezier(.2,.8,.3,1.2); }
  .motion-full .course-atlas :is(.trail-go,.workshop-card,.syl-card) { transition: transform 180ms ease, box-shadow 180ms ease; }
  .motion-full .course-atlas :is(.trail-go,.workshop-card,.syl-card):hover { transform: translateY(-3px); }
}
[data-theme="dark"] .level-adventure { background: linear-gradient(125deg, var(--paper), #30374c); }
[data-theme="dark"] .practice-rounds input:checked + span { background: #30374c; }
[data-theme="dark"] .challenge-stamp.is-earned { background: #4b3c20; color: #f2d595; }
[data-theme="dark"] .challenge-earned { color: #f2d595; }
[data-theme="dark"] .lesson-demonstration mark { background: #594c2b; }
@media (max-width: 700px) {
  .level-adventure { grid-template-columns: minmax(0,1fr) 100px; gap: 12px; padding: 22px; }
  .adventure-scene { align-self: start; }
  .adventure-scene > span { font-size: .75rem; }
  .course-atlas .learning-trail { grid-template-columns: 1fr; }
  .practice-rounds { grid-template-columns: 1fr; }
  .practice-rounds label > span { min-height: 0; padding-block: 12px; }
  .practice-rounds input:checked + span { padding-block: 11px; }
  .challenge-stamp { flex-direction: column; text-align: center; padding: 6px; }
  .challenge-stamp small { display: none; }
  .level-browse label, .course-search { width: 100%; }
}
@media (max-width: 420px) {
  .level-adventure { display: block; }
  .adventure-scene { display: none; }
  .adventure-copy > .btn { width: 100%; }
}

/* The browsing rooms share a warm page; the lesson keeps its own scene.
   A short rail and a featured lesson put the first action on the first screen. */
html:not([data-theme="dark"]) body:has(:is(#v-home,#v-course,#v-library,#v-review,#v-words).show) {
  background-color: var(--room) !important;
  background-image: none !important;
  background-size: 100% 100% !important;
  background-repeat: no-repeat !important;
}
body:has(:is(#v-home,#v-course,#v-library,#v-review,#v-words).show)::before,
#v-course::before { display: none; }
.bar .nav-tabs button { font-family: var(--sans); font-size: var(--type-control); font-weight: 650; letter-spacing: .015em; text-transform: none; border-radius: 8px; }
#v-course > .study-orientation { display: none; }
#v-course > .masthead { margin-top: 20px; padding-block: 8px 14px; border: 0; }
#v-course > .masthead::after { display: none; }
#v-course > .masthead h1 { font: 600 clamp(1.8rem,3vw,2.45rem)/1.2 var(--serif); letter-spacing: -.025em; color: var(--carbon); }
#v-course .cs-wings { gap: 8px; padding: 0 0 14px; margin: 0 0 24px; border-bottom: 1px solid var(--rule); }
#v-course .cs-wing-tab { min-height: 52px; padding: 10px 14px; gap: 12px; border-radius: 12px; border: 1px solid transparent; background: transparent; box-shadow: none; }
#v-course .cs-wing-tab[aria-selected="true"] { background: var(--paper); border-color: var(--rule); box-shadow: 0 3px 0 #1d2d3a0c; }
#v-course .cs-wt-dev { flex-basis: 32px; width: 32px; height: 32px; }
#v-course .cs-wt-dev svg { width: 32px; height: 32px; }
#v-course .cs-wt-mid b { font: 700 .9375rem/1.3 var(--sans); }
#v-course .cs-wt-mid em { font: .75rem/1.4 var(--sans); }
#v-course .atlas-heading { margin: 0 0 22px; }
#v-course .atlas-heading h2 { font: 600 1.35rem/1.3 var(--serif); letter-spacing: -.015em; margin: 0 0 6px; }
#v-course .atlas-heading p { font-size: .875rem; }
#v-course .level-adventure { margin-top: 0; border: 1px solid #273c4d19; border-bottom: 4px solid #273c4d35; border-radius: 24px; }
#v-course .adventure-copy h2 { text-transform: none; font: 600 clamp(1.7rem,2.5vw,2.25rem)/1.18 var(--serif); letter-spacing: -.025em; }
#v-course .adventure-copy > p { font-weight: 400; font-size: .9375rem; }
#v-course .adventure-copy > .btn { border: 0; background: #315b51; color: #fffdf7; box-shadow: 0 4px 0 #203c35; padding: 14px 22px; }
#v-course .adventure-copy > .btn:hover { background: #3d7164; }
#v-course .adventure-copy > .btn:active { transform: translateY(2px); box-shadow: 0 2px 0 #203c35; }
#v-course .level-browse h3 { text-transform: none; font: 600 1.2rem/1.3 var(--serif); }
#v-course .course-atlas .workshop-card { border: 1px solid #24384322; background: var(--paper); box-shadow: 0 3px 0 #2438430d; border-radius: 16px; padding: 20px; gap: 14px; }
#v-course .course-atlas .workshop-card:hover { border-color: var(--play); box-shadow: 0 6px 18px #24384312; }
#v-course .workshop-card-art { width: 46px; height: 46px; flex-basis: 46px; }
#v-course .workshop-card-copy strong { font: 600 1.0625rem/1.4 var(--serif); }
#v-course .workshop-card-copy > span:not(.smallcaps) { font: .8125rem/1.5 var(--sans); font-weight: 400; }
#v-course .workshop-card-copy .smallcaps { font-size: .6875rem; letter-spacing: .09em; color: var(--steel); }
#v-course .level-section-title { font: 600 1.2rem/1.4 var(--serif); }
#v-course .level-section-note { font-size: .8125rem; font-weight: 400; }
#v-course .course-progress { margin-top: 32px; }
#v-course .level-tab { border-radius: 14px; gap: 5px; padding: 14px 16px; box-shadow: none; }
#v-course .level-tab[aria-selected="true"] { box-shadow: inset 3px 0 var(--atlas-ink), 0 3px 0 #24384310; }
#v-course .level-tab-code { font: 700 .875rem/1.4 var(--sans); }
#v-course .level-tab strong { font: 600 .9375rem/1.4 var(--serif); }
#v-course .level-tab .level-fit { font-size: .75rem; }
#v-course .level-tab progress { margin-top: 4px; }
#v-course .level-caption { font: .6875rem/1.5 var(--sans); }
#v-course .level-panel { padding-top: 0; margin-top: 0; }
#v-course .level-tab:not([aria-selected="true"]) .level-fit { display: none; }
.wk-body p, .wk-body .lesson-demonstration > div { font-weight: 400; }
.wk-body .wk-goal { font-weight: 600; }
.gx-adventure .ink-tally { display: none; }
.challenge-stamp small { display: none; }
.gx-adventure .ink-challenge, #practice-challenge .ink-challenge { padding: 12px 16px; }
.gx-adventure .ink-rules { margin-top: 8px; grid-column: 1/-1; font-size: .75rem; }
.gx-adventure .ink-challenge .ink-message { margin: 2px 0 0; font-size: .75rem; }
.gx-adventure .challenge-stamps { margin-top: 8px; }
.gx-adventure .challenge-stamp { padding: 5px 8px; font-size: .6875rem; }
.gx-adventure .challenge-stamp > b { width: 20px; height: 20px; flex-basis: 20px; }
@media (min-width: 1000px) {
  #v-course .course-atlas { display: grid; grid-template-columns: 218px minmax(0,1fr); column-gap: 30px; align-items: start; }
  #v-course .atlas-heading { grid-column: 1/-1; }
  #v-course .level-rail { grid-column: 1; grid-row: 2; align-self: stretch; min-width: 0; }
  #v-course .level-tabs { display: flex; flex-direction: column; gap: 10px; }
  #v-course .level-caption { grid-column: 1; grid-row: 3; }
  #v-course .level-panel { grid-column: 2; grid-row: 2 / span 3; min-width: 0; }
  #v-course .level-adventure { grid-template-columns: minmax(0,1fr) 180px; gap: 18px; padding: 28px; }
  #v-course .adventure-scene > svg { overflow: visible; }
}
@media (max-width: 999px) {
  #v-course .level-fit { display: none; }
  #v-course .level-tab > span:last-of-type { font-size: .6875rem; }
  #v-course .level-caption { margin-block: 12px 20px; }
}
@media (max-width: 700px) {
  #v-course > .masthead { margin-top: 12px; }
  #v-course .cs-wing-tab { padding: 10px; }
  #v-course .cs-wt-dev, #v-course .cs-wt-mid em { display: none; }
  #v-course .cs-wt-mid b { font-size: .8125rem; }
  #v-course .level-tabs { gap: 8px; }
  #v-course .level-tab { padding: 10px 12px; }
  #v-course .level-tab-code { font-size: .75rem; }
  #v-course .level-tab strong { font-size: .8125rem; }
  #v-course .atlas-heading p { font-size: .8125rem; }
}

/* During a question, leave the foreground to the sentence and its choices. */
.gx-stage.wk-stage .wk-sheet { gap: 16px; }
.gx-stage.wk-stage .wk-sheet[data-phase="question"] .wk-page-slots,
.gx-stage.wk-stage .wk-sheet[data-phase="question"] .wk-coach-line { display: none; }
.gx-stage.wk-stage .wk-sheet .lesson-compass h2 { font-size: clamp(1.2rem,2vw,1.65rem); }
.gx-stage.wk-stage .wk-sheet .lesson-window { height: 64px; }
.gx-stage.wk-stage .wk-sheet .lesson-compass { margin-bottom: 0; }
.gx-stage.wk-stage .wk-sheet .wk-body { gap: 14px; }
.gx-stage.wk-stage .wk-sheet .wk-body > .wk-line { font-size: clamp(1.3rem,2.3vw,1.85rem); padding: 16px; margin-block: 4px; }
.gx-stage.wk-stage .wk-sheet .wk-options { gap: 10px; }
.gx-stage.wk-stage .wk-sheet .wk-footer { position: static; min-height: 0; padding-block: 12px; }
.course-search { border-radius: 10px; padding: 12px; }
@media (max-width: 700px) {
  #v-course .level-tab > strong,
  #v-course .level-tab > span:not(.level-tab-code),
  #v-course .level-tab > progress { display: none; }
  #v-course .level-tab { min-height: 48px; justify-content: center; }
  #v-course > .masthead { margin-bottom: 12px; padding-bottom: 6px; }
  #v-course .cs-wings { margin-bottom: 20px; }
  #v-course .atlas-heading { margin-bottom: 16px; }
}
/* Keep browsing within reach, including when a reader has a saved place. */

#v-library #resume-hero { display: block; padding: 22px; border: 1px solid var(--rule); border-radius: 18px; box-shadow: 0 4px 0 #24384315; }
#v-library #resume-hero .hero-vol-wrap { display: none; }
#v-library #resume-hero .hero-copy { min-width: 0; width: 100%; }
@media (max-width: 700px) {
  #v-library #resume-hero { display: block; margin-block: 18px; }
  #v-library #resume-hero .hero-copy blockquote { display: none; }
  #v-library .masthead { margin-block: 16px; }
}

/* A small, optional encounter belongs beside the sentence that prompted it. */
#reading-discovery { display: grid; gap: 10px; clear: both; margin: 18px 0 24px; padding: 16px 18px; border: 1px solid var(--rule); border-left: 3px solid #547364; border-radius: 4px 16px 16px 16px; background: var(--paper); color: var(--carbon); font: 400 .875rem/1.5 var(--sans); text-indent: 0; letter-spacing: 0; box-shadow: 0 3px 0 #253c3310; }
#reading-discovery *, #reading-discovery button { text-indent: 0; }
#reading-discovery [hidden] { display: none !important; }
#reading-discovery button, .discovery-return button { min-height: 44px; border-radius: 10px; padding: 10px 14px; cursor: pointer; color: inherit; background: transparent; border: 1px solid var(--rule); box-shadow: none; font: 600 .8125rem/1.4 var(--sans); letter-spacing: 0; text-transform: none; }
#reading-discovery button:focus-visible, .discovery-return button:focus-visible { outline: 3px solid var(--play); outline-offset: 3px; }
.rd-invite-copy { font-weight: 500; }
.rd-invite-actions, .rd-support { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
#reading-discovery :is(.rd-start,.rd-continue), .discovery-return button { background: #315b51; color: #fffdf7; border-color: #315b51; }
#reading-discovery .rd-dismiss, #reading-discovery .rd-hint, #reading-discovery .rd-save { border-color: transparent; text-decoration: underline; text-underline-offset: 3px; }
#reading-discovery.is-open { padding: clamp(18px,3vw,22px); gap: 14px; border-left-width: 4px; }
#reading-discovery, #reading-discovery .rd-result, #reading-discovery .rd-clue, #text .sent[tabindex="-1"] { scroll-margin-block: 110px 100px; }
.rd-eyebrow { display: flex; justify-content: space-between; align-items: center; color: #527161; font: 650 .6875rem/1.4 var(--sans); letter-spacing: .09em; text-transform: uppercase; }
.rd-eyebrow > span { font-size: 1.3rem; }
#reading-discovery .rd-question { font: 600 clamp(1.3rem,2vw,1.65rem)/1.3 var(--serif); }
#reading-discovery .rd-source { display: block; padding: 12px 14px; border-radius: 10px; background: var(--galley); font: 400 clamp(1.1rem,1.6vw,1.25rem)/1.5 var(--serif); }
.rd-options { display: grid; gap: 8px; }
#reading-discovery .rd-options button { text-align: left; min-height: 48px; padding: 13px 16px; background: var(--paper); }
#reading-discovery .rd-options button:hover:not(:disabled) { border-color: #547364; background: #eff4ed; }
#reading-discovery .rd-options button:disabled { opacity: .6; cursor: default; }
#reading-discovery .rd-options button.is-correct { opacity: 1; background: #e8f0e5; border: 2px solid #547364; color: #294837; }
#reading-discovery .rd-options button.is-missed { opacity: 1; border-style: dashed; }
.rd-clue { padding: 12px; border-left: 2px solid #aa8132; background: var(--galley); }
.rd-result { display: grid; gap: 12px; padding-top: 16px; border-top: 1px solid var(--rule); }
.rd-result > strong { font: 600 1.125rem/1.4 var(--serif); }
.rd-result > span { font-weight: 400; }
#reading-discovery .rd-continue { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-top: 4px; }
.rd-continue svg { width: 18px; height: 18px; }
.rd-result small { font: 400 .75rem/1.5 var(--sans); text-align: center; color: var(--steel); }
.discovery-return { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 18px; margin-bottom: 22px; padding: 22px; border: 1px solid var(--rule); border-radius: 16px; background: var(--paper); }
.discovery-return > span { display: grid; gap: 6px; font: 400 .875rem/1.5 var(--sans); }
.discovery-return strong { font: 600 1.125rem/1.4 var(--serif); }
.counter-benefits { list-style: none; display: grid; gap: 8px; padding: 0; margin: 14px 0 18px; font: 500 .875rem/1.5 var(--sans); }
.counter-benefits li::before { content: '✓'; display: inline-block; margin-right: 10px; color: #315b51; }
.counter-offer .plans .p-note { font-size: .75rem; line-height: 1.45; }
@media (prefers-reduced-motion: no-preference) {
  body.motion-full .rd-result > strong { animation: discovery-answer .35s ease-out both; }
  @keyframes discovery-answer { from { opacity: .4; transform: translateY(4px); } to { opacity: 1; transform: none; } }
}
[data-theme="dark"] #reading-discovery .rd-options .is-correct { background: #25483c; color: #e6f1e5; border-color: #92b39a; }
[data-theme="dark"] #reading-discovery .rd-options button:hover:not(:disabled) { background: #25483c; }
[data-theme="dark"] .rd-eyebrow { color: #aac8b4; }
@media(max-width:560px) {
  #reading-discovery.is-open { padding: 16px; margin-inline: -8px; }
  #reading-discovery .rd-source { padding: 12px; }
  #reading-discovery .rd-options button { padding: 12px; }
  .counter-benefits { font-size: .8125rem; }
}

/* The account door is a short form, with the optional detail folded away. */
#key-panel:has(.k-tabs) { width: min(480px,calc(100vw - 24px)); max-width: none; padding: 26px; border: 1px solid var(--rule); border-radius: 18px; box-shadow: 0 18px 80px #172e3a35; }
#key-panel:has(.k-tabs) > h3 { display: block !important; font: 600 1.6rem/1.2 var(--serif) !important; text-transform: none; letter-spacing: -.02em; margin: 0 0 14px !important; padding: 0 !important; border: 0 !important; color: var(--carbon); background: transparent; text-wrap: wrap; }
#key-panel:has(.k-tabs) > h3::before, #key-panel:has(.k-tabs) > h3::after { display: none; }
#key-panel .k-tabs { margin-bottom: 16px; }
#key-panel .k-tabs button { font: 600 .8125rem/1.4 var(--sans); letter-spacing: 0; text-transform: none; min-height: 36px; }
#key-panel:has(.k-tabs) .k-lede { font: 400 .9375rem/1.5 var(--serif); margin-bottom: 12px; }
#key-panel .k-account-promise { padding: 10px 12px; margin: 0 0 14px; border: 0; border-radius: 10px; background: var(--galley); font: 400 .8125rem/1.5 var(--sans); }
#key-panel .k-field-label { margin-top: 12px; font: 500 .8125rem/1.4 var(--sans); }
#key-panel input[type=email], #key-panel input[type=password], #key-panel input[type=text] { min-height: 46px; border-radius: 8px; padding: 10px 12px; margin-bottom: 0; font: 400 1rem/1.4 var(--sans); border-color: var(--rule); }
#key-panel .k-rule { margin: 6px 0 8px; font: 400 .75rem/1.5 var(--sans); color: var(--steel); }
#key-panel .k-name-field { margin-top: 6px; font: 400 .75rem/1.4 var(--sans); }
#key-panel .k-name-field summary { min-height: 32px; display: list-item; cursor: pointer; padding-block: 8px; }
#key-panel .k-name-field input { margin-bottom: 10px; }
#key-panel:has(.k-tabs) .btnrow { display: flex; gap: 10px; }
#key-panel:has(.k-tabs) .btnrow button { min-height: 46px; font: 600 .875rem/1.4 var(--sans); text-transform: none; letter-spacing: 0; border-radius: 10px; box-shadow: none; }
#key-panel:has(.k-tabs) #k-go { flex: 2 1 0; background: #315b51; color: #fffdf7; border-color: #315b51; }
#key-panel:has(.k-tabs) #k-close { flex: 1 1 0; border-color: transparent; background: transparent; }
#key-panel .k-alt { margin: 12px 0 0; }
#key-panel .k-linkish { min-height: 44px; font: 500 .75rem/1.4 var(--sans); text-transform: none; letter-spacing: 0; }
#key-panel .k-social { margin-bottom: 12px; }
#key-panel .k-oauth { min-height: 46px; border-radius: 10px; }
#key-panel .k-or { font-size: .75rem; }
#key-panel:focus { outline: none; }
@media(max-width:560px) {
  #key-panel:has(.k-tabs) { padding: 20px; }
  #key-panel:has(.k-tabs) > h3 { font-size: 1.4rem !important; }
}


/* ===== 103-grammar-adventure.css ===== */
/* A teaching surface: the sentence, its meaning and the changing form stay
   together. Colour always has a written label beside it. */
.case-explorer { border:1px solid var(--rule); border-radius:18px; background:var(--paper); overflow:hidden; color:var(--carbon); }
.course-atlas > .case-explorer { grid-column:1/-1; margin:0 0 24px; }
.case-explorer > summary { display:flex; align-items:center; gap:14px; padding:16px 20px; cursor:pointer; list-style:none; font-family:var(--sans); }
.case-explorer > summary::-webkit-details-marker { display:none; }
.case-explorer > summary > span:nth-child(2) { flex:1; min-width:0; overflow-wrap:anywhere; }
.case-explorer > summary strong { display:block; font-size:1rem; }
.case-explorer > summary small { display:block; margin-top:4px; font-size:var(--type-body); line-height:1.5; }
.case-entry-icon { width:48px; height:48px; flex:0 0 48px; }
.case-entry-icon svg { width:100%; height:100%; }
.case-explorer[open] > summary { border-bottom:1px solid var(--rule); background:color-mix(in srgb,var(--play) 8%,var(--paper)); }
.case-lab { padding:clamp(16px,3vw,28px); font:400 var(--type-body)/1.65 var(--sans); }
.case-lab p { margin:0; max-width:65ch; overflow-wrap:anywhere; }
.case-lab-intro { max-width:65ch; }
.case-controls { display:flex; gap:18px; justify-content:space-between; align-items:end; margin:20px 0; flex-wrap:wrap; }
.case-controls label { display:grid; gap:6px; font-weight:600; flex:1 1 190px; min-width:0; }
.case-controls select { box-sizing:border-box; width:100%; min-width:0; border:1px solid var(--rule); background:var(--paper); color:var(--carbon); border-radius:10px; padding:12px; font:inherit; min-height:44px; }
.case-number { display:flex; flex-wrap:wrap; gap:5px; padding:5px; border:1px solid var(--rule); border-radius:12px; }
.case-lab button,.wk-scene-tabs button { min-height:44px; max-width:100%; white-space:normal; overflow-wrap:anywhere; padding:10px 14px; background:var(--paper); color:var(--carbon); border:1px solid var(--rule); border-radius:10px; font:600 var(--type-control)/1.5 var(--sans); cursor:pointer; box-shadow:none; text-transform:none; letter-spacing:0; }
.case-lab button[aria-pressed="true"],.wk-scene-tabs button[aria-pressed="true"] { background:#315b51; color:#fffdf7; border-color:#315b51; }
.case-roles { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:8px; }
.case-roles button { display:flex; gap:10px; align-items:center; text-align:left; min-width:0; white-space:normal; overflow-wrap:anywhere; }
.case-roles button b { font:500 1.3rem var(--serif); opacity:.7; }
.case-roles small { display:block; font-weight:400; font-size:var(--type-meta); margin-top:3px; }
.case-transform { margin:20px 0; padding:clamp(16px,3vw,24px); border:1px solid #a8c1b6; border-radius:16px; background:color-mix(in srgb,#b8d5c1 16%,var(--paper)); animation:grammar-settle .25s ease-out; }
.case-transform .smallcaps { color:var(--carbon); font-size:var(--type-meta); }
.case-transform .case-cue { margin:10px 0 20px; font-weight:600; }
.case-equation { display:flex; flex-wrap:wrap; align-items:center; gap:14px; font:500 var(--type-prompt)/1.6 var(--serif); }
.case-equation > span:first-child { color:var(--pencil); }
.form-piece { white-space:normal; overflow-wrap:anywhere; display:inline-block; }
.case-lab mark,.lens-parts mark { padding:2px 4px; margin-left:1px; border-radius:4px; background:#d4e8dd; color:#1f4d40; border-bottom:2px solid #477766; }
.case-transform .case-sentence { font:500 var(--type-prompt)/1.65 var(--serif); margin:20px 0 5px; }
.case-transform .case-rule { margin:16px 0; }
.case-family-note { margin-top:16px!important; color:var(--pencil); }
.case-transform .case-practise { display:block; margin-top:16px; background:#315b51; color:#fffdf7; }
.case-practice-options { display:flex; gap:10px; flex-wrap:wrap; margin:20px 0; }
.case-practice-options button { font-family:var(--serif); font-size:var(--type-prompt); }
.case-lab .case-answer-right { background:#d4e8dd; color:#1f4d40; opacity:1; }
.case-lab .case-answer-retry { background:#faedd9; color:#6b4e25; }
.case-transform .case-explain { margin:16px 0; }
.case-transform > small { display:block; font-size:var(--type-meta); }
.case-lab [hidden] { display:none!important; }
.case-table summary,.case-scope summary { min-height:44px; padding:12px 0; cursor:pointer; font-weight:600; }
.case-table-scroll { overflow-x:auto; }
.case-table table { width:100%; border-collapse:collapse; font-size:var(--type-meta); }
.case-table th,.case-table td { padding:12px 8px; border-bottom:1px solid var(--rule); text-align:left; }
.case-table th { font-size:var(--type-meta); }
.case-table td { font-family:var(--serif); font-size:1rem; }
.case-lab :focus-visible,.case-explorer > summary:focus-visible,.wk-scene-tabs button:focus-visible { outline:3px solid var(--play); outline-offset:3px; }

.gx-stage.wk-stage { background:radial-gradient(ellipse at 5% 10%,#e0e9e780,transparent 55%),var(--paper); }
.gx-stage.wk-stage .wk-sheet { max-width:980px; padding:clamp(16px,3vw,36px); gap:16px; }
.wk-top-actions { display:flex; flex-wrap:wrap; align-items:center; gap:12px; }
.wk-sound { min-height:40px; padding:8px 12px; border:1px solid var(--rule); border-radius:20px; background:var(--paper); color:var(--carbon); font:600 var(--type-control)/1.5 var(--sans); cursor:pointer; }
.gx-stage.wk-stage .wk-sheet .lesson-compass { display:grid; grid-template-columns:130px minmax(0,1fr); gap:22px; padding:0; background:transparent; }
.gx-stage.wk-stage .wk-sheet .lesson-window { height:120px; max-height:120px; border:1px solid var(--rule); background:#e5ebdf; }
.gx-stage.wk-stage .wk-sheet .lesson-window > svg { height:120px; }
.gx-stage.wk-stage .wk-sheet .lesson-window .gx-mate { width:78px; height:88px; left:26px; top:28px; }
.gx-stage.wk-stage .wk-sheet .lesson-compass h2 { font:500 var(--type-heading)/1.3 var(--serif); }
.wk-journey { display:flex; flex-wrap:wrap; align-items:center; gap:8px; padding:10px 0; border-block:1px solid var(--rule); }
.wk-journey > span { flex:1 1 9rem; min-width:0; overflow-wrap:anywhere; display:flex; align-items:center; gap:8px; color:var(--pencil); font:500 var(--type-meta)/1.5 var(--sans); }
.wk-journey b { display:grid; place-items:center; border-radius:50%; width:25px; height:25px; border:1px solid var(--rule); font-size:var(--type-meta); flex-shrink:0; }
.wk-journey .is-current { color:var(--carbon); font-weight:700; }
.wk-journey .is-current b { background:#315b51; color:#fff; border-color:#315b51; }
.wk-encouragement { display:flex; gap:10px; font:500 var(--type-body)/1.5 var(--sans); color:var(--pencil); }
.wk-encouragement > span:first-child { color:#946727; }
.wk-stage .wk-stakes { display:none; }
.gx-stage.wk-stage .wk-sheet .wk-body { border-radius:20px; padding:clamp(18px,3vw,30px); background:var(--paper); border:1px solid var(--rule); box-shadow:0 5px 0 #193c3010; }
.gx-stage.wk-stage .wk-sheet .wk-opening .wk-mission { font:500 var(--type-prompt)/1.5 var(--serif); margin:12px 0; }
.wk-scene-tabs { display:flex; gap:8px; flex-wrap:wrap; margin:20px 0 12px; }
.grammar-lens { padding:20px; border:1px solid #b6ccc1; border-radius:16px; background:color-mix(in srgb,#bdd5c6 13%,var(--paper)); animation:grammar-settle .3s ease-out; text-align:left; }
.lens-scene { font:500 var(--type-body)/1.6 var(--sans); }
.lens-scene .smallcaps { font-size:var(--type-meta); color:var(--pencil); }
.lens-connection { display:flex; flex-wrap:wrap; align-items:center; gap:10px; padding:12px 0; margin-top:10px; font:500 var(--type-body)/1.5 var(--sans); }
.lens-connection > span:first-child { padding:7px 10px; background:var(--paper); border:1px solid var(--rule); border-radius:8px; }
.lens-connection strong { color:var(--carbon); margin:0; }
.grammar-lens .lens-sentence { font:500 var(--type-prompt)/1.65 var(--serif); margin:14px 0; }
.lens-sentence mark { background:#d4e8dd; color:#1f4d40; border-bottom:2px solid #477766; padding:2px 5px; border-radius:4px; }
.lens-parts { display:flex; flex-wrap:wrap; gap:12px; margin:16px 0; }
.lens-parts > div { flex:1; min-width:130px; padding:12px 16px; background:var(--paper); border:1px solid var(--rule); border-radius:12px; }
.lens-parts small { color:var(--pencil); font:600 var(--type-meta)/1.5 var(--sans); }
.lens-parts p { font:500 1.45rem/1.6 var(--serif); margin:8px 0 0!important; }
.grammar-lens .lens-why,.grammar-lens .lens-tip { font:400 var(--type-body)/1.65 var(--sans); margin:12px 0 0; }
.lens-tip { padding-top:12px; border-top:1px solid var(--rule); }
.wk-memory-note { border-left:3px solid #ba8540; padding:12px 16px; background:color-mix(in srgb,#e9cd9c 20%,var(--paper)); border-radius:4px 12px 12px 4px; }
.wk-memory-note strong { display:block; margin-top:6px; }
.gx-stage.wk-stage .wk-sheet .wk-options button { border-radius:14px; transition:transform .15s,box-shadow .15s,background .15s; }
.gx-stage.wk-stage .wk-sheet .wk-options button:not(:disabled):hover { transform:translateY(-2px); box-shadow:0 5px 0 #315b5120; }
.gx-stage.wk-stage .wk-sheet .wk-options .gx-right { opacity:1; background:#e1efe5!important; color:#205640!important; border-color:#638674!important; box-shadow:0 4px 0 #638674; }
.gx-stage.wk-stage .wk-sheet .wk-options .gx-wrong { opacity:1; background:#faedd9!important; color:#6b4e25!important; border-color:#c59b65!important; box-shadow:0 3px 0 #c59b65; }
.gx-stage.wk-stage .wk-sheet .wk-verdict { padding:0; border:0; background:transparent; scroll-margin-block:24px; outline:none; }
.wk-feedback-heading { display:flex; gap:14px; align-items:center; margin:8px 0 18px; }
.wk-feedback-heading strong { font:700 1rem/1.4 var(--sans); margin:0 0 5px; }
.wk-feedback-heading > div > span { font:400 var(--type-body)/1.5 var(--sans); }
.wk-feedback-symbol { flex:0 0 64px; width:64px; height:64px; border-radius:18px; background:#e5eee5; display:grid; place-items:center; animation:grammar-cheer .5s ease-out; }
.wk-feedback-symbol svg { width:60px; height:60px; }
.wk-stage .tutor-local { border:0; padding:0; background:transparent; box-shadow:none; }
.wk-stage .tutor-local-head { padding:12px 0; }
.wk-stage .tutor-local-head .smallcaps,.wk-stage .tutor-mark { display:none; }
.wk-stage .tutor-local-head p { margin:0; font-size:var(--type-body); }
.wk-stage .tutor-prompts { margin:0 0 14px; }
.wk-stage .tutor-local-body .case-explorer { margin-top:16px; }
.wk-stage .wk-footer { position:static!important; padding:16px 0!important; background:var(--paper); border-top:1px solid var(--rule); }
.gx-stage.wk-stage .wk-sheet[data-phase="intro"] .wk-page-slots { display:none; }
.gx-stage.wk-stage .wk-sheet[data-phase="question"] .lesson-compass { grid-template-columns:90px minmax(0,1fr); }
.gx-stage.wk-stage .wk-sheet[data-phase="question"] .lesson-window { height:86px; }
.gx-stage.wk-stage .wk-sheet[data-phase="question"] .lesson-window .gx-mate { width:60px; height:67px; left:14px; top:17px; }
.wk-earned { display:flex; gap:16px; align-items:center; padding:20px; border:1px solid #a9bdad; border-radius:16px; background:color-mix(in srgb,#c9dcc5 20%,var(--paper)); }
.wk-earned > span { color:#a1752e; font-size:2.5rem; animation:grammar-cheer .6s ease-out; }
.wk-earned strong { font:500 1.4rem/1.4 var(--serif); }
.gx-stage.wk-stage .wk-sheet[data-phase="complete"] .wk-body > h3 { font:500 var(--type-heading)/1.4 var(--serif); margin:20px 0 12px; }
@keyframes grammar-settle { from { opacity:.5; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
@keyframes grammar-cheer { 0% { transform:scale(.85) rotate(-7deg); } 55% { transform:scale(1.07) rotate(4deg); } 100% { transform:scale(1) rotate(0); } }
@media(max-width:600px) {
  .case-explorer > summary { padding:14px; gap:10px; }
  .case-roles { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .case-controls { gap:12px; }
  .case-equation { gap:8px; }
  .gx-stage.wk-stage .wk-sheet .lesson-compass { grid-template-columns:80px minmax(0,1fr); gap:14px; }
  .gx-stage.wk-stage .wk-sheet .lesson-window { height:84px; }
  .gx-stage.wk-stage .wk-sheet .lesson-window .gx-mate { width:56px; height:65px; left:12px; top:17px; }
  .wk-journey > span { font-size:var(--type-meta); gap:5px; }
  .wk-journey { gap:4px; }
  .wk-top-actions { gap:6px; }
  .grammar-lens { padding:16px; }
  .wk-feedback-symbol { flex-basis:48px; width:48px; height:48px; }
  .wk-feedback-symbol svg { width:46px; height:46px; }
}
body.motion-low .grammar-lens,body.motion-low .case-transform,body.motion-low .wk-feedback-symbol,body.motion-low .wk-earned > span { animation:none; }
body.motion-low .wk-options button { transition:none!important; transform:none!important; }
@media(prefers-reduced-motion:reduce) { .grammar-lens,.case-transform,.wk-feedback-symbol,.wk-earned > span { animation:none; } .wk-options button { transition:none!important; transform:none!important; } }

.wk-stage .gx-top { gap:8px; align-items:center; }
.gx-stage.wk-stage .gx-top .gx-topic { display:block; min-width:0; white-space:normal; overflow:visible; text-overflow:clip; line-height:1.4; }
@media(max-width:600px) { .gx-stage.wk-stage .gx-top .gx-topic { font-size:var(--type-meta); } }


/* ===== 104-daily-adventure.css ===== */
/* One invitation, one task, one earned ending. The extra machinery remains
   reachable through a disclosure, rather than preceding the first action. */
#v-review.daily-mode #practice-welcome { max-width:980px; margin:18px auto 40px; width:100%; scroll-margin-top:90px; }
#v-review.daily-mode > .rev-head, #v-review.daily-mode .practice-mode-label,
#v-review.daily-mode .rev-dial, #v-review.daily-mode #practice-challenge,
#v-review.daily-mode #practice-workbench, #v-review.daily-mode .quill-wrap { display:none; }
#v-review.daily-mode > .rev-head { display:none!important; }
.daily-adventure { color:var(--carbon); font:400 1rem/1.6 var(--sans); }
.daily-adventure *, .desk-daily * { box-sizing:border-box; }
.daily-adventure [hidden] { display:none!important; }
.daily-adventure h2,.daily-adventure h3 { font-family:var(--serif); text-transform:none; letter-spacing:0; color:var(--carbon); }
.daily-adventure h2 { font-size:clamp(1.7rem,3vw,2.6rem); line-height:1.2; margin:12px 0 18px; }
.daily-adventure p { margin:12px 0; font-weight:400; }
.daily-adventure button { cursor:pointer; white-space:normal; }
.daily-adventure .btn { text-transform:none; letter-spacing:0; font:600 .95rem/1.4 var(--sans); border-radius:12px; min-height:48px; padding:13px 22px; }
.daily-adventure .btn.primary { background:#365e54; border-color:#365e54; color:#fffdf7; box-shadow:0 4px 0 #234139; }
.daily-adventure .btn.primary:hover { background:#294f45; }
.daily-adventure .btn.primary:active { transform:translateY(2px); box-shadow:0 2px 0 #234139; }
.daily-adventure .daily-top { display:flex; justify-content:space-between; align-items:center; gap:16px; padding:0 0 16px; position:static; background:transparent; border:0; box-shadow:none; width:auto; min-height:0; height:auto; }
.daily-top .smallcaps { font-size:.73rem; letter-spacing:.1em; }
.daily-sound,.daily-back,.daily-clue { color:var(--carbon); background:transparent; border:0; box-shadow:none; min-height:44px; padding:8px 0; font:600 .8rem/1.4 var(--sans); text-transform:none; letter-spacing:0; }
.daily-sound { border:1px solid var(--rule); padding:8px 14px; border-radius:24px; flex-shrink:0; }
.daily-sound[aria-pressed="true"] { background:color-mix(in srgb,#b8d5c1 30%,var(--paper)); }
.daily-invitation { display:grid; grid-template-columns:minmax(0,1.35fr) minmax(220px,1fr); gap:28px; padding:clamp(24px,4vw,44px); border:1px solid var(--rule); border-radius:28px; background:var(--paper); box-shadow:0 6px 0 color-mix(in srgb,var(--rule) 55%,transparent); }
.daily-copy > p { color:var(--pencil); max-width:440px; }
.daily-copy > small { display:block; margin-top:15px; font-size:.75rem; }
.daily-copy .btn { margin-top:10px; width:100%; }
.daily-landscape { position:relative; align-self:center; min-width:0; }
.daily-landscape > svg { display:block; width:100%; height:auto; }
.daily-landscape .daily-mate { position:absolute; width:100px; height:112px; right:12%; bottom:22%; }
.daily-postmark { display:block; text-align:center; font:600 .75rem/1.4 var(--sans); color:var(--pencil); margin-top:16px; }
.daily-route { display:flex; list-style:none; padding:0; gap:0; margin:26px 0 18px; }
.daily-route li { display:flex; align-items:center; gap:8px; font-size:.78rem; flex:1; position:relative; }
.daily-route li:not(:last-child)::after { content:''; height:1px; background:var(--rule); flex:1; margin:0 12px; }
.daily-route li > span { display:grid; place-items:center; border-radius:50%; width:30px; height:30px; border:1px solid var(--rule); flex-shrink:0; background:var(--paper); }
.daily-route .is-current > span { background:#edc869; color:#273544; border-color:#b68c36; box-shadow:0 0 0 4px #edc86928; }
.daily-route .is-done > span { background:#365e54; color:#fffdf7; border-color:#365e54; }
.daily-personal { color:var(--pencil); font-size:.83rem; padding:10px 14px; text-align:center; }
.daily-options { border-top:1px solid var(--rule); margin-top:22px; padding:4px 0; }
.daily-options > summary,.daily-album > summary,.level-browse-all > summary,#desk-preferences > summary { cursor:pointer; min-height:48px; padding:14px 0; font:600 .9rem/1.5 var(--sans); color:var(--carbon); }
.daily-options > div { display:flex; flex-wrap:wrap; gap:10px; }
.daily-options > p { font-size:.85rem; color:var(--pencil); }
.daily-meter { display:flex; align-items:center; gap:16px; margin:20px 0; font-size:.78rem; }
.daily-meter progress { flex:1; height:8px; width:100%; appearance:none; border:0; border-radius:6px; overflow:hidden; background:var(--rule); }
.daily-meter progress::-webkit-progress-bar { background:var(--rule); }
.daily-meter progress::-webkit-progress-value { background:#638f7e; transition:width .25s; }
.daily-meter progress::-moz-progress-bar { background:#638f7e; }
.daily-meter span { white-space:nowrap; }
.daily-task { max-width:780px; margin:0 auto; padding:clamp(22px,4vw,38px); border:1px solid var(--rule); border-radius:22px; background:var(--paper); animation:daily-arrive .22s ease-out; }
.daily-task-head { display:flex; align-items:center; justify-content:space-between; gap:16px; min-height:62px; }
.daily-task-head .smallcaps { font-size:.73rem; }
.daily-mate { width:66px; height:72px; display:block; flex-shrink:0; }
.daily-mate svg { width:100%; height:100%; }
.daily-task h2 { font-size:clamp(1.45rem,3vw,2rem); }
.daily-task > .grammar-lens + .btn { margin-top:18px; }
.daily-line { font:500 clamp(1.3rem,2.7vw,1.9rem)/1.65 var(--serif); padding:18px 22px; border-left:3px solid #a48b4f; border-radius:0 12px 12px 0; background:color-mix(in srgb,#e6ca89 12%,var(--paper)); overflow-wrap:anywhere; }
.daily-meaning { font-weight:600; }
.daily-source { font-size:.8rem; color:var(--pencil); font-style:italic; }
.daily-choices { display:grid; gap:10px; margin:24px 0 12px; }
.daily-choices button { min-height:54px; text-align:left; padding:15px 20px; color:var(--carbon); background:var(--paper); border:1px solid var(--rule); border-radius:12px; box-shadow:0 3px 0 var(--rule); font:500 1rem/1.5 var(--sans); letter-spacing:0; text-transform:none; }
.daily-choices button[lang] { font-family:var(--serif); font-size:1.25rem; }
.daily-choices button:hover:not(:disabled) { border-color:#638f7e; background:color-mix(in srgb,#b8d5c1 16%,var(--paper)); transform:translateY(-1px); }
.daily-adventure button:disabled { cursor:default; opacity:.55; }
.daily-clue { text-decoration:underline; text-underline-offset:4px; }
.daily-feedback { margin-top:20px; padding:22px; background:color-mix(in srgb,#b8d5c1 20%,var(--paper)); border:1px solid #799d8e; border-radius:16px; animation:daily-arrive .22s ease-out; }
.daily-feedback.is-learning { background:color-mix(in srgb,#e6ca89 20%,var(--paper)); border-color:#bda271; }
.daily-feedback h3 { font-size:1.4rem; margin:0 0 12px; }
.daily-feedback > p { font-size:.88rem; }
.daily-feedback .grammar-lens { margin:12px 0; }
.daily-feedback .btn { margin-top:16px; }
.daily-correction { font-weight:600; }
.daily-word-meaning { font:500 1.3rem/1.5 var(--serif); padding:18px; border-radius:12px; background:color-mix(in srgb,#b8d5c1 20%,var(--paper)); }
.daily-self-check { display:flex; gap:12px; flex-wrap:wrap; }
#daily-form { display:grid; grid-template-columns:1fr auto; gap:12px; margin:22px 0; }
#daily-form label { grid-column:1/-1; font-size:.85rem; font-weight:600; }
#daily-input { width:100%; min-width:0; padding:12px 14px; font:500 1.3rem var(--serif); color:var(--carbon); border:1px solid var(--rule); border-radius:10px; background:var(--paper); }
.daily-tile-target,.daily-tiles { display:flex; flex-wrap:wrap; gap:8px; min-height:80px; padding:14px 0; }
.daily-tile-target { padding:14px; border:1px dashed #b49c6b; border-radius:12px; margin:20px 0 10px; background:color-mix(in srgb,#e6ca89 10%,var(--paper)); }
.daily-tile-target button,.daily-tiles button { font:500 1.08rem/1.3 var(--serif); min-height:44px; border:1px solid var(--rule); border-radius:8px; color:var(--carbon); background:var(--paper); padding:10px 12px; box-shadow:0 2px 0 var(--rule); }
.daily-tiles button:disabled { visibility:hidden; }
.daily-finish { max-width:680px; margin:0 auto; text-align:center; }
.daily-postcard { position:relative; padding:26px; background:var(--paper); border:1px solid #bda271; border-radius:12px; box-shadow:5px 7px 0 color-mix(in srgb,#bda271 25%,transparent); transform:rotate(-1deg); }
.daily-postcard > svg { display:block; width:100%; max-width:330px; max-height:230px; margin:0 auto; }
.daily-postcard h2 { font-size:clamp(1.5rem,3vw,2rem); margin-bottom:8px; }
.daily-postcard p { font:500 1.05rem var(--serif); }
.daily-postcard-date { display:block; text-align:left; font-size:.75rem; color:var(--pencil); }
.daily-stamp { position:absolute; right:24px; top:24px; color:#365e54; border:2px dotted #638f7e; padding:6px 12px; font-size:1.4rem; transform:rotate(12deg); }
.daily-finish > h3 { font-size:1.5rem; margin:30px 0 8px; }
.daily-finish-note { color:var(--pencil); font-size:.82rem; max-width:490px; margin:12px auto 24px!important; }
.daily-finish > .daily-back { display:block; margin:12px auto; }
.daily-album { border-top:1px solid var(--rule); margin-top:24px; text-align:left; }
.daily-album > div { display:grid; grid-template-columns:repeat(4,minmax(0,1fr)); gap:12px; }
.daily-album figure { margin:0; padding:10px; border:1px solid var(--rule); background:var(--paper); border-radius:8px; }
.daily-album svg { width:100%; height:auto; }
.daily-album figcaption { font-size:.7rem; text-align:center; }
.earned-flight { pointer-events:none; position:fixed; z-index:9999; top:45vh; left:0; width:130px; height:150px; filter:drop-shadow(0 8px 10px #27354430); }
.earned-flight > svg { width:100%; height:100%; }
.earned-flight > span { position:absolute; color:#d7a936; font-size:2rem; top:40%; left:-15%; }
.earned-flight > span:nth-child(3) { top:65%; left:-40%; font-size:1.4rem; }
.earned-flight > span:nth-child(4) { top:15%; left:-65%; font-size:1rem; }
.desk-daily { display:flex; align-items:center; gap:20px; width:100%; text-align:left; border:1px solid var(--rule); border-radius:20px; background:var(--paper); color:var(--carbon); padding:22px 28px; box-shadow:0 4px 0 color-mix(in srgb,var(--rule) 50%,transparent); cursor:pointer; font-family:var(--sans); }
.desk-daily-art { width:70px; height:76px; flex-shrink:0; }
.desk-daily-art svg { width:100%; height:100%; }
.desk-daily > span:nth-child(2) { flex:1; }
.desk-daily small,.desk-daily strong,.desk-daily span span { display:block; }
.desk-daily small { font-size:.72rem; color:var(--pencil); }
.desk-daily strong { font:600 1.3rem/1.4 var(--serif); margin:4px 0; }
.desk-daily span span { font-size:.82rem; color:var(--pencil); }
.desk-shortcuts { display:flex; gap:18px; margin:12px 0 24px; }
.desk-shortcuts button { flex:1; padding:12px 8px; display:flex; justify-content:space-between; border:0; border-bottom:1px solid var(--rule); box-shadow:none; border-radius:0; color:var(--carbon); background:transparent; font:600 .85rem var(--sans); min-height:44px; }
#desk-preferences { max-width:1160px; margin:8px auto 24px; border-top:1px solid var(--rule); padding:0 16px; }
#desk-preferences[open] > summary { margin-bottom:14px; }
#desk-preferences .friend-strip { position:static; max-width:760px; }
#desk-preferences .ask-card { max-width:760px; }
#desk-preferences .fr-bubble { font-size:.9rem; }
.level-browse-all { border-top:1px solid var(--rule); margin-top:24px; }
.level-browse-all > summary { padding:18px 0; }
.level-browse-all .level-browse { margin-top:6px; }
#v-profile .study-orientation { display:none; }
#v-library .room-head .rh-ru { font:600 var(--type-meta)/1.5 var(--sans); letter-spacing:.08em; margin-bottom:8px; }
#v-library .room-head h2 { font:600 clamp(1.8rem,3vw,2.5rem)/1.2 var(--serif); text-transform:none; letter-spacing:0; font-style:normal; }
#v-library .room-head .rh-sub { font:400 var(--type-body)/1.6 var(--sans); max-width:65ch; }
#v-library .lib-spread { display:block; margin:24px 0; }
#v-library #resume-hero { max-width:760px; }
.daily-adventure :focus-visible,.desk-daily:focus-visible,.desk-shortcuts button:focus-visible,.level-browse-all > summary:focus-visible,#desk-preferences > summary:focus-visible { outline:3px solid var(--play); outline-offset:4px; }
@keyframes daily-arrive { from { opacity:0; transform:translateY(6px); } to { opacity:1; transform:translateY(0); } }
@media(max-width:640px) {
  #v-review.daily-mode #practice-welcome { margin-top:8px; scroll-margin-top:110px; }
  .daily-invitation { grid-template-columns:1fr; gap:0; padding:24px; border-radius:20px; }
  .daily-landscape { grid-row:1; width:170px; justify-self:center; margin-bottom:10px; }
  .daily-landscape .daily-mate { width:60px; height:66px; }
  .daily-postmark { display:none; }
  .daily-copy h2 { margin-top:0; }
  .daily-copy > p { font-size:.92rem; }
  .daily-route li { font-size:.74rem; gap:6px; }
  .daily-route li:not(:last-child)::after { margin:0 6px; }
  .daily-route li > span { width:26px; height:26px; }
  .daily-top .smallcaps { max-width:190px; font-size:.7rem; }
  .daily-task { padding:20px; }
  .daily-feedback { padding:18px; }
  .daily-line { padding:12px 14px; }
  #daily-form { grid-template-columns:1fr; }
  .daily-options > div { flex-direction:column; }
  .daily-album > div { grid-template-columns:repeat(2,minmax(0,1fr)); }
  .desk-daily { padding:18px; gap:12px; }
  .desk-daily-art { width:50px; height:58px; }
  .desk-daily strong { font-size:1.1rem; }
  .desk-shortcuts { gap:12px; }
}
@media(prefers-reduced-motion:reduce) { .daily-task,.daily-feedback { animation:none; } .earned-flight { display:none; } .daily-meter progress::-webkit-progress-value { transition:none; } }

/* A visible writing option and a clear onward path, shared across rooms. */
/* THE KEYS ARE FINGER-SIZED (13 Sep 2026, the owner: «awkwardly small»).
   One set of sizes for every pad in the house — the lesson's type case, the
   workshop's form, Practice and the daily adventure. The keyboard is a block
   under its toggle; each row shares the full width, a letter grows to 60px on
   a desk and shrinks with the row on a phone, and the space bar and the
   backspace are wider than a letter so they are found without looking. */
.typing-keyboard { display:block; width:100%; grid-column:1/-1; }
.typing-keyboard [hidden] { display:none!important; }
.typing-keyboard-toggle { display:inline-flex; align-items:center; width:auto; color:var(--carbon); background:transparent; border:1px solid var(--rule); border-radius:10px; padding:10px 14px; min-height:44px; font:600 .85rem/1.4 var(--sans); text-transform:none; letter-spacing:0; cursor:pointer; }
.typing-keyboard-toggle::before { content:'＋'; margin-right:8px; }
.typing-keyboard-toggle[aria-expanded="true"]::before { content:'−'; }
.typing-keyboard-keys { display:block; width:100%; margin-top:10px; padding:4px 0 2px; }
.gx-pad .gx-pad-row,.typing-keyboard .gx-pad-row,#wk-keys .gx-pad-row { display:flex; gap:6px; width:100%; margin-top:6px; justify-content:center; }
.gx-pad .gx-pad-k,.typing-keyboard .gx-pad-k,#wk-keys .gx-pad-k { flex:1 1 0; min-width:0; max-width:60px; min-height:54px; padding:10px 0; font:500 1.25rem/1.2 var(--sans); letter-spacing:0; text-transform:none; color:var(--carbon); border:1px solid var(--rule); border-radius:8px; background:var(--paper); box-shadow:0 2px 0 var(--rule); cursor:pointer; }
.gx-pad .gx-pad-space,.typing-keyboard .gx-pad-space,#wk-keys .gx-pad-space { flex:4 1 0; max-width:280px; font-size:.95rem; }
.gx-pad .gx-pad-bs,.typing-keyboard .gx-pad-bs,#wk-keys .gx-pad-bs { flex:1.6 1 0; max-width:96px; }
#daily-keyboard { grid-column:1/-1; }
.daily-next-adventure { margin:24px 0 18px; padding:22px; background:var(--paper); border:1px solid var(--rule); border-radius:16px; }
.daily-next-adventure p { font:600 1.25rem/1.4 var(--serif); }
.daily-next-adventure small { display:block; font-size:.8rem; color:var(--pencil); margin-bottom:16px; }
.daily-next-adventure .btn { width:100%; }
.daily-finish-actions { display:flex; justify-content:center; flex-wrap:wrap; gap:12px 24px; }
.practice-settings,.practice-format { text-align:left; max-width:640px; margin:18px auto; border-top:1px solid var(--rule); }
.practice-settings summary,.practice-format summary { color:var(--carbon); cursor:pointer; font:600 .9rem/1.5 var(--sans); min-height:44px; padding:12px 0; }
#v-review.daily-mode .practice-format,#v-review.practice-empty .practice-format,#v-review.practice-finished:not(.practice-lobby) .practice-format { display:none; }
#v-review:not(.daily-mode) .practice-invitation { padding:26px; }
#v-review:not(.daily-mode) .practice-portrait { height:64px; padding:0; margin:0 auto 16px; background:transparent; }
#v-review:not(.daily-mode) .practice-portrait svg { height:64px; }
#v-review:not(.daily-mode) .practice-invitation h2 { font:600 clamp(1.6rem,3vw,2.1rem)/1.2 var(--serif); }
#v-review:not(.daily-mode) .practice-start-actions { margin-top:22px; }
#v-review:not(.daily-mode) .practice-start-actions .btn { font:600 .9rem/1.4 var(--sans); text-transform:none; letter-spacing:0; }
#rev-kbd { font:600 .8rem/1.4 var(--sans); width:auto; }
@media(max-width:640px) {
  .gx-pad .gx-pad-row,.typing-keyboard .gx-pad-row,#wk-keys .gx-pad-row { gap:3px; margin-top:4px; }
  .gx-pad .gx-pad-k,.typing-keyboard .gx-pad-k,#wk-keys .gx-pad-k { min-height:48px; padding:8px 0; font-size:1.05rem; }
  .gx-pad .gx-pad-space,.typing-keyboard .gx-pad-space,#wk-keys .gx-pad-space { font-size:.8rem; }
  .practice-settings .practice-rounds { margin-top:10px; }
}

/* Keep both onward choices visible beside the reward on a laptop. */
.daily-finish { max-width:980px; display:grid; grid-template-columns:1fr 1fr; gap:0 32px; align-items:start; }
.daily-finish .daily-postcard { grid-column:1; grid-row:1/5; align-self:center; }
.daily-finish > h3 { grid-column:2; margin:6px 0 12px; }
.daily-finish > p:not(.daily-finish-note) { grid-column:2; margin:0 0 12px; }
.daily-next-adventure { grid-column:2; margin:10px 0 18px; }
.daily-finish-actions { grid-column:2; }
.daily-finish-note { grid-column:1/-1; grid-row:5; margin:26px auto 0!important; }
.daily-finish .daily-album { grid-column:1/-1; }
@media(max-width:700px) {
  .daily-finish { display:block; }
  .daily-finish .daily-postcard { display:grid; grid-template-columns:100px 1fr; gap:4px 14px; padding:16px; text-align:left; }
  .daily-postcard > svg { grid-column:1; grid-row:1/4; align-self:center; }
  .daily-postcard-date,.daily-postcard h2,.daily-postcard p { grid-column:2; margin:0; }
  .daily-postcard h2 { font-size:1.2rem; }
  .daily-postcard p { font-size:.9rem; }
  .daily-postcard .daily-stamp { display:none; }
  .daily-finish > h3 { margin-top:24px; }
  .daily-finish-note { display:none; }
}

/* Course volumes: the same bound chapter trail in every room and level. */
.chapter-journey { max-width:820px; margin:0 auto; color:var(--carbon); }
.chapter-heading { position:static; display:grid; grid-template-columns:minmax(0,1fr) 160px; gap:24px; align-items:center; padding:24px; margin:0 0 24px; border:1px solid var(--rule); border-radius:20px; background:var(--paper); box-shadow:none; }
.chapter-heading h2 { font:600 1.8rem/1.2 var(--serif); margin:10px 0; text-transform:none; letter-spacing:0; }
.chapter-heading p { font:400 .9rem/1.6 var(--sans); color:var(--pencil); margin:8px 0 16px; }
.chapter-heading progress { width:100%; height:8px; accent-color:#638f7e; display:block; margin-top:10px; }
.chapter-tally { font:600 .78rem var(--sans); }
.chapter-companion svg { width:100%; height:auto; }
.chapter-stops { list-style:none; padding:0; margin:0 0 0 22px; border-left:2px solid var(--rule); }
.chapter-stop { position:relative; padding:0 0 16px 30px; }
.chapter-stop::before { content:''; position:absolute; left:-6px; top:37px; width:10px; height:10px; border-radius:50%; background:var(--paper); border:2px solid var(--rule); }
.chapter-stop.is-done::before { background:#638f7e; border-color:#638f7e; }
.chapter-stop.is-current::before { background:#e4ba59; border-color:#a6802c; box-shadow:0 0 0 5px #e4ba5930; }
#v-course .chapter-journey .chapter-go { display:flex; align-items:center; gap:18px; width:100%; min-width:0; padding:16px 20px; border:1px solid var(--rule); border-radius:14px; background:var(--paper); color:var(--carbon); box-shadow:0 3px 0 var(--rule); text-align:left; font:500 .95rem/1.4 var(--sans); text-transform:none; letter-spacing:0; cursor:pointer; transition:transform .15s; }
#v-course .chapter-journey .chapter-go:hover { transform:translateY(-2px); border-color:#638f7e; }
#v-course .chapter-journey .is-current .chapter-go { border:2px solid #9d7a32; background:color-mix(in srgb,#f1d990 15%,var(--paper)); box-shadow:0 4px 0 #9d7a3240; }
.chapter-book { flex:0 0 44px; height:54px; display:grid; place-items:center; border:1px solid var(--rule); border-left:5px double #a78b52; border-radius:2px 9px 9px 2px; font:600 1.2rem var(--serif); background:var(--paper); }
.is-done .chapter-book { background:#365e54; border-color:#365e54; color:#fff; }
.is-current .chapter-book { background:#efd18b; color:#293b36; }
.chapter-label { flex:1; min-width:0; }
.chapter-label small { display:block; font-size:.72rem; color:var(--pencil); margin-bottom:4px; }
.chapter-label strong { display:block; font:600 1.2rem/1.3 var(--serif); }
.chapter-label > span { display:block; font-size:.83rem; margin-top:8px; }
.chapter-fold { margin:4px 0 18px; }
.chapter-fold > summary { cursor:pointer; padding:14px 10px; min-height:48px; color:var(--pencil); font:600 .85rem/1.5 var(--sans); }
.chapter-fold[open] > summary { margin-bottom:14px; }
.chapter-ending { position:static; text-align:center; padding:24px; border:1px dashed #ac9463; border-radius:18px; margin:20px 0; background:var(--paper); }
.chapter-ending h3 { font:600 1.4rem var(--serif); margin:10px 0; }
.chapter-ending p { font:.85rem/1.6 var(--sans); color:var(--pencil); }
.chapter-seal { display:grid; place-items:center; width:48px; height:48px; border:2px solid #ac9463; border-radius:50%; margin:auto; color:#9d7a32; font-size:1.4rem; }
#v-course .chapter-ending .chapter-go { justify-content:center; width:auto; margin:16px auto; }
.chapter-next-level { margin-top:12px; }
@media(min-width:950px) {
  #v-course .course-atlas { display:grid; grid-template-columns:230px minmax(0,1fr); gap:0 32px; align-items:start; }
  #v-course .course-atlas > .atlas-heading { grid-column:1/-1; }
  /* THE RAIL IS A CELL, THE TABS STICK INSIDE IT (12 Sep 2026). A sticky
     grid item is held by the grid container, not by its area, so the tabs
     rode past the panel's end and covered the caption and the case explorer
     in rows 3–4. Inside a cell that stretches to the row, they cannot. */
  #v-course .course-atlas > .level-rail { grid-column:1; grid-row:2; align-self:stretch; min-width:0; }
  #v-course .course-atlas > .level-rail > .level-tabs { display:grid; grid-template-columns:1fr; position:sticky; top:90px; }
  #v-course .course-atlas > .level-panel { grid-column:2; grid-row:2; min-width:0; }
  #v-course .course-atlas > .level-caption { grid-column:1/-1; grid-row:3; }
  #v-course .course-atlas > .case-explorer { grid-column:1/-1; grid-row:4; }
}
@media(max-width:640px) {
  .chapter-heading { grid-template-columns:1fr 80px; padding:18px; gap:10px; }
  .chapter-heading h2 { font-size:1.45rem; }
  .chapter-heading p { font-size:.83rem; }
  .chapter-stops { margin-left:7px; }
  .chapter-stop { padding-left:18px; }
  #v-course .chapter-journey .chapter-go { padding:14px; gap:12px; }
  .chapter-book { flex-basis:34px; height:44px; }
  .chapter-label strong { font-size:1.07rem; }
}
@media(prefers-reduced-motion:reduce) { #v-course .chapter-journey .chapter-go { transition:none; transform:none; } }

/* A winding ink trail, with little volumes instead of a menu of cards. */
.chapter-stops { border:0; margin:18px 0; }
.chapter-stop { height:176px; padding:0; isolation:isolate; }
.chapter-stop::before { display:none; }
.chapter-ink-route { position:absolute; inset:0; width:100%; height:176px; overflow:visible; z-index:-1; pointer-events:none; }
.chapter-ink-route path { fill:none; stroke:#b6ab8a; stroke-width:3; stroke-dasharray:5 7; stroke-linecap:round; vector-effect:non-scaling-stroke; }
.is-done .chapter-ink-route path { stroke:#638f7e; stroke-dasharray:none; }
#v-course .chapter-journey .chapter-stop .chapter-go { width:170px; min-height:0; margin-left:calc(var(--stop-x) - 85px); padding:0; display:flex; flex-direction:column; gap:8px; text-align:center; background:transparent; border:0; box-shadow:none; }
#v-course .chapter-journey .chapter-stop .chapter-go > span:last-child { display:none; }
.chapter-stop .chapter-book { flex:0 0 auto; width:68px; height:80px; border:2px solid #a79670; border-left:7px double #a79670; border-radius:5px 15px 15px 5px; box-shadow:4px 5px 0 #a7967028; font-size:1.5rem; }
.chapter-stop .chapter-label { padding:7px 10px; border-radius:12px; background:var(--paper); }
.chapter-stop .chapter-label strong { font-size:1.02rem; }
.chapter-stop .chapter-label > span { display:none; }
.chapter-stop .chapter-label small { font-size:.7rem; margin-bottom:3px; }
.is-current .chapter-book { outline:3px solid #cfac54; outline-offset:5px; box-shadow:4px 7px 0 #9d7a3240; }
.chapter-trail-friend { position:absolute; top:5px; left:calc(var(--stop-x) + 42px); width:56px; height:66px; pointer-events:none; }
.chapter-trail-friend svg { width:100%; height:100%; }
.chapter-trail-friend.is-lively { animation:chapter-hop 1.2s ease-in-out 2; transform-origin:50% 100%; }
.chapter-ending { max-width:480px; margin:36px auto 20px; }
.lesson-first-guide { padding:14px 18px; border-left:3px solid #8ca996; border-radius:0 12px 12px 0; background:var(--world-tint,var(--paper)); font:.9rem/1.6 var(--sans); color:var(--carbon); }
.lesson-given-meaning { font:500 1.05rem/1.6 var(--sans); color:var(--carbon); margin:8px 0; }
.lesson-specimen .lesson-line-tools { align-items:center; gap:14px; flex-wrap:wrap; }
@keyframes chapter-hop { 0%,100% { transform:translateY(0) rotate(-5deg); } 45% { transform:translateY(-14px) rotate(7deg); } 75% { transform:translateY(-3px) rotate(-3deg); } }
@media(max-width:640px) {
  .chapter-stops { margin:18px 0; }
  .chapter-stop { padding:0; }
  #v-course .chapter-journey .chapter-stop .chapter-go { width:150px; margin-left:calc(var(--stop-x) - 75px); }
  .chapter-trail-friend { width:42px; left:calc(var(--stop-x) + 37px); }
}
@media(prefers-reduced-motion:reduce) { .chapter-trail-friend.is-lively { animation:none; } }

.word-first-meeting { padding:20px 0; }
.word-first-pair { display:grid; gap:12px; padding:26px; margin:18px 0; border:1px solid var(--rule); border-radius:18px; background:var(--paper); text-align:center; }
.word-first-pair strong { font:600 2rem/1.4 var(--serif); }
.word-first-pair > span { font:500 1.15rem/1.5 var(--sans); }
.word-first-meeting > p,.word-first-meeting blockquote { font:.95rem/1.6 var(--sans); }
.word-first-meeting blockquote { border-left:3px solid #a79670; margin:18px 0; padding:0 20px; }


/* ===== 105-living-lessons.css ===== */
/* Living lessons: an illustrated stage, a pocket postcard and an editorial
   spread. The residents are the existing rigged cast, not new static art. */
.living-shelf,.lesson-theatre,.theatre-discovery,.living-stage { --scene-ink:#335d52; --scene-wash:#dbe9df; --scene-accent:#edba64; --scene-night:#203e39; }
[data-setting="cafe"] { --scene-ink:#7e4545; --scene-wash:#f0dfd6; --scene-accent:#e1ac51; --scene-night:#523b45; }
[data-setting="bookshop"] { --scene-ink:#475484; --scene-wash:#e0e5f1; --scene-accent:#d2ae65; --scene-night:#303c60; }
[data-setting="plaza"] { --scene-ink:#96502f; --scene-wash:#f4e5ca; --scene-accent:#ddb550; --scene-night:#68432e; }
[data-setting="library"] { --scene-ink:#565077; --scene-wash:#e8e3f0; --scene-accent:#c8a763; --scene-night:#36334c; }
.living-shelf { margin:0 0 28px; min-width:0; }
.language-welcome { position:relative; display:grid; grid-template-columns:1.15fr 1fr; gap:20px; align-items:center; overflow:hidden; padding:clamp(24px,4vw,42px); border:1px solid var(--rule); border-top:3px solid var(--pine); border-radius:var(--learning-radius); background:var(--galley); color:var(--carbon); }
.welcome-copy { position:relative; z-index:1; }
.welcome-copy .smallcaps { color:var(--pine); font-size:var(--type-meta); letter-spacing:.08em; }
.language-welcome .welcome-copy h2 { font:500 clamp(2rem,4vw,3rem)/1.1 var(--serif); text-transform:none; letter-spacing:-.05em; margin:14px 0; overflow-wrap:anywhere; }
.welcome-copy p { max-width:330px; font:400 var(--type-body)/1.6 var(--sans); }
.welcome-copy > small { display:block; margin-top:14px; font:var(--type-meta)/1.5 var(--sans); }
.welcome-illustration { position:relative; min-width:0; transform:rotate(-3deg); }
.welcome-illustration svg { width:100%; height:auto; display:block; }
.welcome-speech { position:absolute; top:-10%; left:12%; max-width:85%; padding:12px 18px; background:var(--paper); border:1px solid color-mix(in srgb,var(--scene-ink) 25%,transparent); border-radius:20px 20px 20px 0; font:500 1.2rem/1.3 var(--serif); transform:rotate(3deg); box-shadow:0 4px 0 #00000009; }
.living-shelf .btn,.living-stage .btn,.lesson-theatre .btn,.theatre-discovery .btn { min-height:46px; padding:12px 20px; border-radius:var(--learning-control-radius); font:650 var(--type-control)/1.5 var(--sans); text-transform:none; letter-spacing:0; }
/* The old exercise skin marks its blue buttons !important; these stage-owned
   declarations keep one yellow action across its normal, hover and press states. */
.living-shelf .btn.primary,.living-stage .btn.primary,.lesson-theatre .btn.primary,.theatre-discovery .btn.primary { background:var(--signal)!important; border-color:var(--signal-ink); color:var(--on-signal)!important; box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent)!important; }
.living-shelf .btn.primary:hover:not(:disabled),.living-stage .btn.primary:hover:not(:disabled),.lesson-theatre .btn.primary:hover:not(:disabled),.theatre-discovery .btn.primary:hover:not(:disabled) { background:color-mix(in srgb,var(--signal) 90%,var(--sheet))!important;color:var(--on-signal)!important;border-color:var(--signal-ink);box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent)!important;transform:none; }
.living-shelf .btn.primary:active,.living-stage .btn.primary:active,.lesson-theatre .btn.primary:active,.theatre-discovery .btn.primary:active { transform:translateY(1px);box-shadow:0 1px 0 var(--signal-ink)!important; }
.living-shelf button:focus-visible,.living-stage button:focus-visible,.lesson-theatre button:focus-visible,.theatre-discovery button:focus-visible { outline:3px solid var(--pine); outline-offset:4px; }
.living-shelf-heading { display:block; padding:18px 0; border:0; background:transparent; position:static; height:auto; }
.living-shelf-heading h3 { margin:8px 0; font:500 var(--type-heading)/1.2 var(--serif); text-transform:none; letter-spacing:-.03em; }
.living-shelf-heading p { font:var(--type-body)/1.6 var(--sans); margin:8px 0; }
.living-cards { display:grid; gap:14px; }
.living-card { display:grid; grid-template-columns:155px minmax(0,1fr) 24px; align-items:center; gap:24px; background:var(--paper); padding:18px; color:var(--carbon); border:1px solid var(--rule); border-radius:var(--learning-radius); text-align:left; text-transform:none; letter-spacing:0; box-shadow:0 4px 0 color-mix(in srgb,var(--rule) 65%,transparent); }
.living-card-art svg { width:100%; display:block; }
.living-card-copy { display:grid; gap:10px; }
.living-card-copy small { font:600 var(--type-meta)/1.5 var(--sans); text-transform:uppercase; letter-spacing:.09em; }
.living-card-copy strong { font:500 var(--type-heading)/1.25 var(--serif); }
.living-card-copy > span { font:var(--type-body)/1.6 var(--sans); }
.living-card:hover { border-color:var(--scene-ink); background:color-mix(in srgb,var(--scene-wash) 28%,var(--paper)); }
.living-card[data-format="debate"] { border-top:5px solid var(--scene-ink); }
/* The beginner stage uses its own full-width scene, so the old illustrated
   compass becomes a small readable title instead of a second illustration. */
.gx-stage.gx-theatre .gx { width:min(980px,100%); }
.gx-theatre .lesson-compass { grid-template-columns:1fr; padding:14px 20px; border:0; background:transparent; border-radius:0; box-shadow:none; }
.gx-theatre .lesson-window { display:none; }
.gx-theatre .lesson-progress { display:none; }
.gx-stage.gx-theatre .gx-mid { overflow:visible; padding:0; border:0; box-shadow:none; background:transparent; }
.gx-theatre .gx-head:empty,.gx-theatre #gx-after:empty { display:none; }
.lesson-theatre { display:grid; grid-template-columns:minmax(0,1fr); position:relative; border:1px solid color-mix(in srgb,var(--scene-ink) 30%,transparent); border-radius:18px; overflow:hidden; background:var(--paper); color:var(--carbon); }
.theatre-heading { position:relative; display:flex; justify-content:space-between; padding:12px 20px; background:var(--pine); color:var(--on-pine); font:650 var(--type-meta)/1.5 var(--sans); letter-spacing:.04em; }
.theatre-set { grid-area:2/1; position:relative; min-height:350px; overflow:hidden; background:linear-gradient(0deg,color-mix(in srgb,var(--scene-ink) 23%,var(--scene-wash)) 0 14%,transparent 14%),var(--scene-wash); }
.theatre-window { position:absolute; width:130px; height:160px; border:9px solid color-mix(in srgb,var(--scene-ink) 45%,white); border-radius:65px 65px 0 0; background:#fffdf599; left:38px; top:24px; overflow:hidden; }
.theatre-window::after { content:''; position:absolute; inset:0; background:linear-gradient(90deg,transparent 47%,color-mix(in srgb,var(--scene-ink) 45%,white) 47% 53%,transparent 53%),linear-gradient(0deg,transparent 47%,color-mix(in srgb,var(--scene-ink) 45%,white) 47% 53%,transparent 53%); }
.theatre-window i { position:absolute; bottom:0; background:var(--scene-ink); opacity:.16; width:35%; height:35%; }
.theatre-window i:nth-child(2){left:35%;height:47%;}.theatre-window i:nth-child(3){left:70%;height:30%;}
.theatre-sun { width:58px; height:58px; border-radius:50%; background:var(--scene-accent); position:absolute; right:70px; top:35px; opacity:.65; }
.theatre-shelf { position:absolute; right:35px; top:110px; width:130px; height:12px; background:var(--scene-ink); opacity:.6; }
.theatre-shelf i { position:absolute; bottom:12px; height:45px; width:16px; left:10px; border-radius:2px 3px 0 0; background:var(--scene-ink); }
.theatre-shelf i:nth-child(2){left:28px;height:52px;background:var(--scene-accent);}.theatre-shelf i:nth-child(3){left:46px;height:40px;}.theatre-shelf i:nth-child(4){left:68px;height:49px;transform:rotate(-10deg);}.theatre-shelf i:nth-child(5){left:90px;height:38px;background:var(--scene-accent);}
.theatre-table { position:absolute; bottom:28px; left:35%; right:35%; height:85px; border-top:12px solid var(--scene-ink); border-left:8px solid var(--scene-ink); border-right:8px solid var(--scene-ink); border-radius:10px 10px 0 0; opacity:.55; }
.theatre-table i { position:absolute; top:-36px; left:24%; width:26px; height:24px; border-radius:2px 2px 12px 12px; background:var(--paper); }
.theatre-table i::after { content:''; position:absolute; width:12px; height:12px; border:3px solid var(--paper); right:-10px; top:4px; border-radius:50%; }
.theatre-plant { position:absolute; right:15px; bottom:25px; height:60px; width:42px; background:var(--scene-ink); border-radius:4px 4px 14px 14px; opacity:.4; }
.theatre-plant i { position:absolute; bottom:50px; left:10px; width:22px; height:75px; border-radius:100% 0; background:var(--scene-ink); transform:rotate(-30deg); }
.theatre-plant i:nth-child(2){transform:rotate(25deg);left:24px;height:90px;}.theatre-plant i:nth-child(3){transform:rotate(-65deg);left:-8px;height:55px;}
.theatre-resident { position:absolute; bottom:18px; width:190px; height:200px; z-index:2; transform-origin:50% 100%; transition:transform .25s ease,opacity .25s ease; }
.theatre-resident svg { width:100%; height:100%; display:block; }
.theatre-resident-a{left:2%;}.theatre-resident-b{right:2%;}
[data-speaker="a"] .theatre-resident-a,[data-speaker="b"] .theatre-resident-b{transform:scale(1.12);}
[data-speaker="a"] .theatre-resident-b,[data-speaker="b"] .theatre-resident-a{opacity:.7;}
.theatre-dialogue { grid-area:2/1; position:relative; align-self:start; margin:26px 24% 110px; padding:22px 26px; background:#fffdf7; border:1px solid color-mix(in srgb,var(--scene-ink) 20%,transparent); border-radius:24px 24px 24px 3px; box-shadow:0 7px 0 #00000008; z-index:3; }
[data-speaker="b"] .theatre-dialogue { border-radius:24px 24px 3px 24px; }
.theatre-speaker { font:700 var(--type-meta)/1.5 var(--sans); text-transform:uppercase; letter-spacing:.12em; color:var(--scene-ink); }
.theatre-dialogue .theatre-line { font:500 clamp(1.375rem,1.25rem + 1vw,2rem)/1.45 var(--serif); margin:10px 0 12px; letter-spacing:-.02em; font-style:normal; overflow-wrap:anywhere; }
.theatre-line-actions { display:flex; flex-wrap:wrap; gap:12px; align-items:center; }
.theatre-audio,.theatre-translation,.theatre-skip,.living-text-button { background:transparent; border:0; color:var(--pine); min-height:44px; font:600 var(--type-control)/1.5 var(--sans); padding:6px 0; box-shadow:none; text-transform:none; letter-spacing:0; }
.theatre-audio { width:44px; height:44px; padding:11px; border-radius:50%; background:var(--scene-wash); flex-shrink:0; }
.theatre-audio svg { width:22px; height:22px; }
.theatre-translation { text-decoration:underline; text-underline-offset:4px; }
.theatre-dialogue .theatre-meaning { font:var(--type-body)/1.6 var(--sans); margin:10px 0 0; }
.theatre-audio-status { font:var(--type-meta)/1.5 var(--sans); margin:0; }
.theatre-audio-status:empty { display:none; }
.theatre-turns { display:flex; justify-content:center; gap:7px; padding:17px 18px 3px; flex-wrap:wrap; }
.theatre-turns button { width:44px; height:44px; border-radius:50%; border:1px solid var(--rule); background:transparent; color:var(--carbon); font:600 var(--type-meta)/1 var(--sans); padding:0; box-shadow:none; }
.theatre-turns [aria-current] { background:var(--scene-ink); color:white; border-color:var(--scene-ink); }
.theatre-footer { display:flex; flex-wrap:wrap; justify-content:space-between; align-items:center; gap:18px; padding:18px 24px; }
.theatre-transcript { margin:0 24px 20px; border-top:1px solid var(--rule); font:var(--type-body)/1.6 var(--sans); }
.theatre-transcript summary { font-size:var(--type-control); cursor:pointer; padding:13px 0; min-height:44px; }
.theatre-transcript p { display:grid; grid-template-columns:70px 1fr; gap:4px 15px; margin:14px 0; }
.theatre-transcript p > span { font:500 var(--type-prompt)/1.5 var(--serif); }.theatre-transcript small{grid-column:2;font-size:var(--type-body);}
[data-setting="garden"] .theatre-window,[data-setting="plaza"] .theatre-window { border:0; background:transparent; width:220px; }
[data-setting="garden"] .theatre-window::after,[data-setting="plaza"] .theatre-window::after{display:none;}
[data-setting="garden"] .theatre-shelf{display:none;}
[data-setting="garden"] .theatre-window i { border-radius:100% 100% 0 0; height:80%; }
[data-setting="library"] .theatre-shelf,[data-setting="bookshop"] .theatre-shelf { box-shadow:0 70px 0 var(--scene-ink); }
.theatre-discovery { padding:clamp(24px,4vw,44px); border:1px solid var(--rule); background:var(--paper); border-radius:18px; }
.theatre-discovery h2 { font:500 var(--type-heading)/1.25 var(--serif); text-transform:none; margin:10px 0 20px; }
.theatre-phrase { display:grid; grid-template-columns:130px 1fr; align-items:center; gap:18px; background:var(--scene-wash); border-radius:12px; padding:12px 24px 12px 4px; }
.theatre-coach { display:block; width:130px; height:140px; }.theatre-coach svg{width:100%;height:100%;}
.theatre-phrase blockquote { margin:0; padding:0; font:500 var(--type-prompt)/1.5 var(--serif); border:0; background:none; font-style:normal; }
.theatre-phrase blockquote small { display:block; font:400 var(--type-body)/1.6 var(--sans); margin-top:8px; }
.theatre-note { max-width:58ch; font:400 var(--type-body)/1.65 var(--sans); margin:22px 0; }
.theatre-word-cloud { display:flex; flex-wrap:wrap; gap:12px; margin:18px 0; }
.theatre-word-cloud button { display:grid; gap:5px; text-align:left; background:var(--paper); border:1px solid var(--rule); border-radius:10px; color:var(--carbon); padding:13px 18px; text-transform:none; letter-spacing:0; box-shadow:0 3px 0 #00000007; transform:rotate(-1deg); }
.theatre-word-cloud button:nth-child(even){transform:rotate(1deg);}
.theatre-word-cloud strong { font:500 1.4rem/1.4 var(--serif); }.theatre-word-cloud span{font:var(--type-body)/1.5 var(--sans);}
.theatre-word-cloud [aria-pressed="true"]{background:var(--scene-wash);border-color:var(--scene-ink);transform:translateY(-3px);}
.theatre-word-status { font:var(--type-body)/1.6 var(--sans); min-height:26px; }
/* Standalone original reading activities share the modal/focus contract. */
.gx-stage.living-stage { display:block; overflow-y:auto; padding:24px max(18px,env(safe-area-inset-right)) 48px max(18px,env(safe-area-inset-left)); color:var(--carbon); }
.living-sheet { width:min(980px,100%); margin:0 auto; }
.living-top { display:flex; justify-content:space-between; align-items:center; gap:20px; position:static; width:auto; height:auto; min-height:0; background:transparent; box-shadow:none; border:0; padding:0 0 14px; font:650 var(--type-meta)/1.5 var(--sans); }
.living-top>span { min-width:0;overflow-wrap:anywhere; }
.living-top .btn { flex-shrink:0;max-width:100%; }
.living-top b { margin-left:10px; border:1px solid var(--rule); padding:4px 8px; border-radius:4px; }
.living-steps { display:flex; justify-content:space-between; gap:12px; border-bottom:1px solid var(--rule); padding:0 0 12px; margin-bottom:18px; font:600 var(--type-meta)/1.5 var(--sans); }
.living-steps > span { display:flex; align-items:center; gap:8px; min-width:0; flex:1 1 0; color:var(--pencil); }.living-steps > span > i{font-style:normal;display:grid;place-items:center;flex-shrink:0;min-width:26px;height:26px;border:1px solid var(--rule);border-radius:50%;}
.living-steps > span > .living-step-label { min-width:0; overflow-wrap:anywhere; }
.living-steps > .is-current{color:var(--carbon);}.living-steps > .is-current > i,.living-steps > .is-done > i{background:var(--pine);color:var(--on-pine);border-color:var(--pine);}
.living-content { min-width:0; }.living-content h1,.living-content h2,.living-content h3 { text-transform:none; letter-spacing:-.025em; font-family:var(--serif); }
.living-content h3{font-size:1.4rem;line-height:1.45;}
.living-footer { display:flex; flex-wrap:wrap; gap:14px; justify-content:flex-end; padding:24px 0 0; }
.living-welcome-hero { display:grid; grid-template-columns:minmax(0,1fr) 110px; gap:20px; align-items:center; padding:0 0 18px; }
.living-welcome-hero h1 { font:500 clamp(2rem,4vw,3rem)/1.1 var(--serif); margin:12px 0; }
.living-welcome-hero>.living-coach { width:110px;height:120px; }
.living-welcome-hero p { font:400 var(--type-body)/1.6 var(--sans); margin:12px 0; max-width:50ch; }
.living-coach { display:block; width:150px; height:160px; }.living-coach svg{width:100%;height:100%;}
.living-article { background:var(--paper); padding:clamp(24px,4vw,44px); position:relative; border:1px solid color-mix(in srgb,var(--scene-ink) 25%,transparent); box-shadow:6px 6px 0 color-mix(in srgb,var(--scene-ink) 10%,transparent); }
.living-article > header { position:static; display:block; height:auto; min-height:0; background:none; color:var(--carbon); padding:0; border:0; box-shadow:none; width:65%; }
.living-article h1 { font:500 clamp(2rem,4vw,3rem)/1.1 var(--serif); margin:18px 0; letter-spacing:-.05em; }
.living-article > header > p { font:var(--type-body)/1.6 var(--sans); max-width:38ch; }
.living-article-hero { position:absolute; right:25px; top:36px; width:28%; transform:rotate(3deg); }.living-article-hero svg{width:100%;display:block;}
.living-read-hint { margin:30px 0 0; padding:15px 0; border-top:2px solid var(--scene-ink); font:var(--type-body)/1.6 var(--sans); }
.living-paragraphs { display:grid; grid-template-columns:repeat(3,minmax(0,1fr)); gap:26px; }
.living-paragraphs section { min-width:0; }.living-paragraphs section > p{font:400 var(--type-prompt)/1.65 var(--serif);margin:8px 0 12px;font-style:normal;}
.living-paragraph-number{font:600 var(--type-meta)/1.5 var(--sans);color:var(--scene-ink);}
.living-paragraphs details{font:var(--type-body)/1.6 var(--sans);}.living-paragraphs summary{min-height:44px;padding:10px 0;cursor:pointer;color:var(--scene-ink);}
.living-highlight { display:inline; background:color-mix(in srgb,var(--scene-accent) 40%,transparent); border:0; border-bottom:2px solid var(--scene-accent); border-radius:2px; color:inherit; font:inherit; line-height:inherit; text-transform:none; letter-spacing:0; padding:0 3px; margin:0; box-shadow:none; }
.living-highlight[aria-expanded="true"]{background:var(--scene-accent);}
.living-phrase-note { margin:20px 0 0; padding:18px 22px; background:var(--scene-wash); border-left:4px solid var(--scene-ink); font:var(--type-body)/1.6 var(--sans); }.living-phrase-note strong{font:500 1.4rem/1.4 var(--serif);}
.living-stage[data-format="postcard"] .living-article { border:8px solid var(--paper); outline:1px solid var(--rule); background:repeating-linear-gradient(0deg,transparent 0 34px,#8888880c 34px 35px),var(--paper); border-radius:2px; }
.living-stage[data-format="postcard"] .living-paragraphs { grid-template-columns:1fr; gap:16px; max-width:680px; }
.living-stage[data-format="postcard"] .living-paragraphs section { display:grid;grid-template-columns:24px 1fr;gap:0 14px;}.living-stage[data-format="postcard"] .living-paragraphs details{grid-column:2;}
.living-stage[data-format="debate"] .living-article { border-top:12px solid var(--scene-ink); }
.living-stage[data-format="debate"] .living-paragraphs { grid-template-columns:1fr 1fr; }.living-stage[data-format="debate"] .living-paragraphs section:last-child{grid-column:1/-1;border-top:1px solid var(--rule);padding-top:20px;}
.living-notice { display:grid; grid-template-columns:160px 1fr; gap:24px; align-items:center; }.living-notice h2{font:500 var(--type-heading)/1.25 var(--serif);margin:12px 0;}.living-notice p{font:var(--type-body)/1.65 var(--sans);max-width:55ch;}
.living-example { margin:24px 0; padding:24px 30px; border-left:4px solid var(--scene-ink); background:var(--paper); border-radius:0 14px 14px 0; font:500 var(--type-prompt)/1.6 var(--serif); font-style:normal; }
.living-options { display:grid; gap:12px; }.living-options button{min-height:60px;padding:18px 24px;background:var(--paper);border:1px solid var(--rule);border-radius:9px;color:var(--carbon);text-align:left;font:500 var(--type-prompt)/1.5 var(--serif);text-transform:none;letter-spacing:0;box-shadow:0 3px 0 #00000006;}
.living-options .is-right { border-color:var(--scene-ink); background:color-mix(in srgb,var(--scene-ink) 12%,var(--paper)); }
.living-feedback { margin-top:24px; padding:22px 26px; background:var(--paper); border-left:4px solid var(--scene-ink); border-radius:0 12px 12px 0; }.living-feedback h3{margin:0 0 12px;}.living-feedback p{font:var(--type-body)/1.6 var(--sans);}.living-feedback .btn{margin-top:12px;}
.living-recall { position:relative; padding:clamp(24px,4vw,44px); background:var(--paper); border-radius:16px; border:1px solid var(--rule); }.living-recall > .living-coach{float:right;width:100px;height:110px;}.living-recall h2{font-size:2rem;line-height:1.25;max-width:24ch;}.living-recall-line{clear:both;font:500 var(--type-prompt)/1.65 var(--serif);padding:20px 0;}.living-recall-line span{display:inline-block;min-width:90px;text-align:center;border-bottom:2px dashed var(--scene-ink);color:var(--scene-ink);}
#living-recall-form { display:grid; gap:12px; }#living-recall-form label{font:var(--type-body)/1.6 var(--sans);}#living-recall-input{width:100%;min-height:58px;background:var(--paper);border:2px solid var(--scene-ink);border-radius:8px;padding:12px 16px;font:var(--type-prompt)/1.5 var(--serif);color:var(--carbon);box-sizing:border-box;}
.living-support { display:flex;align-items:center;gap:18px;flex-wrap:wrap;margin-top:22px; }
.living-finish { text-align:center; padding:28px 20px; }.living-finish h1{font:500 clamp(2rem,4vw,3rem)/1.1 var(--serif);margin:18px auto;max-width:15ch;}.living-finish > p{font:var(--type-body)/1.65 var(--sans);max-width:50ch;margin:18px auto;}.living-finish > .living-coach{margin:12px auto 0;}
.living-finish-seal { display:grid;place-items:center;width:68px;height:68px;margin:0 auto 24px;border:2px solid var(--scene-ink);outline:1px dashed var(--scene-ink);outline-offset:5px;border-radius:50%;background:var(--paper);color:var(--scene-ink);transform:rotate(-8deg);}.living-finish-seal svg{width:32px;height:32px;}
.living-finish-phrase { font:500 var(--type-prompt)/1.5 var(--serif); background:var(--paper); padding:18px 24px; display:inline-block; border-radius:8px; }.living-due{display:flex;justify-content:center;gap:12px;align-items:center;}.living-due strong{padding:6px 12px;background:var(--scene-ink);color:#fff;border-radius:5px;}
/* Practice choices remain on the entrance, with real due counts. */
.practice-paths { display:grid; grid-template-columns:1fr 1fr; gap:16px; margin:24px 0; }
.practice-path { display:grid; grid-template-columns:54px 1fr; align-items:center; gap:16px; padding:20px; border:1px solid var(--rule); border-radius:14px; background:var(--paper); color:var(--carbon); text-align:left; text-transform:none; letter-spacing:0; }
.practice-path > svg { width:46px; height:46px; }.practice-path > span { display:grid;gap:7px; }.practice-path strong{font:650 var(--type-control)/1.5 var(--sans);}.practice-path small{font:var(--type-body)/1.6 var(--sans);}.practice-path b{font:650 var(--type-meta)/1.5 var(--sans);color:var(--pine);}
@media(min-width:1000px){.course-atlas .level-panel{min-width:0;}.living-shelf .language-welcome{grid-template-columns:1.15fr .85fr;}}
@media(max-width:650px){
 .language-welcome { grid-template-columns:1fr 110px; padding:24px 20px; gap:8px; }.welcome-copy p{font-size:var(--type-body);}.language-welcome .welcome-copy h2{font-size:clamp(2rem,4vw,3rem);}.welcome-speech{font-size:var(--type-body);left:-10%;padding:8px;top:-20%;}.welcome-illustration{align-self:start;margin-top:50px;}
 .living-card { grid-template-columns:85px minmax(0,1fr) 16px;gap:14px;padding:15px; }.living-card-copy strong{font-size:1.3rem;}.living-card-copy > span{font-size:var(--type-body);}
 .theatre-set { min-height:370px; }.theatre-dialogue{margin:26px 18px 155px;padding:18px 22px;}.theatre-dialogue .theatre-line{font-size:1.65rem;}.theatre-resident{width:130px;height:138px;bottom:8px;}.theatre-resident-a{left:5%;}.theatre-resident-b{right:5%;}.theatre-table{height:66px;left:37%;right:37%;}.theatre-window{width:85px;height:135px;left:18px;}.theatre-shelf{right:18px;width:85px;}.theatre-shelf i:nth-child(5){display:none;}
 .theatre-footer{padding:14px 18px;gap:12px;}.theatre-footer .btn{padding:12px 14px;font-size:var(--type-control);}.theatre-skip{font-size:var(--type-control);max-width:none;}.theatre-heading{padding:12px 18px;}
 .theatre-discovery{padding:22px;}.theatre-phrase{grid-template-columns:85px 1fr;padding:12px;gap:12px;}.theatre-coach{width:85px;height:100px;}.theatre-phrase blockquote{font-size:var(--type-prompt);}.theatre-word-cloud{gap:9px;}.theatre-word-cloud button{padding:10px 12px;}
 .living-welcome-hero{padding:0 0 18px;grid-template-columns:1fr 82px;}.living-welcome-hero .living-coach{width:82px;height:100px;}.living-welcome-hero h1{font-size:clamp(2rem,4vw,3rem);}.living-welcome-hero p{font-size:var(--type-body);}
 .living-top{gap:8px;flex-wrap:wrap;}.living-top > span{font-size:var(--type-meta);}.living-top .btn{font-size:var(--type-control);padding:10px 12px;}
 .living-article > header{width:100%;}.living-article h1{font-size:clamp(2rem,4vw,3rem);}.living-article-hero{position:relative;top:auto;right:auto;width:100%;max-width:250px;margin:16px auto;transform:rotate(-2deg);}.living-read-hint{margin-top:18px;}.living-paragraphs,.living-stage[data-format="debate"] .living-paragraphs{grid-template-columns:1fr;gap:20px;}.living-stage[data-format="debate"] .living-paragraphs section:last-child{grid-column:auto;}.living-paragraphs section > p{font-size:var(--type-prompt);}
 .living-notice{grid-template-columns:75px 1fr;gap:15px;}.living-notice .living-coach{width:75px;height:95px;}.living-notice h2{font-size:1.8rem;}.living-notice p{font-size:var(--type-body);}.living-example{padding:18px;font-size:var(--type-prompt);}.living-recall h2{font-size:1.6rem;}.living-recall > .living-coach{width:72px;height:82px;}.living-footer .btn{flex:1;}.practice-paths{grid-template-columns:1fr;}
}
/* Four or five positions fit a phone. The current name gets the full line;
   the other names remain in the accessibility tree at their own position. */
@media(max-width:600px){
 .living-steps { position:relative;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin-bottom:16px; }
 .living-steps[data-step-count="5"] { grid-template-columns:repeat(5,minmax(0,1fr)); }
 .living-steps > span { display:contents; }
 .living-steps > span > i { grid-row:1;justify-self:center;width:32px;height:32px; }
 .living-steps > .is-current > .living-step-label { grid-column:1/-1;grid-row:2;text-align:center; }
 .living-steps > span:not(.is-current) > .living-step-label { 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; }
}
@media(prefers-reduced-motion:reduce){.theatre-resident{transition:none;}.welcome-illustration,.welcome-speech,.living-article-hero,.theatre-word-cloud button{transform:none;}}
body.calm .theatre-resident{transition:none;}body.calm .theatre-word-cloud button{transform:none;}


/* ===== 106-language-missions.css ===== */
/* The mission collection: varied work, one familiar set of navigation controls. */
.mission-shelf-heading{display:grid;grid-template-columns:minmax(0,1fr) 150px;gap:24px;align-items:center;margin:0 0 22px;padding:24px;border:1px solid var(--rule);background:var(--paper);border-radius:20px 4px 20px 4px;position:static;height:auto;box-shadow:none;}
.mission-shelf-heading h3{font:500 clamp(1.8rem,3vw,2.7rem)/1.15 var(--serif);text-transform:none;letter-spacing:-.025em;margin:10px 0;}.mission-shelf-heading p{font:.9rem/1.6 var(--sans);max-width:52ch;margin:10px 0;}
.mission-journal{display:grid;text-align:center;gap:8px;padding:18px 0 18px 20px;border-left:1px dashed var(--rule);font:.75rem/1.5 var(--sans);}.mission-journal>strong{font:500 2.8rem/1 var(--serif);}.mission-journal small{font:500 1rem/1 var(--sans);}.mission-journal>div{display:flex;gap:7px;justify-content:center;flex-wrap:wrap;}.mission-journal>div>span{display:grid;place-items:center;width:24px;height:24px;border:1px solid var(--rule);border-radius:50%;color:var(--pencil);}.mission-journal svg{width:14px;height:14px;}.mission-journal .is-collected{background:#335d52;color:#fffdf7;border-color:#335d52;}
.mission-filters{display:flex;gap:8px;flex-wrap:wrap;margin:0 0 20px;}.mission-filters button{min-height:44px;padding:10px 16px;border:1px solid var(--rule);border-radius:24px;background:var(--paper);color:var(--carbon);font:600 .8rem/1.4 var(--sans);letter-spacing:0;text-transform:none;box-shadow:none;}.mission-filters [aria-pressed="true"]{background:#293c42;border-color:#293c42;color:#fffdf7;}
.mission-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:18px;}.mission-card{display:grid;grid-template-columns:minmax(0,1fr) 24px;grid-template-rows:140px 1fr;gap:18px;padding:22px;align-items:start;border-radius:18px 18px 6px 6px;overflow:hidden;}.mission-card[hidden]{display:none!important;}.mission-card .living-card-art{grid-column:1/-1;width:100%;height:140px;display:grid;place-items:center;background:#e6e1ec;border-radius:10px;position:relative;}.mission-card .living-card-art>svg{width:auto;height:135px;max-width:100%;}.mission-card .living-card-copy{grid-column:1;gap:10px;}.mission-card .living-card-copy strong{font-size:1.6rem;}.mission-card .living-card-copy small{font-size:.7rem;}.mission-card .living-card-arrow{grid-column:2;align-self:center;font-size:1.5rem;}
.mission-card[data-format="roleplay"] .living-card-art{background:#e2eee5;}.mission-card[data-format="negotiation"] .living-card-art{background:#e4e8f5;}.mission-card[data-format="timeline"] .living-card-art{background:#f5e6c9;}.mission-card[data-format="editor"] .living-card-art{background:#e2ebf0;}.mission-card[data-format="bridge"] .living-card-art{background:#efdfd7;}
.mission-card-cast{position:relative;display:flex;align-items:end;justify-content:center;height:125px;width:100%;}.mission-card-cast>svg{width:105px;height:105px;}.mission-card-cast>svg:nth-child(2){transform:rotate(6deg);}.mission-card-cast>i{position:absolute;top:4px;left:40%;font:500 1.8rem/1 var(--serif);background:var(--paper);border-radius:20px 20px 20px 0;padding:0 15px 12px;}
.mission-card-bridge{display:flex;align-items:center;gap:18px;}.mission-card-bridge>svg{width:65px;height:65px;}.mission-card-bridge i{font:500 2rem var(--serif);}
.mission-card-timeline{display:flex;gap:10px;align-items:center;}.mission-card-timeline i{display:grid;place-items:center;font:500 1.8rem var(--serif);width:55px;height:73px;background:#fffdf7;box-shadow:3px 4px 0 #56431d18;border-radius:5px;transform:rotate(-6deg);}.mission-card-timeline i:nth-child(2){transform:translateY(-12px) rotate(4deg);}.mission-card-timeline i:nth-child(3){transform:rotate(8deg);}
.mission-card-letter{width:135px;height:106px;background:#fffdf7;box-shadow:5px 5px 0 #26384518;padding:16px;transform:rotate(-4deg);display:grid;gap:9px;}.mission-card-letter svg{width:28px;height:28px;justify-self:end;}.mission-card-letter i{height:2px;background:#617c8b66;width:90%;}.mission-card-letter i:last-child{width:60%;}
.mission-collection-note,.mission-no-results{font:.8rem/1.6 var(--sans);margin:20px 0;color:var(--pencil);}
.mission-heading{max-width:780px;margin:12px 0 28px;}.mission-heading h1{font:500 clamp(2.5rem,5vw,4.5rem)/1.08 var(--serif);margin:15px 0;letter-spacing:-.035em;}.mission-heading h2{font:500 clamp(1.8rem,4vw,3rem)/1.2 var(--serif);margin:14px 0;}.mission-heading>p{font:1rem/1.65 var(--sans);max-width:65ch;}.mission-heading>.smallcaps{color:var(--scene-ink);font-size:.75rem;}
.living-stage[data-format="negotiation"]{--scene-wash:#dde5f1;--scene-ink:#3e526f;}.living-stage[data-format="editor"]{--scene-wash:#dde9e6;--scene-ink:#375d56;}.living-stage[data-format="timeline"]{--scene-wash:#f0e3ca;--scene-ink:#7c552d;}.living-stage[data-format="bridge"]{--scene-wash:#eadfd7;--scene-ink:#754c3e;}
.mission-register{display:grid;grid-template-columns:1fr 1fr;gap:24px;}.mission-register>section{min-width:0;padding:30px;}.mission-register blockquote{margin:24px 0;padding:0;border:0;background:none;font:500 clamp(1.3rem,2.7vw,2rem)/1.6 var(--serif);font-style:normal;}.mission-register p{font:.95rem/1.7 var(--sans);}.mission-register-intro{font:.95rem/1.65 var(--sans);max-width:65ch;margin:0 0 26px;}.mission-old{background:#fcf3dd;border:1px solid #b49a72;box-shadow:5px 5px 0 #79543515;transform:rotate(-1deg);}.mission-old small{font:.75rem/1.5 var(--sans);color:#6b543e;}.mission-now{border:2px solid #587d70;border-radius:22px;background:#f8fff9;}.mission-register-mate{display:block;width:110px;height:115px;margin:12px auto;}.mission-register-mate>svg{width:100%;height:100%;}
.mission-documents{display:grid;gap:18px;max-width:820px;margin:0 auto;}.mission-documents>section{position:relative;min-width:0;padding:25px 28px 25px 70px;background:var(--paper);border:1px solid var(--rule);border-radius:20px 20px 20px 0;}.mission-message-stack>section:nth-child(2){margin-left:10%;border-radius:20px 20px 0 20px;background:#f8efd9;}.mission-message-stack>section:nth-child(3){margin-right:8%;}.mission-document-number{position:absolute;left:22px;top:28px;font:600 .85rem/1.3 var(--sans);color:var(--scene-ink);}.mission-documents>section>p{font:500 1.35rem/1.65 var(--serif);margin:0 0 12px;}.mission-documents details,.mission-prompt-support{font:.85rem/1.65 var(--sans);}.mission-documents summary,.mission-prompt-support summary{min-height:44px;cursor:pointer;padding:10px 0;color:var(--scene-ink);}.mission-editor-brief{gap:0;border-top:7px solid var(--scene-ink);box-shadow:6px 6px 0 #26382a16;}.mission-editor-brief>section{border-radius:0;border:0;border-bottom:1px solid var(--rule);}
.mission-response-stage{display:grid;grid-template-columns:120px minmax(0,1fr) 120px;align-items:center;gap:24px;margin:0 0 30px;padding:24px;background:var(--paper);border:1px solid var(--rule);border-radius:24px;}.mission-response-stage h2{font:500 clamp(1.5rem,3vw,2.2rem)/1.45 var(--serif);margin:12px 0;}.mission-reply-mate{display:block;width:120px;height:130px;}.mission-reply-mate svg{width:100%;height:100%;}.mission-options button{display:grid;grid-template-columns:30px minmax(0,1fr);gap:15px;align-items:center;}.mission-options button>span:first-child{font:600 .8rem/1.5 var(--sans);border:1px solid var(--rule);border-radius:50%;width:28px;height:28px;display:grid;place-items:center;}
.mission-order{list-style:none;margin:24px 0;padding:0;display:grid;gap:15px;}.mission-order li{display:grid;grid-template-columns:38px minmax(0,1fr) 95px;align-items:center;gap:18px;padding:20px;background:var(--paper);border:1px solid var(--rule);border-radius:12px;box-shadow:0 3px 0 #00000009;}.mission-order li>b{font:500 1.6rem/1.2 var(--serif);color:var(--scene-ink);}.mission-order li>span{font:500 1.4rem/1.5 var(--serif);}.mission-order li>div{display:flex;gap:6px;}.mission-order button{width:44px;height:44px;border:1px solid var(--rule);border-radius:50%;background:var(--scene-wash);color:var(--carbon);font:500 1.4rem/1 var(--serif);padding:0;box-shadow:none;}.mission-order button:disabled{opacity:.35;}.mission-order-answer{font:500 1.25rem/1.7 var(--serif);padding:20px;background:var(--paper);border-left:4px solid var(--scene-ink);}
.mission-writing{display:grid;grid-template-columns:minmax(0,1.25fr) minmax(0,.75fr);gap:24px;align-items:start;}.mission-writing>section{min-width:0;}.mission-writing label{display:block;font:600 .85rem/1.4 var(--sans);margin:0 0 12px;}.mission-writing textarea{display:block;box-sizing:border-box;resize:vertical;width:100%;min-height:260px;padding:24px;color:var(--carbon);background:repeating-linear-gradient(0deg,transparent 0 31px,#3044380d 31px 32px),var(--paper);border:2px solid var(--scene-ink);border-radius:8px;font:400 1.25rem/1.6 var(--serif);}.mission-writing aside{padding:24px;background:var(--paper);border:1px solid var(--rule);border-radius:18px;}.mission-writing-mate{display:block;width:100px;height:105px;margin:-12px auto 14px;}.mission-writing-mate svg{width:100%;height:100%;}.mission-model{font:400 1.1rem/1.75 var(--serif);border-top:1px solid var(--rule);padding-top:20px;margin:20px 0 0;}.mission-speaking-cue{display:flex;gap:12px;align-items:center;font:.9rem/1.6 var(--sans);}.mission-speaking-cue svg{width:28px;height:28px;flex-shrink:0;}
.mission-self-review{margin:30px 0 16px;border:1px solid var(--rule);padding:24px;border-radius:12px;background:var(--paper);}.mission-self-review legend{font:.8rem/1.6 var(--sans);padding:0 8px;max-width:90%;}.mission-self-review label{display:flex;gap:14px;align-items:flex-start;min-height:44px;padding:9px 0;font:.95rem/1.6 var(--sans);cursor:pointer;}.mission-self-review input{width:22px;height:22px;flex-shrink:0;margin:0;accent-color:var(--scene-ink);}.mission-save-hint{font:.8rem/1.6 var(--sans);}.living-stage #mission-save:disabled{opacity:.45;box-shadow:none;}.mission-production-saved{padding:12px;border:1px dashed var(--scene-ink);border-radius:8px;}.mission-next-preview strong{display:block;font:500 1.6rem/1.5 var(--serif);margin-top:10px;}
.mission-desk-door{display:grid;grid-template-columns:140px minmax(0,1fr) 30px;gap:24px;align-items:center;width:100%;padding:22px;margin:16px 0;background:#e3ebe5;color:var(--carbon);border:1px solid var(--rule);border-radius:16px;text-align:left;text-transform:none;letter-spacing:0;}.mission-desk-door>span:first-child{max-width:140px;overflow:hidden;}.mission-desk-door>span:nth-child(2){display:grid;gap:8px;}.mission-desk-door strong{font:500 1.5rem/1.3 var(--serif);}.mission-desk-door small,.mission-desk-door>span>span{font:.8rem/1.5 var(--sans);}.mission-desk-door .mission-card-cast svg{width:65px;height:70px;}.mission-desk-door .mission-card-cast{height:90px;}.mission-desk-door .mission-card-timeline i{width:36px;height:55px;font-size:1.2rem;}.mission-desk-door svg{width:90px;height:80px;}.mission-desk-door .mission-card-bridge svg{width:45px;height:50px;}.mission-desk-door .mission-card-letter svg{width:28px;height:28px;}
.mission-reading-door{display:block;width:100%;max-width:1100px;margin:14px auto 22px;padding:16px 22px;background:#e6ebe3;border:1px solid var(--rule);border-radius:10px;color:var(--carbon);text-align:left;font:600 .9rem/1.5 var(--sans);text-transform:none;letter-spacing:0;box-shadow:none;}
.mission-filters button:focus-visible,.mission-order button:focus-visible,.mission-reading-door:focus-visible,.mission-desk-door:focus-visible{outline:3px solid var(--scene-ink,#335d52);outline-offset:4px;}.mission-writing textarea:focus-visible{outline:3px solid var(--scene-ink);outline-offset:3px;}
@media(max-width:650px){.mission-shelf-heading{padding:20px;gap:14px;grid-template-columns:minmax(0,1fr) 85px;}.mission-journal{padding:12px 0 12px 12px;font-size:.7rem;}.mission-journal>strong{font-size:2.1rem;}.mission-journal>div{gap:4px;}.mission-shelf-heading h3{font-size:1.85rem;}.mission-shelf-heading p{font-size:.85rem;}.mission-cards{grid-template-columns:1fr;}.mission-card{grid-template-columns:90px minmax(0,1fr) 20px;grid-template-rows:auto;padding:18px;gap:14px;}.mission-card .living-card-art{grid-column:1;height:110px;}.mission-card .living-card-copy{grid-column:2;}.mission-card .living-card-arrow{grid-column:3;}.mission-card .living-card-copy strong{font-size:1.35rem;}.mission-card .living-card-art>svg{height:auto;width:90px;}.mission-card-cast{height:95px;}.mission-card-cast>svg{width:47px;height:60px;}.mission-card-cast>i{font-size:1.3rem;left:25%;padding:0 10px 8px;}.mission-card-bridge{display:grid;gap:0;justify-items:center;}.mission-card-bridge>svg{width:32px;height:32px;}.mission-card-bridge i{font-size:1.2rem;}.mission-card-timeline{gap:2px;}.mission-card-timeline i{width:26px;height:45px;font-size:1rem;}.mission-card-letter{width:62px;height:85px;padding:10px;gap:6px;}.mission-card-letter svg{width:20px;height:20px;}.mission-register{grid-template-columns:1fr;gap:20px;}.mission-register>section{padding:24px;}.mission-register-mate{width:90px;height:95px;}.mission-heading h1{font-size:2.6rem;}.mission-heading>p{font-size:.9rem;}.mission-documents>section{padding:22px 20px 22px 48px;}.mission-document-number{left:18px;top:26px;}.mission-documents>section>p{font-size:1.2rem;}.mission-message-stack>section:nth-child(2){margin-left:6%;}.mission-response-stage{grid-template-columns:65px minmax(0,1fr);gap:14px;padding:18px;}.mission-reply-mate{width:65px;height:80px;}.mission-reply-mate:last-child{display:none;}.mission-response-stage h2{font-size:1.4rem;}.mission-options button{padding:16px;font-size:1.1rem;gap:10px;}.mission-order li{grid-template-columns:22px minmax(0,1fr);gap:12px;padding:18px;}.mission-order li>div{grid-column:2;justify-self:end;}.mission-order li>span{font-size:1.2rem;}.mission-writing{grid-template-columns:1fr;}.mission-writing textarea{font-size:1.15rem;padding:18px;min-height:230px;}.mission-writing aside{display:grid;grid-template-columns:70px 1fr;align-items:center;gap:12px;padding:18px;}.mission-writing-mate{width:70px;height:85px;margin:0;}.mission-model{grid-column:1/-1;}.mission-self-review{padding:18px;}.mission-self-review label{font-size:.85rem;}.mission-desk-door{grid-template-columns:85px minmax(0,1fr) 14px;gap:14px;padding:18px;}.mission-desk-door strong{font-size:1.25rem;}.mission-desk-door .mission-card-cast>svg{width:44px;height:62px;}.mission-desk-door .mission-card-cast{height:90px;}.mission-desk-door .mission-card-timeline i{width:26px;height:44px;}.mission-desk-door .mission-card-bridge svg{width:35px;height:35px;}}
@media(prefers-reduced-motion:reduce){.mission-old,.mission-card-letter,.mission-card-timeline i,.mission-card-cast>svg:nth-child(2){transform:none;}}

.mission-card[data-format="evidence"] .living-card-art{background:#f1e0ce;}.mission-card-evidence{display:flex;align-items:center;gap:16px;color:#684322;}.mission-card-evidence b{font:500 2.8rem/1 var(--serif);}.mission-card-evidence small{font:500 1.1rem var(--sans);}.mission-card-evidence i{display:grid;place-items:center;width:38px;height:38px;background:var(--paper);border:1px solid #9f7650;border-radius:50%;font:500 1.6rem var(--serif);}
.living-stage[data-format="evidence"]{--scene-wash:#f2e5d2;--scene-ink:#78512f;}.living-stage[data-format="evidence"] .mission-documents{border-top:1px solid var(--scene-ink);border-bottom:4px double var(--scene-ink);box-shadow:none;}.living-stage[data-format="evidence"] .mission-documents>section:first-child>p:first-of-type::first-letter{font-size:2.7rem;line-height:1;}
.mission-claims{display:grid;gap:20px;margin:24px 0;}.mission-claims fieldset{min-width:0;margin:0;padding:22px;border:1px solid var(--rule);border-radius:12px;background:var(--paper);}.mission-claims legend{font:500 1.4rem/1.5 var(--serif);max-width:95%;padding:0 8px;}.mission-claims label{display:flex;gap:12px;align-items:center;min-height:44px;font:.95rem/1.5 var(--sans);cursor:pointer;}.mission-claims input{width:22px;height:22px;flex-shrink:0;accent-color:var(--scene-ink);}.mission-claims p{font:.9rem/1.5 var(--sans);border-top:1px solid var(--rule);padding-top:12px;}.mission-claims+button:disabled{opacity:.45;}
.mission-portfolio{display:grid;gap:32px;}.mission-portfolio article{padding:28px;background:var(--paper);border:1px solid var(--rule);border-radius:12px;}.mission-portfolio h2{font:500 1.9rem/1.3 var(--serif);}.mission-portfolio blockquote{font:400 1.25rem/1.7 var(--serif);white-space:pre-wrap;margin:22px 0;border:0;padding:0;}.mission-portfolio details,.mission-previous{font:400 1rem/1.65 var(--serif);margin:20px 0;}.mission-portfolio summary,.mission-previous summary{min-height:44px;font:600 .85rem/1.6 var(--sans);cursor:pointer;}.mission-previous p{white-space:pre-wrap;padding:22px;background:var(--paper);border:1px solid var(--rule);}
@media(max-width:650px){.mission-card-evidence{display:grid;gap:10px;justify-items:center;}.mission-card-evidence b{font-size:1.8rem;}.mission-card-evidence small{font-size:.75rem;}.mission-card-evidence i{width:28px;height:28px;font-size:1.3rem;}.mission-claims fieldset{padding:18px;}.mission-claims legend{font-size:1.2rem;}.mission-claims label{font-size:.85rem;}.mission-portfolio article{padding:20px;}}

.living-stage [hidden]{display:none!important;}
.mission-claims input[type="radio"],.mission-claims input[type="radio"]:checked{margin:0;appearance:auto;-webkit-appearance:radio;background:none;border-radius:50%;}
.mission-listening{display:flex;gap:24px;align-items:center;justify-content:space-between;margin:24px 0;padding:20px;background:var(--paper);border:1px solid var(--rule);border-radius:12px;}.mission-listening strong{font:600 .9rem/1.5 var(--sans);}.mission-listening p{font:.8rem/1.65 var(--sans);margin:8px 0 0;max-width:58ch;}.mission-listening .btn{flex-shrink:0;max-width:45%;}
@media(max-width:650px){.mission-listening{display:block;}.mission-listening .btn{max-width:100%;margin-top:16px;}}


/* ===== 107-coherent-learning.css ===== */
/* Shared learning surfaces use the same paper, ink and action in every room. */
:root {
 --learning-surface: var(--galley);
 --learning-radius: 8px;
 --learning-control-radius: 5px;
 --learning-shadow: 0 3px 0 color-mix(in srgb,var(--rule) 65%,transparent);
}

/* Study overlays share the shell's solid ground in every theme. Their IDs
   establish ownership over older mood, grammar-grid and reward skins.
   Book colour remains on the page; reactions stay beside the answer. */
.gx-stage:is(#gx-stage, #living-stage) {
 background:var(--room)!important;
 box-shadow:none!important;
}
.gx-stage:is(#gx-stage, #living-stage)::before,
.gx-stage:is(#gx-stage, #living-stage)::after {
 content:none!important;display:none!important;animation:none!important;
}
.gx-stage:is(#gx-stage, #living-stage) :is(.pl,.atmos-stage,.gx-bloom) {
 display:none!important;
}

/* The passport owns its layout in every parent, including the course footer. */
#v-course .passport-strip{box-sizing:border-box;grid-column:1/-1;min-width:0;width:100%;max-width:1100px;grid-template-columns:54px minmax(0,1fr);grid-template-rows:auto auto;align-items:center;gap:6px 20px;padding:20px 24px;margin:24px auto;text-transform:none;letter-spacing:normal;}
#v-course .passport-strip-copy{grid-column:2;grid-row:1;min-width:0;gap:6px;overflow-wrap:anywhere;}
#v-course .passport-strip-copy .smallcaps{font:600 var(--type-meta)/1.5 var(--sans);letter-spacing:.09em;}
#v-course .passport-strip-copy>strong{font:650 1.25rem/1.35 var(--sans);}
#v-course .passport-strip-copy>span:last-child{font:var(--type-meta)/1.6 var(--sans);}
#v-course .passport-strip-emblem{grid-column:1;grid-row:1/3;}
#v-course .passport-strip-go{grid-column:2;grid-row:2;min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;font:600 var(--type-meta)/1.6 var(--sans);justify-self:start;margin-top:4px;}
#v-course .course-view-switch{display:flex;gap:6px;padding:5px;margin:0 0 24px;border:1px solid var(--rule);border-radius:16px;background:var(--paper);}
#v-course .course-view-switch button{flex:1;min-width:0;min-height:48px;padding:12px 18px;border:0;border-radius:11px;box-shadow:none;background:transparent;color:var(--carbon);font:650 var(--type-control)/1.4 var(--sans);text-transform:none;letter-spacing:0;}
#v-course .course-view-switch [aria-pressed="true"]{background:var(--pine);color:var(--on-pine);}
#v-course .course-view-switch button:focus-visible{outline:3px solid var(--signal);outline-offset:2px;}
#v-course [data-course-view="path"]>.course-explorer,#v-course [data-course-view="explore"]>[data-guided-path]{display:none;}
#v-course .level-panel[hidden],#v-course .level-browse-all[hidden]{display:none!important;}
#v-course .chapter-heading{grid-template-columns:minmax(0,1fr) 100px;gap:18px;border-radius:var(--learning-radius);padding:24px;margin-bottom:24px;}
#v-course .chapter-heading h2{font:500 var(--type-heading)/1.25 var(--serif);}
#v-course .chapter-heading p{max-width:55ch;font:var(--type-body)/1.65 var(--sans);}
#v-course .chapter-stops{display:grid;margin:0;padding:0;border:0;list-style:none;}
#v-course .chapter-stop{height:auto;min-height:130px;padding:0 0 24px;position:relative;isolation:isolate;}
#v-course .chapter-stop::before{content:"";display:block;position:absolute;left:44px;top:60px;bottom:-10px;width:0;height:auto;border:0;border-left:2px solid var(--rule);border-radius:0;background:none;z-index:-1;}
#v-course .chapter-stop:last-child::before{display:none;}
#v-course .chapter-stop.is-done::before{border-left-style:solid;border-color:var(--pine);}
#v-course .chapter-ink-route{display:none;}
#v-course .chapter-journey .chapter-stop .chapter-go{box-sizing:border-box;display:grid;grid-template-columns:54px minmax(0,1fr);gap:20px;align-items:center;width:100%;min-height:110px;margin:0;padding:20px;border:1px solid var(--rule);border-radius:var(--learning-radius);background:var(--paper);box-shadow:var(--learning-shadow);text-align:left;}
#v-course .chapter-journey .chapter-stop.is-current .chapter-go{border:2px solid var(--signal-ink);background:color-mix(in srgb,var(--signal) 9%,var(--learning-surface));padding-right:84px;}
#v-course .chapter-stop .chapter-book{width:48px;height:60px;outline:none;border-width:1px 1px 1px 5px;border-radius:4px 10px 10px 4px;font:600 1.2rem/1 var(--serif);box-shadow:3px 3px 0 #a7967020;}
#v-course .chapter-stop .chapter-label{min-width:0;padding:0;background:none;border-radius:0;overflow-wrap:anywhere;}
#v-course .chapter-stop .chapter-label strong{display:block;font:600 1.15rem/1.5 var(--serif);}
#v-course .chapter-stop .chapter-label small{display:block;font:600 var(--type-meta)/1.5 var(--sans);margin:0 0 4px;}
#v-course .chapter-stop .chapter-label em{display:block;font:500 var(--type-meta)/1.5 var(--sans);color:var(--pencil);margin-top:4px;}
#v-course .chapter-stop .chapter-label>span{display:block;font:var(--type-body)/1.6 var(--sans);margin-top:8px;}
#v-course .chapter-trail-friend{left:auto;right:18px;top:24px;width:48px;height:60px;}
#v-course .chapter-ending{max-width:none;margin:10px 0 24px;padding:24px;border-radius:var(--learning-radius);}
#v-course .chapter-later{margin:4px 0 22px;border-block:1px solid var(--rule);}
#v-course .chapter-later>summary{min-height:48px;padding:14px 4px;color:var(--steel);font:500 var(--type-control)/1.5 var(--sans);cursor:pointer;}
#v-course .chapter-later[open]>summary{margin-bottom:16px;}
#v-course .level-browse-all{margin-top:24px;}
#v-course .level-tab{min-width:0;overflow-wrap:anywhere;}
#v-course .level-tab strong{font:600 var(--type-body)/1.5 var(--serif);}
#v-course .level-caption{font:var(--type-meta)/1.6 var(--sans);}
@media(min-width:950px){
 #v-course .course-atlas{display:grid;grid-template-columns:230px minmax(0,1fr);gap:0 32px;}
 #v-course .course-atlas>.atlas-heading{grid-column:1/-1;grid-row:1;}
 #v-course .course-atlas>.passport-strip{grid-column:1/-1;grid-row:2;}
 #v-course .course-atlas>.level-rail{grid-column:1;grid-row:3;align-self:stretch;}
 #v-course .course-atlas>.level-panel{grid-column:2;grid-row:3;}
 #v-course .course-atlas>.level-caption{grid-column:1/-1;grid-row:4;}
 #v-course .course-atlas>.case-explorer{grid-column:1/-1;grid-row:5;}
 #v-course .course-atlas>.level-rail>.level-tabs{top:100px;position:sticky;}
}
@media(max-width:949px){
 #v-course .course-atlas{display:flex;flex-direction:column;}
 #v-course .course-atlas>*{width:100%;min-width:0;box-sizing:border-box;}
 #v-course .atlas-heading{order:0;}#v-course .passport-strip{order:1;}#v-course .level-rail{order:2;margin-bottom:20px;}#v-course .level-panel{order:3;}#v-course .level-caption{order:4;}#v-course .case-explorer{order:5;}
 #v-course .level-tabs{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));position:static;gap:8px;}
}
@media(max-width:560px){
 #v-course .passport-strip{padding:18px;gap:14px 16px;grid-template-columns:48px minmax(0,1fr);}
 #v-course .passport-strip-emblem{grid-row:1;}
 #v-course .passport-strip-go{grid-column:1/-1;grid-row:2;margin-top:0;}
 #v-course .passport-strip-copy>strong{font-size:1.1rem;}#v-course .passport-strip-copy>span:last-child{font-size:var(--type-meta);}
 #v-course .course-view-switch button{padding:12px 8px;font-size:var(--type-control);}
 #v-course .chapter-heading{grid-template-columns:minmax(0,1fr) 64px;padding:18px;gap:12px;}
 #v-course .chapter-heading h2{font-size:1.65rem;}#v-course .chapter-heading p{font-size:var(--type-body);}
 #v-course .chapter-journey .chapter-stop .chapter-go{grid-template-columns:38px minmax(0,1fr);padding:16px;gap:14px;}
 #v-course .chapter-journey .chapter-stop.is-current .chapter-go{padding-right:52px;}
 #v-course .chapter-stop .chapter-book{width:34px;height:46px;font-size:var(--type-body);}
 #v-course .chapter-stop::before{left:34px;}
 #v-course .chapter-stop .chapter-label strong{font-size:1.05rem;}
 #v-course .chapter-trail-friend{right:8px;top:20px;width:34px;height:44px;}
}
@media(prefers-reduced-motion:reduce){#v-course .chapter-trail-friend{animation:none;}#v-course .chapter-go{transition:none!important;transform:none!important;}}
body.motion-low #v-course .chapter-trail-friend{animation:none;}
.study-rhythm{margin:18px 0 24px;padding:0 20px;border:1px solid var(--rule);border-radius:12px;background:var(--paper);color:var(--carbon);text-align:left;}.study-rhythm>summary{min-height:48px;padding:14px 0;font:600 var(--type-control)/1.6 var(--sans);cursor:pointer;overflow-wrap:anywhere;}.study-rhythm>summary>span{font-weight:400;color:var(--pencil);margin-left:14px;}.study-rhythm form{padding:4px 0 20px;max-width:720px;}.study-rhythm form>p{font:var(--type-body)/1.6 var(--sans);}.study-rhythm form>label{display:block;margin-bottom:8px;font:600 var(--type-meta)/1.5 var(--sans);}.study-rhythm input[type="text"]{box-sizing:border-box;width:100%;min-height:46px;max-width:100%;padding:10px 12px;font:400 var(--type-control)/1.5 var(--sans);border:1px solid var(--rule);border-radius:7px;background:var(--paper);color:var(--carbon);}.study-rhythm fieldset{margin:20px 0;border:1px solid var(--rule);border-radius:8px;padding:12px 16px;display:flex;flex-wrap:wrap;gap:12px 24px;min-width:0;}.study-rhythm legend{font:600 var(--type-meta)/1.6 var(--sans);padding:0 6px;}.study-rhythm fieldset label{display:flex;align-items:center;gap:12px;min-height:44px;font:var(--type-body)/1.5 var(--sans);cursor:pointer;}.study-rhythm input[type="radio"],.study-rhythm input[type="radio"]:checked{width:22px;height:22px;margin:0;appearance:auto;-webkit-appearance:radio;background:none;accent-color:var(--pine);}.study-rhythm .rhythm-note,.study-rhythm .rhythm-status{font:var(--type-meta)/1.6 var(--sans);}.study-rhythm input:focus-visible,.study-rhythm summary:focus-visible{outline:3px solid var(--signal);outline-offset:3px;}.rhythm-finish{max-width:60ch;margin:22px auto;font:var(--type-body)/1.65 var(--sans);}.study-rhythm .rhythm-status:empty{display:none;}
@media(max-width:560px){.study-rhythm{padding:0 16px;}.study-rhythm>summary>span{display:block;margin:4px 0 0;}.study-rhythm fieldset{display:block;}}

#v-course .course-progress{box-sizing:border-box;max-width:1100px;margin:24px auto;}
#v-course .course-progress>summary{overflow-wrap:anywhere;}
#v-course .passport-copy{min-width:0;overflow-wrap:anywhere;}
@media(max-width:560px){#v-course .course-progress>summary>span{display:block;margin:6px 0 0;}}

/* A clear current stop, with quiet feedback instead of looping motion. */
#v-course .chapter-stop.is-current{scroll-margin-top:100px;}
#v-course .chapter-stop .chapter-go{transition:transform .16s ease,box-shadow .16s ease;}
#v-course .chapter-stop .chapter-go:hover{transform:translateY(-2px);box-shadow:0 5px 12px #25352e14;}
#v-course .chapter-stop .chapter-go:active{transform:translateY(1px);}
@media(prefers-reduced-motion:reduce){
 #v-course .chapter-stop .chapter-go{transition:none;}
 #v-course .chapter-stop .chapter-go:hover,#v-course .chapter-stop .chapter-go:active{transform:none;}
}
body:not(.motion-full) #v-course .chapter-stop .chapter-go{transition:none;transform:none;}

/* The continuation is an open volume: purpose on the left, a real line on
   the facing page. The primary action precedes the sample on small screens. */
#v-course .course-continuation {
 display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,1fr);gap:0;
 max-width:1100px;margin:12px auto 24px;padding:0;
 border:1px solid var(--rule);border-radius:var(--learning-radius);
 background:var(--learning-surface);box-shadow:var(--learning-shadow);color:var(--carbon);
 overflow:hidden;isolation:isolate;
}
#v-course .course-volume-heading { grid-column:1/-1;display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px 20px;padding:15px clamp(22px,3vw,36px);border-bottom:1px solid var(--rule);font:600 var(--type-meta)/1.5 var(--sans);letter-spacing:.08em;text-transform:uppercase;color:var(--steel); }
#v-course .course-volume-heading>span:last-child { color:var(--steel);font-weight:400;letter-spacing:.035em; }
#v-course .course-continuation-copy { min-width:0;padding:clamp(26px,4vw,44px) clamp(22px,3vw,36px) 30px; }
#v-course .course-continuation-copy>.smallcaps { color:var(--steel);font:600 var(--type-meta)/1.5 var(--sans);letter-spacing:.1em; }
#v-course .course-continuation h2 { margin:12px 0 16px;font:500 clamp(2rem,3.5vw,3rem)/1.1 var(--serif);text-transform:none;letter-spacing:-.025em;overflow-wrap:anywhere; }
#v-course .course-continuation-copy>p { max-width:42ch;margin:0 0 24px;font:var(--type-body)/1.65 var(--sans);color:var(--steel); }
#v-course .course-volume-progress { max-width:360px;margin:22px 0 24px; }
#v-course .course-volume-progress>div { display:flex;justify-content:space-between;gap:12px;margin-bottom:10px;font:var(--type-meta)/1.5 var(--sans);color:var(--steel); }
#v-course .course-volume-progress progress { appearance:none;display:block;width:100%;height:5px;border:0;border-radius:0;background:var(--rule);color:var(--pine); }
#v-course .course-volume-progress progress::-webkit-progress-bar { background:var(--rule); }
#v-course .course-volume-progress progress::-webkit-progress-value { background:var(--pine); }
#v-course .course-volume-progress progress::-moz-progress-bar { background:var(--pine); }
#v-course .course-continuation-copy>.course-action-note { margin:14px 0 0;font:var(--type-meta)/1.55 var(--sans);color:var(--steel); }
#v-course .course-facing-page { position:relative;display:flex;flex-direction:column;align-items:flex-start;min-width:0;margin:0;padding:clamp(26px,4vw,44px) clamp(22px,3vw,36px) 20px;border-left:1px solid var(--rule);background:linear-gradient(90deg,color-mix(in srgb,var(--rule) 28%,transparent),transparent 16px),var(--sheet); }
#v-course .course-facing-page figcaption { max-width:100%;color:var(--steel);font:500 var(--type-meta)/1.6 var(--sans);overflow-wrap:anywhere; }
#v-course .course-facing-page blockquote { position:relative;z-index:1;width:100%;box-sizing:border-box;margin:22px 0 16px;padding:0;border:0;background:none;color:var(--pine);font:500 var(--type-prompt)/1.5 var(--serif);letter-spacing:-.015em;overflow-wrap:anywhere; }
#v-course .course-facing-page .course-preview-letters { letter-spacing:.08em; }
#v-course .course-preview-meaning { position:relative;z-index:1;width:100%;font:var(--type-body)/1.65 var(--sans); }
#v-course .course-preview-meaning>summary { display:inline-flex;align-items:center;min-height:44px;cursor:pointer;color:var(--steel);text-decoration:underline;text-decoration-style:dotted;text-underline-offset:5px; }
#v-course .course-preview-meaning>summary::before { content:"+";margin-right:8px;font-size:1.1rem; }
#v-course .course-preview-meaning[open]>summary::before { content:"−"; }
#v-course .course-preview-meaning>p { max-width:42ch;margin:2px 0 14px;color:var(--carbon); }
#v-course .course-resident-print { display:block;align-self:flex-end;width:clamp(100px,13vw,150px);height:100px;flex-shrink:0;margin:12px 0 0;opacity:.9; }
#v-course .course-resident-print>svg { width:100%;height:100%;display:block; }
#v-course .course-facing-page.is-quiet { justify-content:center;align-items:center;gap:16px; }
#v-course .course-facing-page.is-quiet .course-resident-print { align-self:center;width:190px;height:170px; }
#v-course .course-preview-source { align-self:flex-start;margin:auto 0 0;font:var(--type-meta)/1.5 var(--sans);color:var(--steel); }
#v-course .course-context-actions { grid-column:1/-1;display:flex;flex-wrap:wrap;align-items:center;gap:12px 24px;min-width:0;padding:16px clamp(22px,3vw,36px);border-top:1px solid var(--rule); }
#v-course .course-context-actions>div { flex:1 1 220px;min-width:0; }
#v-course .course-context-actions .smallcaps { display:block;margin-bottom:4px;color:var(--steel);font:500 var(--type-meta)/1.5 var(--sans);letter-spacing:.08em; }
#v-course .course-context-actions strong { display:block;font:500 1.15rem/1.4 var(--serif);overflow-wrap:anywhere; }
#v-course .course-context-actions .linklike { min-height:44px;padding:8px 0;border:0;background:transparent;color:var(--carbon);box-shadow:none;font:500 var(--type-control)/1.5 var(--sans);text-transform:none;letter-spacing:0;text-decoration:underline;text-underline-offset:4px; }
#v-course .course-catalog { max-width:1100px;margin:0 auto 28px;border-block:1px solid var(--rule); }
#v-course .course-catalog>summary { min-height:52px;padding:18px 4px;cursor:pointer;font:600 var(--type-control)/1.5 var(--sans);color:var(--carbon); }
#v-course .course-catalog>summary>span { display:block;margin:5px 0 0 18px;font:400 var(--type-control)/1.6 var(--sans);color:var(--steel); }
#v-course .course-catalog[open]>summary { margin-bottom:20px;border-bottom:1px solid var(--rule); }
#v-course .atlas-heading p { display:block;max-width:65ch;margin:10px 0 0;font:var(--type-body)/1.65 var(--sans);color:var(--steel); }
/* The study-level choice belongs to both course views, at every width. */
#v-course .level-intro { display:block;margin-bottom:16px;padding:16px 20px;border-radius:var(--learning-radius);background:var(--learning-surface); }
#v-course .level-intro>div { display:flex;flex-wrap:wrap;align-items:center;gap:8px 16px;min-width:0; }
#v-course .level-intro .smallcaps,#v-course .level-intro .level-outline { flex-basis:100%; }
#v-course .level-intro h2 { flex:1 1 180px;margin:0;font:500 1.25rem/1.4 var(--serif);overflow-wrap:anywhere; }
#v-course .level-intro [data-study-band] { flex:0 1 auto;margin:0; }
#v-course .level-intro-art { display:none; }
#v-course [data-study-band] { margin:10px 0; }
#v-course [data-study-band][aria-pressed="true"] { background:color-mix(in srgb,var(--signal) 15%,var(--learning-surface));border-color:var(--signal-ink);color:var(--carbon); }
#v-course .course-access-boundary { margin:0 0 22px;padding:14px 18px;border-left:3px solid var(--signal);background:var(--learning-surface);font:var(--type-body)/1.65 var(--sans);color:var(--carbon); }

/* A source survives changing rooms. Keep the return a quiet, usable control. */
.learning-source { display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:4px 20px;max-width:1100px;margin:16px auto;padding:12px 0;border-block:1px solid var(--rule);color:var(--steel);font:var(--type-meta)/1.6 var(--sans); }
.learning-source>span { flex:1 1 240px;min-width:0;overflow-wrap:anywhere; }
.learning-source button { min-height:44px;max-width:100%;padding:8px 0;border:0;border-radius:4px;background:transparent;color:var(--carbon);box-shadow:none;font:600 var(--type-control)/1.5 var(--sans);text-transform:none;letter-spacing:0;white-space:normal;text-align:left;cursor:pointer; }
.learning-source button:hover { text-decoration:underline;text-underline-offset:4px; }
:is(#v-library,#v-words)>.study-orientation { display:block;box-sizing:border-box;max-width:1100px;margin:16px auto 28px;padding:20px 24px;border:1px solid var(--rule);border-left:3px solid var(--signal);border-radius:var(--learning-radius);background:var(--learning-surface);box-shadow:var(--learning-shadow); }
:is(#v-library,#v-words)>.study-orientation[hidden] { display:none!important; }
.study-orientation .study-suggestion { display:flex;align-items:center;justify-content:space-between;gap:20px 32px;padding:0; }
.study-orientation .study-suggestion>div,.study-suggestion-copy { min-width:0; }
.study-orientation .study-suggestion strong { margin:6px 0;font:500 var(--type-heading)/1.35 var(--serif);overflow-wrap:anywhere; }
.study-orientation .study-suggestion div>span:not(.smallcaps) { font:var(--type-meta)/1.5 var(--sans);color:var(--steel);overflow-wrap:anywhere; }
.study-orientation .study-suggestion p { max-width:65ch;margin:8px 0 0;font:var(--type-body)/1.6 var(--sans);color:var(--steel); }
.study-orientation .study-suggestion>.btn { flex-shrink:0;max-width:100%; }
.study-orientation .learning-source { margin:18px 0 0;padding:10px 0 0;border-bottom:0; }

/* All three voluntary paths stay visible without competing with the next step. */
.desk-paths { display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin:18px 0 24px; }
.desk-paths>button { display:flex;flex-direction:column;gap:6px;min-width:0;min-height:88px;padding:16px 18px;border:1px solid var(--rule);border-radius:var(--learning-control-radius);background:var(--learning-surface);color:var(--carbon);box-shadow:none;text-align:left;font:inherit;text-transform:none;letter-spacing:0;cursor:pointer; }
.desk-paths>button:hover { border-color:var(--pine);background:color-mix(in srgb,var(--pine) 4%,var(--learning-surface)); }
.desk-paths strong { font:600 var(--type-body)/1.5 var(--sans); }
.desk-paths span { font:var(--type-meta)/1.5 var(--sans);color:var(--steel);overflow-wrap:anywhere; }

/* Mixed practice and saved words are peers, with one active mode at a time. */
#v-review .practice-modes { display:flex;gap:6px;max-width:980px;margin:8px auto 18px;padding:5px;border:1px solid var(--rule);border-radius:14px;background:var(--learning-surface); }
#v-review .practice-modes button { flex:1;min-width:0;min-height:46px;padding:10px 14px;border:0;border-radius:var(--learning-control-radius);background:transparent;color:var(--carbon);box-shadow:none;font:600 var(--type-control)/1.5 var(--sans);text-transform:none;letter-spacing:0;white-space:normal;cursor:pointer; }
#v-review .practice-modes button[aria-pressed="true"] { background:var(--pine);color:var(--on-pine); }
#v-review .daily-invitation,#v-review .daily-task { border-radius:var(--learning-radius);background:var(--learning-surface);box-shadow:var(--learning-shadow); }
#v-review .daily-invitation { padding:28px;gap:24px; }
#v-review .daily-copy { min-width:0; }
#v-review .daily-copy>p { display:block;color:var(--steel); }
#v-review .daily-personal { display:block;padding:0;text-align:left;color:var(--steel); }
#v-review .daily-sound { border-radius:var(--learning-control-radius); }
#v-review .daily-landscape { max-width:280px;justify-self:center; }
#v-review .daily-completion-summary { grid-template-columns:minmax(0,1fr) 160px;gap:20px 28px;padding:28px;border:1px solid var(--rule);border-radius:var(--learning-radius);background:var(--learning-surface);box-shadow:var(--learning-shadow); }
#v-review .daily-completion-summary>.daily-flow-copy { grid-column:1;grid-row:1; }
#v-review .daily-completion-summary>.daily-flow-picture { grid-column:2;grid-row:1;max-width:160px;padding:12px;align-self:start;border-radius:var(--learning-radius);background:color-mix(in srgb,var(--signal) 9%,var(--learning-surface)); }
#v-review .daily-completion-summary .daily-flow-copy h2 { margin:12px 0 16px; }
#v-review .daily-completion-summary>.daily-finish-actions { grid-column:1/-1;justify-content:flex-start;align-items:center;gap:12px;padding:0; }
#v-review .daily-completion-summary>.daily-source { grid-column:1/-1;margin:0;font-style:normal; }
#v-review .daily-completion-summary>.daily-more,#v-review .daily-completion-summary>.daily-album { grid-column:1/-1;margin:0;padding-top:12px;border-top:1px solid var(--rule);font:var(--type-control)/1.6 var(--sans); }
#v-review .daily-more>summary,#v-review .daily-round-notes>summary { min-height:44px;padding:10px 0;font:600 var(--type-control)/1.6 var(--sans);cursor:pointer; }
#v-review .daily-more>small { display:block;margin:8px 0;color:var(--steel); }

/* Stable controls and one shared gold primary; no room-specific green CTA. */
:is(.course-continuation,.study-suggestion) .btn,#v-course [data-study-band],#v-review .daily-adventure .btn { box-sizing:border-box;min-height:48px;max-width:100%;padding:12px 20px;border-radius:var(--learning-control-radius);font:600 var(--type-control)/1.5 var(--sans);text-transform:none;letter-spacing:0;white-space:normal;overflow-wrap:anywhere; }
:is(.course-continuation,.study-suggestion) .btn.primary,#v-review .daily-adventure .btn.primary { background:var(--signal);border:1px solid var(--signal-ink);color:var(--on-signal);box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent); }
:is(.course-continuation,.study-suggestion) .btn.primary:hover,#v-review .daily-adventure .btn.primary:hover { background:color-mix(in srgb,var(--signal) 90%,var(--sheet)); }
:is(.course-continuation,.study-suggestion) .btn.primary:active,#v-review .daily-adventure .btn.primary:active { transform:none;box-shadow:0 1px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent); }
#v-course .course-continuation .btn:not(.primary) { background:transparent;border:1px solid var(--rule);color:var(--carbon);box-shadow:none; }
:is(.course-continuation,.course-catalog,.study-orientation,.learning-source,.practice-modes,.desk-paths) :focus-visible,#v-review .daily-adventure :focus-visible { outline:3px solid var(--signal-ink);outline-offset:4px; }
:is(.course-catalog,.study-orientation,.practice-modes,.desk-paths) button { scroll-margin-top:110px; }

@media(max-width:700px) {
 #v-course .course-continuation { grid-template-columns:minmax(0,1fr);gap:0; }
 #v-course .course-facing-page { border-left:0;border-top:1px solid var(--rule);padding:22px;background:var(--sheet); }
 #v-course .course-facing-page blockquote { max-width:26ch;margin:12px 0 6px;font-size:var(--type-prompt); }
 #v-course .course-resident-print { width:90px;height:68px;margin:-8px 0 0; }
 #v-course .course-facing-page.is-quiet { display:none; }
 #v-course .course-context-actions { padding:16px 22px;gap:4px 20px; }
 .study-orientation .study-suggestion { align-items:flex-start;flex-direction:column;gap:18px; }
 #v-review .daily-invitation { grid-template-columns:minmax(0,1fr);gap:12px; }
 #v-review .daily-invitation>.daily-copy { grid-row:1; }
 #v-review .daily-invitation>.daily-landscape { grid-row:2;width:130px;max-height:100px;margin:0;overflow:hidden; }
 #v-review .daily-completion-summary { grid-template-columns:minmax(0,1fr);padding:22px; }
 #v-review .daily-completion-summary>.daily-flow-picture { display:none; }
}
@media(max-width:560px) {
 #v-course .course-continuation { padding:0;margin-top:8px; }
 #v-course .course-volume-heading { padding:12px 20px;font-size:var(--type-meta);gap:5px 12px; }
 #v-course .course-continuation-copy { padding:24px 20px; }
 #v-course .course-continuation h2 { margin:9px 0 12px;font-size:2rem; }
 #v-course .course-continuation-copy>p { margin-bottom:18px;font-size:var(--type-body); }
 #v-course .course-volume-progress { margin:18px 0; }
 #v-course .course-continuation .btn { width:100%; }
 #v-course .course-catalog>summary>span { margin-left:0; }
 :is(#v-library,#v-words)>.study-orientation { padding:18px;margin:12px auto 20px; }
 .study-orientation .study-suggestion>.btn { width:100%;margin:0; }
 .learning-source { align-items:flex-start;gap:0; }
 .learning-source>span { flex-basis:100%; }
 .desk-paths { gap:8px; }
 .desk-paths>button { padding:12px 10px;min-height:82px; }
 .desk-paths strong { font-size:var(--type-body); }
 .desk-paths span { font-size:var(--type-meta); }
 #v-review .practice-modes button { padding:10px 8px;font-size:var(--type-control); }
 #v-review .daily-invitation,#v-review .daily-task { padding:20px; }
 #v-review .daily-completion-summary>.daily-finish-actions { align-items:stretch;flex-direction:column; }
 #v-review .daily-completion-summary>.daily-finish-actions>.btn { width:100%; }
}
@media(prefers-reduced-motion:reduce) {
 :is(.course-continuation,.study-orientation,.practice-modes,.desk-paths) *,#v-review .daily-adventure * { animation:none!important;transition:none!important;scroll-behavior:auto!important; }
 :is(.course-continuation,.study-orientation,.practice-modes,.desk-paths) button,#v-review .daily-adventure .btn { transform:none!important; }
}
body.motion-low :is(.course-continuation,.study-orientation,.practice-modes,.desk-paths) *,body.motion-low #v-review .daily-adventure * { animation:none!important;transition:none!important; }

/* Saved words carry a real encounter address when one was recorded. */
.word-source{display:block;border:0;background:none;padding:5px 0 0;color:var(--ink);font:inherit;font-size:var(--type-meta);text-align:left;text-decoration:underline;text-underline-offset:3px;cursor:pointer}
.word-source:focus-visible{outline:2px solid var(--ink);outline-offset:3px}

/* The scheduled-word engine shares the same surfaces and completion grammar. */
#v-review .practice-invitation,#v-review #rev-card { border:1px solid var(--rule);border-radius:var(--learning-radius);background:var(--learning-surface);box-shadow:var(--learning-shadow); }
#v-review .practice-start-actions .btn,#v-review #rev-btns button { border-radius:var(--learning-control-radius);font:600 var(--type-control)/1.5 var(--sans);text-transform:none;letter-spacing:0; }
#v-review .practice-start-actions .btn.primary,#v-review #rev-btns .btn.primary { background:var(--signal);border:1px solid var(--signal-ink);color:var(--on-signal);box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent); }
#v-review.practice-finished #rev-card { padding:24px; }
#v-review.practice-finished #rev-card>.cloze { border:0;border-radius:0;background:none;padding:0;margin:16px 0;font:500 1.7rem/1.3 var(--serif);box-shadow:none; }
#v-review.practice-finished #rev-card>p.daily-completion-summary { display:block;border:0;border-radius:0;background:none;padding:0;margin:12px auto 24px;box-shadow:none;font:var(--type-body)/1.6 var(--sans);color:var(--steel); }
#v-review.practice-finished #rev-btns { display:flex;flex-wrap:wrap;gap:12px;background:var(--learning-surface);border:0;padding:18px;border-radius:var(--learning-radius);box-shadow:var(--learning-shadow); }
#v-review.practice-finished #rev-btns>.btn:not(.primary) { background:transparent;color:var(--carbon);border:1px solid var(--rule);box-shadow:none; }
#v-review .practice-round-details { padding:0 20px;border-bottom:1px solid var(--rule); }
#v-review .practice-round-details>summary { min-height:44px;padding:12px 0;font:var(--type-control)/1.5 var(--sans);cursor:pointer; }
@media(max-width:560px) { #v-review.practice-finished #rev-btns>.primary { flex:1 1 100%; } #v-review.practice-finished #rev-btns>.btn:not(.primary) { flex:1 1 auto; } }

/* Progress belongs to the page; earned feedback belongs beside the answer.
   Neither surface floats over an exercise or occupies the navigation layer. */
#practice-rewards { position:static;max-width:780px;margin:12px auto 20px;scroll-margin-top:90px; }
#practice-rewards[hidden] { display:none!important; }
.practice-progress { padding:14px 20px 10px;border:1px solid var(--rule);border-radius:16px;background:var(--paper);color:var(--carbon);box-shadow:0 3px 12px #1427470a;font:400 var(--type-meta)/1.45 var(--sans); }
.pp-heading { display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px; }
.pp-level { min-height:44px;display:inline-flex;align-items:center;gap:10px;padding:0;border:0;background:none;color:var(--carbon);font:600 var(--type-control)/1.35 var(--sans);text-align:left;cursor:pointer;box-shadow:none;text-transform:none;letter-spacing:0; }
.pp-level small { display:block;font-size:var(--type-meta);font-weight:400;color:var(--steel);margin-top:2px; }
.pp-level-mark { display:grid;place-items:center;min-width:36px;height:36px;border:1px solid var(--signal-ink);border-radius:12px;background:var(--signal);color:var(--carbon);font:700 var(--type-body) var(--sans); }
.pp-streak { font-size:var(--type-meta);text-align:right;color:var(--steel); }
.pp-streak>span { color:var(--signal-ink); }
.pp-meter { height:7px;overflow:hidden;border-radius:20px;background:color-mix(in srgb,var(--pine) 10%,var(--paper)); }
.pp-meter>span { display:block;height:100%;width:100%;transform-origin:left;background:linear-gradient(90deg,var(--pine),var(--signal-ink));border-radius:inherit; }
.pp-numbers,.pp-session { display:flex;justify-content:space-between;gap:12px;margin-top:5px;font-size:var(--type-meta);color:var(--steel); }
.pp-session strong { color:var(--pine); }
:is(#practice-rewards,.course-rank-progress) .practice-progress { display:grid;grid-template-columns:auto minmax(100px,1fr) auto;column-gap:24px;padding:12px 0;border:0;border-bottom:1px solid var(--rule);border-radius:0;background:transparent;box-shadow:none; }
:is(#practice-rewards,.course-rank-progress) .pp-heading { display:contents; }
:is(#practice-rewards,.course-rank-progress) .pp-level { grid-column:1;grid-row:1/3; }
:is(#practice-rewards,.course-rank-progress) .pp-streak { grid-column:3;grid-row:1/3;align-self:center; }
:is(#practice-rewards,.course-rank-progress) .pp-meter { grid-column:2;grid-row:1;align-self:end;height:5px; }
:is(#practice-rewards,.course-rank-progress) .pp-numbers { grid-column:2;grid-row:2;align-self:start; }
:is(#practice-rewards,.course-rank-progress) .pp-session { grid-column:1/-1;margin-top:10px; }
.pp-receipt { display:grid;grid-template-columns:32px minmax(0,1fr) 108px;align-items:center;gap:12px;padding:14px 16px;border:1px solid color-mix(in srgb,var(--pine) 16%,var(--rule));border-radius:12px;background:color-mix(in srgb,var(--pine) 5%,var(--paper));color:var(--carbon);font:var(--type-meta)/1.45 var(--sans); }
.pp-receipt.is-milestone { background:color-mix(in srgb,var(--signal) 12%,var(--paper));border-color:color-mix(in srgb,var(--signal-ink) 40%,var(--rule)); }
.pp-receipt-mark { display:grid;place-items:center;width:32px;height:32px;border-radius:50%;background:var(--paper);color:var(--pine);font:600 1.1rem var(--sans); }
.is-milestone .pp-receipt-mark { background:var(--signal);color:var(--on-signal); }
.pp-receipt-copy { min-width:0;overflow-wrap:anywhere; }
.pp-receipt-copy>strong,.pp-receipt-copy>span { display:block; }
.pp-receipt-copy>span { font-size:var(--type-meta);color:var(--steel);margin-top:3px; }
.pp-receipt-progress { font-size:var(--type-meta);color:var(--steel); }
.pp-receipt-progress .pp-meter { height:4px;margin:5px 0; }
.pp-receipt-progress small { font:inherit; }
.pp-receipt.is-live { animation:practice-reward-arrive 240ms ease-out both; }
.pp-receipt.is-live .pp-meter>span { animation:practice-reward-fill 420ms ease-out both; }
.pp-receipt.is-live.is-milestone .pp-receipt-mark { animation:practice-reward-mark 480ms ease-out both; }
#practice-answer-reward { margin-top:12px; }
#practice-answer-reward[hidden] { display:none!important; }
.daily-feedback>.pp-receipt { margin:0 0 18px;padding:0 0 14px;border:0;border-bottom:1px solid var(--rule);border-radius:0;background:transparent; }
#v-review .daily-task-head { min-height:40px; }
#v-review .daily-task-head .daily-mate { width:38px;height:40px;overflow:hidden;contain:paint;pointer-events:none; }
.practice-answer-glow { animation:practice-answer-glow 550ms ease-out; }
.pp-level:focus-visible,.pp-rules>summary:focus-visible { outline:3px solid var(--signal-ink);outline-offset:4px; }
.practice-progress.is-profile { padding:24px;margin-bottom:24px; }
.is-profile .pp-level { cursor:default;font-size:1.1rem; }
.is-profile .pp-level-mark { width:46px;height:46px;font-size:1.3rem; }
.is-profile .pp-meter { margin-top:18px;height:10px; }
.pp-explainer { margin:16px 0;color:var(--steel);font-size:var(--type-meta); }
.pp-milestones { display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px; }
.pp-badge { display:flex;flex-direction:column;gap:7px;padding:16px 12px;border:1px solid var(--rule);border-radius:12px;background:var(--paper);min-width:0; }
.pp-badge>span:first-child { font:1.5rem var(--serif);color:var(--steel); }
.pp-badge strong { font-size:var(--type-meta); }
.pp-badge small { color:var(--steel);font-size:var(--type-meta);flex:1; }
.pp-badge>span:last-child { font-size:var(--type-meta);color:var(--steel); }
.pp-badge.is-earned { border-color:color-mix(in srgb,var(--signal-ink) 50%,var(--rule));background:color-mix(in srgb,var(--signal) 8%,var(--paper)); }
.pp-badge.is-earned>span:first-child { color:var(--signal-ink); }
.pp-rules { margin-top:18px;color:var(--steel);font-size:var(--type-meta); }
.pp-rules>summary { cursor:pointer;min-height:36px;display:flex;align-items:center;text-decoration:underline;text-underline-offset:3px; }
.pp-rules p { max-width:70ch;line-height:1.7; }
@keyframes practice-reward-arrive { from { opacity:0;transform:translateY(4px); } to { opacity:1;transform:translateY(0); } }
@keyframes practice-reward-fill { from { transform:scaleX(var(--pp-from,0)); } to { transform:scaleX(var(--pp-to,0)); } }
@keyframes practice-reward-mark { 40% { transform:scale(1.12) rotate(-8deg); } }
@keyframes practice-answer-glow { 35% { box-shadow:0 0 0 3px color-mix(in srgb,var(--signal) 45%,transparent); } }
@media(max-width:600px) {
  #practice-rewards { margin:8px auto 12px;scroll-margin-top:118px; }
  :is(#practice-rewards,.course-rank-progress) .practice-progress { grid-template-columns:minmax(0,1fr) auto;column-gap:12px; }
  :is(#practice-rewards,.course-rank-progress) .pp-level { grid-column:1;grid-row:1; }
  :is(#practice-rewards,.course-rank-progress) .pp-streak { grid-column:2;grid-row:1; }
  :is(#practice-rewards,.course-rank-progress) .pp-meter { grid-column:1/-1;grid-row:2;margin-top:10px; }
  :is(#practice-rewards,.course-rank-progress) .pp-numbers { grid-column:1/-1;grid-row:3; }
  .practice-progress { padding:10px 12px 9px;border-radius:12px; }
  .pp-heading { gap:8px;margin-bottom:7px; }
  .pp-level { font-size:var(--type-control);gap:7px; }
  .pp-level-mark { min-width:30px;height:30px;border-radius:9px; }
  .pp-streak { max-width:115px;font-size:var(--type-meta); }
  .pp-receipt { grid-template-columns:28px minmax(0,1fr);gap:9px;padding:12px; }
  .pp-receipt-mark { width:28px;height:28px; }
  .pp-receipt-progress { grid-column:2;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:3px 10px;width:100%; }
  .pp-receipt-progress .pp-meter { grid-column:1/-1;grid-row:2;margin:0; }
  .practice-progress.is-profile { padding:18px; }
  .pp-milestones { grid-template-columns:repeat(2,minmax(0,1fr)); }
}
@media(prefers-reduced-motion:reduce) {
  .pp-receipt,.pp-receipt *,.practice-answer-glow { animation:none!important; }
}
body.motion-low .pp-receipt,body.motion-low .pp-receipt * { animation:none!important; }

/* The room's resident and original class insignia are one identity everywhere. */
.rank-identity { display:block;width:100%;height:100%;overflow:visible; }
.pp-level-mark,.is-profile .pp-level-mark { flex:none;width:48px;min-width:48px;height:53px;background:none;border:0;border-radius:0; }
.pp-level>span:last-child { min-width:0;max-width:26ch;overflow-wrap:anywhere; }
.pp-level { font-family:var(--serif);font-size:var(--type-control); }
.pp-level small { font-family:var(--sans); }
.is-profile .pp-level-mark { width:76px;height:84px; }
.is-profile .pp-level { font-size:1.35rem; }
.pp-receipt-mark,.is-milestone .pp-receipt-mark { width:32px;height:36px;border-radius:0;background:none; }
.pp-milestone-heading { font:600 var(--type-meta)/1.5 var(--sans);margin:20px 0 12px; }
.pp-rank-ladder { margin:14px 0;border-top:1px solid var(--rule);border-bottom:1px solid var(--rule); }
.pp-rank-ladder>summary { cursor:pointer;padding:14px 0;font:600 var(--type-control)/1.5 var(--sans); }
.pp-rank-ladder ol { list-style:none;padding:0;margin:0 0 12px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px; }
.pp-rank-ladder li { display:flex;align-items:center;gap:10px;padding:10px;border:1px solid var(--rule);border-radius:10px;min-width:0; }
.pp-rank-ladder li>span:first-child { width:36px;height:40px;flex:none; }
.pp-rank-ladder li>span:nth-child(2) { flex:1;min-width:0; }
.pp-rank-ladder strong,.pp-rank-ladder small { display:block;overflow-wrap:anywhere; }
.pp-rank-ladder strong { font:var(--type-meta)/1.4 var(--serif); }
.pp-rank-ladder small { color:var(--steel);font:var(--type-meta)/1.4 var(--sans); }
.pp-rank-ladder .is-current { border-color:var(--signal-ink);background:color-mix(in srgb,var(--signal) 10%,var(--paper)); }
#v-course .passport-strip-emblem .rank-identity { width:54px;height:60px; }
.passport-emblem .rank-identity { width:70px;height:77px; }
@media(max-width:600px) {
 .pp-level-mark { width:40px;min-width:40px;height:44px; }
 :is(#practice-rewards,.course-rank-progress) .pp-level { font-size:var(--type-control); }
 .pp-rank-ladder ol { grid-template-columns:minmax(0,1fr); }
}

.course-rank-progress { max-width:1100px;margin:8px auto 28px; }


/* ===== 108-story-objects.css ===== */
.scene-word-stage{position:fixed;inset:0;z-index:2200;overflow-y:auto;background:#f6f1e7;color:#24364b;padding:24px;box-sizing:border-box;font-family:var(--sans);overscroll-behavior:contain;}
.object-sheet{max-width:1320px;margin:0 auto;}
.object-top{display:flex;align-items:center;justify-content:space-between;gap:20px;border-bottom:1px solid #cfc5b5;padding-bottom:18px;}
.object-top>span{display:flex;align-items:center;gap:12px;font:600 .85rem/1.5 var(--sans);}
.object-top .ic{width:24px;height:24px;}
.object-top .btn{min-height:44px;font-size:.8rem;flex-shrink:0;}
.object-heading{margin:28px 0;max-width:850px;}
.object-heading .smallcaps{font:650 .7rem/1.5 var(--sans);letter-spacing:.12em;}
.object-heading h1{font:500 clamp(1.9rem,3.7vw,3.1rem)/1.12 var(--serif);margin:10px 0 14px;text-wrap:balance;}
.object-heading p{font:.8rem/1.7 var(--sans);color:#56616a;}
.object-heading cite{font-style:normal;}
.object-game-grid{display:grid;grid-template-columns:minmax(0,1.85fr) minmax(310px,1fr);align-items:start;gap:28px;}
.object-world{min-width:0;}
.object-diorama{box-sizing:border-box;width:100%;min-width:0;position:relative;isolation:isolate;aspect-ratio:9/5.6;min-height:310px;border:8px solid #faf5e8;border-radius:48% 48% 12px 12px/8% 8% 12px 12px;box-shadow:0 2px 0 #a38d69,0 12px 0 -5px #dcd1bb,0 28px 42px -20px #59482a55;overflow:hidden;background:#e7dec9;}
.object-room-architecture{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;}
.object-room-caption{position:absolute;top:4%;left:10%;right:10%;text-align:center;font:500 clamp(.7rem,1vw,.75rem)/1.4 var(--serif);color:#5d5d55;}
.object-props{position:absolute;inset:13% 3% 7%;}
.object-prop{position:absolute;left:calc(3% + var(--object-column)*33%);top:calc(var(--object-row)*51%);width:27%;height:48%;min-width:44px;min-height:44px;background:transparent!important;box-shadow:none!important;border:1px solid transparent!important;padding:7px 8px!important;border-radius:22px;display:flex;align-items:center;justify-content:center;transition:transform .18s ease-out,background-color .18s ease-out;overflow:visible;}
.object-prop .object-illustration{height:100%;width:100%;filter:drop-shadow(2px 5px 1px #5a47371c);}
.object-prop:hover:not(:disabled),.object-prop:focus-visible{background:#fffdf52e!important;transform:translateY(-3px);}
.object-prop:focus-visible{outline:3px solid #35568c;outline-offset:0;}
.object-prop.is-selected{background:#fffdf54d!important;border:1px dashed #8c713a!important;}
.object-prop:disabled{opacity:1;cursor:default;color:inherit;}
.object-prop:disabled:not(.is-selected){opacity:.68;}
.object-number{position:absolute;left:5%;bottom:3%;display:grid;place-items:center;min-width:25px;height:25px;border:1px solid #cfbd97;border-radius:50%;background:#fffdf5;color:#35568c;font:600 .8rem/1 var(--sans);box-shadow:0 2px 0 #ae9b7233;}
.object-number .ic{width:17px;height:17px;}
.object-world-note{margin:26px 0 12px;text-align:center;font:.8rem/1.6 var(--sans);color:#56616a;}
.object-round-track{display:flex;justify-content:center;gap:10px;min-height:30px;}
.object-round-track>span{display:grid;place-items:center;width:28px;height:28px;border:1px solid #c9bda7;border-radius:50%;font:.75rem/1 var(--sans);}
.object-round-track .ic{width:17px;height:17px;}.object-round-track .is-met{background:#d7e5d8;border-color:#719880;}
.object-workbench{box-sizing:border-box;position:relative;min-width:0;background:#fffdf5;border:1px solid #d9cdb5;border-radius:24px 24px 8px 8px;box-shadow:0 5px 0 #e4d9c5;padding:26px;}
.object-resident{width:64px;height:68px;margin-bottom:14px;}.object-resident svg{width:100%;height:100%;}
.object-workbench h2{font:500 1.6rem/1.25 var(--serif);margin:10px 0 16px;overflow-wrap:anywhere;}
.object-workbench p{font:.9rem/1.7 var(--sans);margin:12px 0;}
.object-workbench .smallcaps{font:600 .7rem/1.5 var(--sans);letter-spacing:.09em;}
.object-workbench .btn{display:flex;align-items:center;justify-content:center;gap:12px;width:100%;min-height:48px;white-space:normal;margin-top:14px;text-transform:none;letter-spacing:0;font:600 .85rem/1.5 var(--sans);padding:12px 16px;box-sizing:border-box;}
.object-workbench .btn .ic{width:20px;height:20px;flex-shrink:0;}
.object-workbench .btn.primary{background:#35568c;border-color:#35568c;color:#fffdf5;}
.object-fact{display:flex;align-items:center;gap:14px;}.object-detail-art{flex:0 0 72px;}.object-detail-art svg{width:72px;height:78px;}.object-fact h2{font-size:1.6rem;margin-bottom:6px;}.object-fact p{font-size:.85rem;margin:0;color:#596b73;}
.object-workbench .object-sentence{padding:15px 0;border-top:1px solid #e6dcc8;font:500 1.2rem/1.6 var(--serif);}
.object-explored{display:flex;gap:12px;align-items:center;margin:25px 0;font:.75rem/1.5 var(--sans);}.object-explored progress{width:100%;height:6px;accent-color:#4b8b78;}.object-explored span{white-space:nowrap;}
.object-recall-art{height:124px;display:flex;justify-content:center;}.object-recall-art svg{width:124px;height:100%;max-width:100%;}.object-workbench .object-meaning{text-align:center;font-weight:600;}
#object-form label{display:block;font:.8rem/1.6 var(--sans);margin:16px 0 8px;}
#object-answer{box-sizing:border-box;display:block;width:100%;min-height:50px;max-width:100%;padding:12px;border:1px solid #9da9b8;border-radius:8px;font:500 1.1rem/1.5 var(--sans);background:#fff;color:#213855;}
#object-answer:focus-visible{outline:3px solid #efb94e;outline-offset:2px;}
#object-keyboard{margin-top:10px;}.object-help{display:flex;align-items:center;gap:12px;flex-wrap:wrap;}.object-help .btn{width:auto;flex:1;font-size:.75rem;}
.object-text-button{border:0;background:none;box-shadow:none;color:#35568c;text-decoration:underline;text-underline-offset:4px;padding:10px 0;min-height:44px;white-space:normal;font:600 .8rem/1.6 var(--sans);text-align:left;cursor:pointer;text-transform:none;letter-spacing:0;}
.object-workbench .object-clue{min-height:1.5em;color:#35568c;font:600 1.1rem/1.5 var(--serif);margin:8px 0 0;}
.object-workbench .object-clue:empty{display:none;}.object-feedback.is-right>h2{color:#3c7258;}.object-workbench .object-feedback-note{font-size:.78rem;}
.object-finish-seal{display:grid;place-items:center;width:64px;height:64px;background:#f3e5bc;border-radius:50%;}.object-finish-seal .ic{width:34px;height:34px;color:#8f6e2c;}
.object-score{display:flex;flex-direction:column;gap:6px;margin:20px 0;font:.85rem/1.5 var(--sans);}.object-score strong{font:500 2.8rem/1 var(--serif);}
.object-bottom{display:flex;justify-content:space-between;align-items:center;gap:20px;margin:30px 0 8px;border-top:1px solid #d5ccbd;padding-top:14px;font:.75rem/1.5 var(--sans);color:#616b6a;}
.object-scene-door{display:grid;grid-template-columns:116px minmax(0,1fr) 24px;align-items:center;gap:20px;text-align:left;width:100%;box-sizing:border-box;min-height:135px;padding:20px 24px;margin:22px 0;background:#e8efea;border:1px solid #c8d5cc;border-radius:20px;box-shadow:0 4px 0 #263e3510;color:#233e39;text-transform:none;letter-spacing:0;cursor:pointer;}
.object-scene-door:hover{background:#e0ebe3;}.object-scene-door>span:nth-child(2){display:grid;gap:6px;min-width:0;}.object-scene-door strong{font:500 1.45rem/1.3 var(--serif);}.object-scene-door span span{font:.85rem/1.6 var(--sans);}.object-scene-door small{font:650 .75rem/1.5 var(--sans);}.object-scene-door>.ic{width:24px;height:24px;}
.object-door-art{position:relative;height:95px;display:block;}.object-door-art>svg{position:absolute;width:76px;height:88px;top:0;left:0;transform:rotate(-10deg);}.object-door-art>svg+svg{left:48px;top:12px;transform:rotate(12deg);}
#v-course>.object-scene-door,#v-library>.object-scene-door{max-width:1100px;margin:28px auto;}
.daily-object-art{width:140px;height:148px;margin:15px auto;}.daily-object-art svg{width:100%;height:100%;}
@media(max-width:900px){.object-game-grid{grid-template-columns:minmax(0,1fr);gap:24px;}.object-diorama{max-width:720px;margin:0 auto;}.object-workbench{max-width:720px;width:100%;margin:0 auto;}.object-resident{margin:0 0 12px;width:54px;height:58px;}.object-recall-art{height:110px;}.object-workbench .object-fact{clear:both;}}
@media(max-width:560px){.scene-word-stage{padding:14px 12px 24px;}.object-top{gap:10px;}.object-top>span{font-size:.72rem;gap:8px;}.object-top .btn{font-size:.7rem;padding:9px 11px;max-width:46%;white-space:normal;}.object-heading{margin:22px 4px;}.object-heading h1{font-size:2rem;}.object-heading p{font-size:.75rem;}.object-diorama{min-height:270px;border-width:5px;}.object-room-caption{font-size:.7rem;top:3%;}.object-props{inset:14% 1% 7%;}.object-prop{width:29%;padding:8px 2px!important;}.object-workbench{padding:22px 18px;}.object-workbench h2{font-size:1.45rem;}.object-fact h2{font-size:1.5rem;}.object-bottom{align-items:flex-start;flex-direction:column;gap:6px;}.object-scene-door{grid-template-columns:80px minmax(0,1fr);gap:16px;padding:18px 16px;}.object-scene-door>.ic{display:none;}.object-door-art{height:80px;}.object-door-art>svg{width:56px;height:65px;}.object-door-art>svg+svg{left:28px;top:18px;}.object-scene-door strong{font-size:1.2rem;}.object-scene-door span span{font-size:.78rem;}}
@media(prefers-reduced-motion:reduce){.object-prop{transition:none!important;transform:none!important;}}
body.motion-low .object-prop{transition:none!important;transform:none!important;}

.scene-word-stage h1,.scene-word-stage h2,.scene-word-stage .btn{text-transform:none;letter-spacing:normal;}

.object-round-track .is-supported{background:#f3e6c2;border-color:#bda35b;}
/* Expeditions: the setting is a place; the workbench asks one thing. */
.expedition-stage .object-sheet{max-width:1400px;}.expedition-stage .object-top>span{font:500 1.15rem/1.4 var(--serif);}.quest-toolbar{display:flex;justify-content:space-between;align-items:start;gap:20px;margin:20px 0;font:.85rem/1.5 var(--sans);}.quest-settings{position:relative;max-width:540px;min-width:0;}.quest-settings>summary{cursor:pointer;min-height:44px;text-align:right;color:#35568c;font-weight:600;}.quest-settings[open]{background:#fffdf5;border:1px solid #cfc5b5;padding:16px;border-radius:16px;}.quest-settings nav{display:flex;gap:8px;}.quest-settings nav button{flex:1;min-height:44px;border:1px solid #c6cfc8;border-radius:9px;padding:8px;background:#fffdf5;box-shadow:none;}.quest-settings nav [aria-pressed="true"]{background:#35568c;color:white;}.quest-world-list{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-top:12px;}.quest-world-list button{display:flex;align-items:center;gap:8px;min-width:0;padding:8px;border:1px solid #d8d0c0;border-radius:9px;background:#f4f0e5;text-align:left;text-transform:none;letter-spacing:0;font:.8rem/1.4 var(--sans);box-shadow:none;}.quest-world-list svg{width:44px;height:50px;flex-shrink:0;}.quest-world-list [aria-pressed="true"]{border:2px solid #35568c;}.quest-room{border-radius:18px;min-height:360px;aspect-ratio:9/5.6;}.quest-prop{position:absolute;left:var(--x);top:var(--y);transform:translate(-50%,-50%);width:19%;height:25%;min-width:44px;min-height:44px;padding:0;border:0!important;box-shadow:none!important;background:transparent!important;cursor:pointer;transition:filter .2s ease-out;}.quest-prop svg{width:100%;height:100%;filter:drop-shadow(3px 6px 2px #43352028);}.quest-prop:disabled{opacity:1;cursor:default;}.quest-prop:focus-visible{outline:3px solid #35568c;outline-offset:3px;border-radius:12px;}.quest-prop.is-selected::before,.quest-prop.is-target::before{content:"";position:absolute;inset:5%;border:2px solid #a98134;border-radius:50%;background:#fff7d535;z-index:-1;}.quest-prop.is-selected::before{background:#d8ede589;border-color:#4b8b78;}.quest-picked{position:absolute;bottom:0;right:0;background:#4b8b78;color:#fffdf5;border-radius:50%;width:24px;height:24px;display:grid;place-items:center;}.quest-picked svg{width:18px;height:18px;}.quest-secret{position:absolute;right:12px;bottom:8px;width:44px;height:44px;padding:10px;border:0;background:#fffdf566;color:#675637;box-shadow:none;border-radius:50%;}.quest-secret .ic{width:22px;height:22px;}.quest-secret-note{display:flex;align-items:center;gap:12px;padding:16px;margin-top:16px;border:1px solid #c5cfbf;border-radius:12px;background:#e6eee3;font:1.1rem/1.5 var(--serif);}.quest-secret-note[hidden]{display:none;}.quest-secret-note svg{width:48px;height:60px;flex-shrink:0;}.quest-source{margin-top:20px;font:.8rem/1.6 var(--sans);color:#617066;}.quest-source summary{cursor:pointer;min-height:44px;}.quest-source p{max-width:70ch;}.quest-source cite{font-style:normal;}.expedition-stage .object-workbench{padding:22px 26px;}.expedition-stage .object-resident{width:72px;height:72px;margin-bottom:8px;}.expedition-stage .object-workbench h2{font-size:1.7rem;}.expedition-stage .quest-prompt{font:500 1.25rem/1.55 var(--serif);}.quest-packing{display:flex;align-items:center;gap:12px;min-height:80px;border:1px dashed #a5b6a8;border-radius:12px;padding:10px;}.quest-packing svg{width:58px;height:66px;}.quest-packing span{margin-left:auto;font-size:.85rem;}.quest-detail-art{width:86px;height:92px;margin:12px auto;}.quest-detail-art svg{width:100%;height:100%;}.quest-built,.quest-tiles{display:flex;flex-wrap:wrap;gap:8px;margin:16px 0;}.quest-built{min-height:72px;border:1px dashed #9baf9c;border-radius:12px;padding:12px;align-items:center;background:#edf2e9;}.quest-built>span{font:.85rem/1.5 var(--sans);color:#5e6e61;}.quest-built button,.quest-tiles button{min-height:44px;padding:9px 12px;background:#fffdf5;color:#253a47;border:1px solid #bcb9a8;border-radius:8px;box-shadow:0 3px 0 #c9c4b4;font:500 1rem/1.5 var(--serif);text-transform:none;letter-spacing:0;}.quest-tiles button:disabled{opacity:.28;box-shadow:none;}.quest-responses{display:grid;gap:12px;margin:18px 0;}.quest-responses button{padding:16px;border:1px solid #b6c8bf;border-radius:12px;background:#f0f4ed;box-shadow:0 3px 0 #d4ddd2;text-align:left;font:500 1.05rem/1.65 var(--serif);text-transform:none;letter-spacing:0;}.quest-responses button:hover{background:#e1ebe2;}.quest-help,.quest-model{margin:18px 0;font:.85rem/1.6 var(--sans);}.quest-help summary,.quest-model summary{cursor:pointer;min-height:44px;}.quest-help button{display:block;}.expedition-stage .quest-answer{font:500 1.55rem/1.5 var(--serif);overflow-wrap:anywhere;}.quest-feedback.is-right h2{color:#3c7258;}.quest-finish-art{width:96px;height:96px;}.quest-finish-art svg{width:100%;height:100%;}.object-bottom progress{width:180px;max-width:45%;height:8px;accent-color:#4b8b78;}.expedition-stage label{font:.85rem/1.6 var(--sans);}.expedition-stage textarea{box-sizing:border-box;width:100%;max-width:100%;padding:14px;border:1px solid #a6b7b2;border-radius:10px;font:1rem/1.65 var(--sans);background:#fffdf5;color:#24364b;margin:12px 0;resize:vertical;}.quest-writing-checks{border:1px solid #d5cebd;padding:12px;border-radius:10px;margin:16px 0;min-width:0;}.quest-writing-checks legend{font:.8rem/1.5 var(--sans);}.quest-writing-checks label{display:flex;align-items:center;gap:10px;min-height:44px;}.quest-writing-checks input{appearance:auto;width:20px;height:20px;flex-shrink:0;}.expedition-stage small{font:.75rem/1.6 var(--sans);display:block;margin-top:10px;}
@media(min-width:1100px){.expedition-stage .object-game-grid{grid-template-columns:minmax(0,1.5fr) minmax(360px,1fr);}.expedition-stage[data-mode="response"] .object-game-grid,.expedition-stage[data-mode="write"] .object-game-grid{grid-template-columns:minmax(0,1fr) minmax(420px,1.2fr);}}
@media(max-width:900px){.expedition-stage .object-game-grid{gap:12px;}.quest-room{min-height:310px;}.expedition-stage .object-resident{width:54px;height:58px;}.quest-source{margin:8px 0;}.expedition-stage .object-workbench{max-width:none;}}
@media(max-width:560px){.expedition-stage .object-top>span{font-size:1rem;}.quest-toolbar{font-size:.78rem;gap:12px;margin:14px 0;}.quest-settings{max-width:80%;}.quest-settings[open]{padding:12px;}.quest-world-list{grid-template-columns:minmax(0,1fr);}.quest-room{min-height:260px;border-width:4px;}.quest-prop{width:19%;height:24%;}.expedition-stage .object-workbench{padding:20px 18px;}.expedition-stage .object-workbench h2{font-size:1.5rem;}.quest-responses button{font-size:1rem;}.quest-secret{right:2px;bottom:1px;}.quest-source summary{min-height:36px;}.expedition-stage .object-bottom{flex-direction:row;}.quest-built button,.quest-tiles button{font-size:.95rem;padding:9px;}}
/* Compact navigation. Progress has one home; extra choices stay available. */
#v-course .course-atlas{display:block;max-width:1100px;margin:auto;}#v-course .course-atlas>.atlas-heading{margin-bottom:18px;}#v-course .atlas-heading h2{font:500 1.55rem/1.25 var(--serif);}#v-course .course-atlas>.level-rail{margin-bottom:24px;}#v-course .course-atlas>.level-rail>.level-tabs{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;position:static;}#v-course .level-tabs .level-tab{padding:14px 12px;border-radius:12px;min-height:70px;display:flex;flex-direction:column;justify-content:center;gap:4px;}#v-course .level-tab .level-tab-code{font:650 1rem/1.3 var(--sans);}#v-course .level-tabs .level-tab strong{font:500 .8rem/1.4 var(--sans);}#v-course .level-caption{margin:16px 0;}#v-course .chapter-heading{padding:16px 20px;margin-bottom:18px;}#v-course .chapter-heading p{display:none;}#v-course .chapter-heading h2{font-size:1.5rem;}#v-course .chapter-heading .chapter-companion{width:68px;height:68px;}#v-course .chapter-heading .chapter-tally{font-size:.8rem;}#v-course .chapter-ending{padding:14px 18px;}#v-course .chapter-ending h3{font:500 1rem/1.4 var(--sans);}#v-course .chapter-ending p,#v-course .chapter-ending .chapter-seal{display:none;}#v-course .level-panel>.object-scene-door{margin:10px 0 24px;min-height:94px;padding:12px 20px;grid-template-columns:76px minmax(0,1fr) 24px;}#v-course .level-panel>.object-scene-door .object-door-art{height:70px;}#v-course .level-panel>.object-scene-door .object-door-art svg{width:52px;height:62px;}#v-course .level-panel>.object-scene-door .object-door-art svg+svg{left:28px;}#v-course .level-panel>.object-scene-door strong{font-size:1.2rem;}
.book-course-tools{max-width:1100px;margin:20px auto;}.book-course-tools label{display:block;font:600 .85rem/1.6 var(--sans);margin-bottom:8px;}.book-course-tools input{box-sizing:border-box;width:100%;min-height:48px;padding:12px 16px;border:1px solid var(--rule);border-radius:12px;background:var(--paper);font:1rem/1.5 var(--sans);color:var(--carbon);}#v-course .cs-wing-books .rd{max-width:1100px;margin:0 auto;padding:0;}#v-course .cs-wing-books .rd-shelfhead{padding:12px 0;margin:12px 0;font-size:.8rem;}#v-course .cs-wing-books .rd-shelfhead>span{display:none;}#v-course .cs-wing-books .rd-book{margin:10px 0;border:1px solid var(--rule);border-radius:14px;background:var(--paper);overflow:hidden;}#v-course .cs-wing-books .rd-book>.rd-hit{padding:18px;min-height:110px;}#v-course .cs-wing-books .rd-book:not(.rd-open) .rd-can{display:none;}#v-course .cs-wing-books .rd-book[hidden]{display:none!important;}#v-course .cs-wing-books .rd-book .rd-ti{font:500 1.25rem/1.5 var(--serif);}#v-course .cs-wing-books .rd-open-box{margin:0;padding:18px;border-top:1px solid var(--rule);}.lesson-window.lesson-emblem{display:grid;place-items:center;background:transparent!important;box-shadow:none!important;border:0!important;min-height:90px;}.lesson-emblem>.brand-art{width:90px;height:90px;}.lesson-emblem:has(.gx-mate)>.brand-art{opacity:.18;}.lesson-emblem .gx-mate{inset:12px!important;width:72px!important;position:absolute!important;}.lesson-emblem::before,.lesson-emblem::after{display:none!important;}
.earned-moment{position:fixed;z-index:3500;left:50%;top:35%;width:180px;height:180px;margin:-90px;pointer-events:none;display:grid;place-items:center;perspective:700px;}.earned-medallion{position:relative;width:132px;height:132px;display:grid;place-items:center;border:7px solid #e7bb5c;border-radius:50%;background:radial-gradient(circle at 30% 20%,#fff9df,#edcc7c);box-shadow:0 8px 0 #a78136,0 16px 0 -5px #654e28,0 32px 48px #403a3744;}.earned-medallion>.cast-portrait{width:90px;height:104px;}.earned-medallion::after{content:"";position:absolute;inset:5px;border:1px solid #fff5d3;border-radius:50%;}.earned-moment>.ic{position:absolute;width:38px;height:38px;right:2px;bottom:8px;color:#35568c;background:#fffdf5;padding:8px;border-radius:50%;}
/* Keep the plain-language level names on phones as well as their CEFR codes. */
#v-course .level-tabs .level-tab > strong { display: block; overflow-wrap: anywhere; }
#v-course .level-tabs .level-tab > .level-tab-code { font: 650 1rem/1.3 var(--sans); margin: 0; }
/* One disclosure opens the passport; its repeated closed summary adds no choice. */
#course-passport-details:not([open]) { display: none; }
@media (max-width: 560px) {
  #v-course .course-atlas > .level-rail > .level-tabs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media(max-width:560px){#v-course .level-tabs .level-tab{padding:12px 5px;min-height:66px;}#v-course .level-tabs .level-tab strong{font-size:.7rem;}#v-course .cs-wings .cs-wt-mid em{display:none;}#v-course .cs-wing-books .rd-book>.rd-hit{padding:14px;}#v-course .cs-wing-books .rd-book .rd-ti{font-size:1.1rem;}.book-course-tools{margin:16px 0;}}
@media(prefers-reduced-motion:reduce){.quest-prop{transition:none;}.earned-moment{display:none;}}
body.motion-low .quest-prop{transition:none;}body.motion-low .earned-moment{display:none;}
.desk-other-discoveries{margin:16px 0;}.desk-other-discoveries>summary{min-height:48px;padding:12px 0;font:600 .9rem/1.6 var(--sans);cursor:pointer;}.daily-mode .daily-top{padding-bottom:6px;}.daily-mode .daily-invitation{padding:24px;}.daily-mode .daily-copy h2{font-size:clamp(1.7rem,3vw,2.3rem);}.daily-mode .daily-landscape{max-height:210px;}.daily-mode .object-scene-door{min-height:105px;margin:16px 0;}.daily-mode .object-scene-door .object-door-art{height:70px;}.daily-mode .object-scene-door .object-door-art svg{height:68px;}.daily-mode .object-scene-door strong{font-size:1.3rem;}
#v-course .cs-wing-books .rd-mark,#v-course .cs-wing-books .rd-beyond{display:none;}#v-course .rd-shelfhead[hidden],#v-course .rd-ask[hidden]{display:none!important;}#v-course .cs-wing-books .rd-book:not(.rd-open) .rd-next{display:none;}
.quest-prop{width:var(--w,19%);height:var(--h,25%);}
.quest-directive{margin:0 0 16px;padding:16px 20px;background:#fffdf5;border:1px solid #d1cbbb;border-radius:14px;font:500 1.05rem/1.55 var(--sans);}.expedition-stage[data-mode="find"] .object-workbench>.quest-prompt,.expedition-stage[data-mode="pack"] .object-workbench>.quest-prompt{display:none;}
#v-course .cs-wing-books>.cs-ref{display:none;}
.expedition-stage .btn:disabled{opacity:.45;cursor:default;box-shadow:none;}
#v-course .rd-access-brief{display:block;font:.8rem/1.5 var(--sans);color:var(--pencil);margin-top:6px;}#v-course .rd-book.rd-open .rd-access-brief,#v-course .rd-book:not(.rd-open) .rd-opens{display:none;}

/* Long compounds and bridge translations must fit the same phone layout. */
.scene-word-stage :is(p,h2,button,summary,label,small){overflow-wrap:anywhere;}
.object-top>span{min-width:0;}.object-top .ic{flex-shrink:0;}
.scene-word-stage .object-top .btn{font:600 .8125rem/1.5 var(--sans);}
.object-workbench :is(button,fieldset),.quest-world-list button>span{min-width:0;max-width:100%;box-sizing:border-box;}
.quest-built,.quest-tiles,.quest-responses{min-width:0;}
.quest-built button,.quest-tiles button,.quest-responses button{white-space:normal;}
.quest-secret-note>span{min-width:0;overflow-wrap:anywhere;}
.object-bottom>span{min-width:0;}.object-bottom progress{flex-shrink:0;}
@media(max-width:560px){
 .quest-toolbar{flex-wrap:wrap;}.quest-toolbar>span{flex-shrink:0;}
 .quest-settings{margin-left:auto;max-width:100%;box-sizing:border-box;}
 .quest-settings[open]{flex-basis:100%;}
 .quest-settings nav{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));}
 .object-scene-door strong,.object-scene-door span span{overflow-wrap:anywhere;}
}

/* A listening card keeps the recording, original and meaning in named rows.
   Playback status belongs to its control, never to the sentence grid. */
.gx-stage .gx-rwl-line{display:grid;grid-template-columns:minmax(0,1fr);gap:14px;padding:clamp(16px,3vw,28px);border:1px solid var(--rule);border-left:4px solid var(--world-deep,var(--play));border-radius:6px 20px 20px 6px;background:var(--paper);}
.gx-stage .gx-rwl-line>.lesson-audio-control{grid-column:1;grid-row:1;min-width:0;display:flex;align-items:center;flex-wrap:wrap;gap:10px 16px;}
.gx-stage .lesson-audio-control>.gx-say{position:static;display:inline-flex;align-items:center;justify-content:center;gap:10px;width:auto;max-width:100%;min-width:44px;min-height:44px;padding:10px 14px;margin:0;font:600 .8125rem/1.5 var(--sans);white-space:normal;letter-spacing:0;text-transform:none;border:1px solid var(--rule);border-radius:12px;background:var(--world-tint,var(--galley));color:var(--carbon);box-shadow:none;}
.lesson-audio-control .ic{width:20px;height:20px;flex-shrink:0;}
.lesson-audio-control .audio-button-status{flex:1 1 180px;margin:0;overflow-wrap:anywhere;}
.gx-stage .gx-rwl-line>.gx-rwl-ru{grid-column:1;grid-row:2;min-width:0;overflow-wrap:anywhere;font:500 clamp(1.35rem,2.6vw,1.8rem)/1.65 var(--serif);}
.gx-stage .gx-rwl-line>.gx-rwl-en{grid-column:1;grid-row:3;min-width:0;overflow-wrap:anywhere;max-width:65ch;padding-top:12px;border-top:1px solid var(--rule);font:400 1rem/1.65 var(--serif);color:var(--pencil);opacity:1;}
.audio-button-status{grid-column:1 / -1;min-width:0;overflow-wrap:anywhere;}

/* Continuing a round stays in practice; its result travels with the next ask. */
.daily-carry{display:flex;align-items:center;gap:12px;padding:14px 18px;margin:8px auto 22px;max-width:760px;box-sizing:border-box;background:#e8f0e6;border:1px solid #bfceba;border-radius:14px;color:#2c513e;font:.8125rem/1.5 var(--sans);}
.daily-carry>.ic{width:24px;height:24px;flex-shrink:0;}.daily-carry>span{display:grid;gap:3px;min-width:0;overflow-wrap:anywhere;}
.daily-flow-finish{--flow-tint:#e6eee3;display:grid;grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);gap:24px 36px;max-width:1000px;margin:16px auto;text-align:left;align-items:center;}
.daily-flow-finish[data-next-kind="restore"]{--flow-tint:#f4e6cb;}.daily-flow-finish[data-next-kind="recall"]{--flow-tint:#e9e3f2;}.daily-flow-finish[data-next-kind="story"]{--flow-tint:#e0eaf3;}
.daily-flow-picture{display:grid;place-items:center;padding:24px;background:var(--flow-tint);border-radius:50% 50% 22px 22px;min-width:0;}.daily-flow-picture>svg{width:100%;max-width:330px;height:auto;max-height:290px;}
.daily-flow-copy{min-width:0;}.daily-flow-copy h2{font:500 clamp(1.6rem,3vw,2.3rem)/1.25 var(--serif);margin:14px 0 24px;text-transform:none;letter-spacing:0;overflow-wrap:anywhere;}
.daily-flow-result{display:flex;gap:8px;align-items:center;font:.85rem/1.5 var(--sans);color:var(--pencil);}.daily-flow-result>.ic{width:22px;height:22px;flex-shrink:0;}
.daily-next-route{list-style:none;padding:0;display:grid;gap:8px;margin:12px 0 20px;}.daily-next-route li{display:grid;grid-template-columns:22px minmax(0,1fr) auto;align-items:center;gap:12px;padding:12px 14px;border:1px solid var(--rule);border-radius:12px;background:var(--paper);font:.875rem/1.5 var(--sans);overflow-wrap:anywhere;}.daily-next-route .ic{width:22px;height:22px;}.daily-next-route small{font:600 .8rem/1.4 var(--sans);}
.daily-flow-copy>.btn{box-sizing:border-box;width:100%;min-height:52px;font:600 1rem/1.5 var(--sans);text-transform:none;letter-spacing:0;white-space:normal;overflow-wrap:anywhere;border-radius:14px;}.daily-flow-copy>small{display:block;margin-top:10px;font:.8rem/1.5 var(--sans);color:var(--pencil);}
.daily-flow-source{font:500 1rem/1.5 var(--serif);color:var(--pencil);overflow-wrap:anywhere;}.daily-round-notes{font:.8rem/1.5 var(--sans);margin-top:18px;}.daily-round-notes summary{cursor:pointer;min-height:44px;padding-block:12px;}
.daily-flow-finish>.daily-finish-actions,.daily-flow-finish>.daily-album{grid-column:1/-1;}.daily-flow-finish>.daily-finish-actions{padding-top:8px;justify-content:flex-end;}.daily-flow-finish>.daily-album{font:.8rem/1.6 var(--sans);}
#daily-round-summary{display:block;margin:12px auto 0;min-height:44px;}
@media(max-width:650px){.daily-flow-finish{grid-template-columns:minmax(0,1fr);gap:16px;margin:8px auto;}.daily-flow-picture{display:none;}.daily-flow-copy h2{margin:12px 0 20px;}.daily-flow-finish>.daily-finish-actions{justify-content:center;}.daily-next-route li{padding:10px 12px;}.daily-carry{margin-bottom:16px;}}
[data-theme="dark"] .daily-carry{background:#263c30;color:#d9e8d6;border-color:#526b50;}[data-theme="dark"] .daily-flow-picture{background:var(--galley);}


/* ===== 109-rank-design.css ===== */
/* The reader's ledger: ink, paper and brass. One material language for
   progress, earned receipts, the profile and the promotion invitation. */
:root { --rank-brass:#906723;--rank-wash:#f4eddc;--rank-track:#ded8cb;--rank-ease:cubic-bezier(.2,.7,.2,1); }
[data-theme="sepia"] { --rank-brass:#80591f;--rank-wash:#eee0c5;--rank-track:#d5c6ad; }
[data-theme="dark"] { --rank-brass:#dfb967;--rank-wash:#2d3040;--rank-track:#46516a; }
[data-theme="dark"] .rank-art { color:var(--steel)!important; }

/* Retire the inset frame and negative-margin cap in this shared dialog.
   The dismiss control occupies a real row, including when translated. */
#ov-key .panel { box-sizing:border-box;width:min(520px,calc(100vw - 32px));max-width:520px;padding:24px 28px 28px;border:1px solid var(--rule);border-radius:14px;background:var(--paper);box-shadow:0 24px 80px #101b3238;max-height:calc(100dvh - 32px);overflow:auto; }
#ov-key .panel::before { content:none; }
#key-panel .key-dialog-close { position:static;display:block;margin:0 0 18px auto;min-height:40px;padding:8px 12px;border:1px solid var(--rule);border-radius:6px;box-shadow:none;background:transparent;color:var(--steel);font:500 .8rem/1.4 var(--sans);letter-spacing:0;text-transform:none; }
#key-panel > h3,#key-panel:has(#key-dialog-close)>h3 { margin:0 0 20px!important;padding:0!important;border:0;background:transparent;color:var(--carbon);font:500 1.75rem/1.2 var(--serif)!important;letter-spacing:-.02em;text-transform:none;text-wrap:wrap;overflow-wrap:anywhere; }
#key-panel>.smallcaps { margin-bottom:10px; }
#key-panel .btnrow { gap:10px;flex-wrap:wrap; }
#key-panel .btnrow>.btn { min-height:44px;border-radius:6px;font:600 .85rem/1.4 var(--sans);letter-spacing:0;text-transform:none;box-shadow:none; }
#key-panel .btnrow>.btn:not(.primary) { border:1px solid var(--rule);background:transparent;color:var(--carbon); }
#key-panel .btnrow>.btn.primary { background:var(--play);color:var(--on-play);border:1px solid var(--play); }
#key-panel .rank-promotion { text-align:center;padding:0 8px 4px; }
.rank-promotion-seal { width:88px;height:97px;margin:0 auto 20px; }
.rank-promotion-class { margin:0 0 8px;color:var(--rank-brass);font:600 .75rem/1.5 var(--sans);letter-spacing:.08em; }
#key-panel .rank-promotion h3 { margin:0 0 14px;padding:0;background:none;border:0;color:var(--carbon);font:500 clamp(1.7rem,5vw,2.25rem)/1.15 var(--serif);letter-spacing:-.025em;text-transform:none;overflow-wrap:anywhere; }
.rank-promotion-earned { font:.95rem/1.6 var(--sans);color:var(--carbon);margin:0 0 6px; }
#key-panel .rank-promotion .k-lede { font:.9rem/1.6 var(--sans);color:var(--steel);margin:0 0 26px; }
.rank-promotion .btnrow { justify-content:center;padding-top:22px;border-top:1px solid var(--rule); }

/* A rank is a small bookplate; progress is one continuous brass rule. */
.practice-progress { border:1px solid var(--rule);border-radius:10px;background:var(--paper);box-shadow:none; }
:is(#practice-rewards,.course-rank-progress) .practice-progress { grid-template-columns:minmax(190px,1fr) minmax(180px,1.2fr);gap:12px 30px;padding:18px 22px;border:1px solid var(--rule);border-radius:10px;background:var(--paper); }
:is(#practice-rewards,.course-rank-progress) .pp-level { grid-column:1;grid-row:1; }
:is(#practice-rewards,.course-rank-progress) .pp-streak { grid-column:1;grid-row:2;text-align:left;padding-left:58px; }
.pp-track { min-width:0; }
:is(#practice-rewards,.course-rank-progress) .pp-track { grid-column:2;grid-row:1/3;align-self:center; }
.pp-destination { color:var(--carbon);font:500 .78rem/1.5 var(--sans);margin:0 0 10px;overflow-wrap:anywhere; }
.pp-level { gap:14px; }
.pp-level>span:last-child { max-width:none; }
.pp-level small { font-size:.72rem;line-height:1.5; }
.pp-level small:last-child:not(:first-of-type) { text-decoration:underline;text-underline-offset:3px;margin-top:4px; }
.pp-meter,:is(#practice-rewards,.course-rank-progress) .pp-meter,.is-profile .pp-meter { height:6px;border-radius:2px;background:var(--rank-track);margin:0; }
.pp-meter>span { border-radius:2px;background:var(--rank-brass); }
.pp-numbers { margin-top:8px;font-size:.7rem;font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 12px; }
.pp-streak { font-size:.72rem;color:var(--steel); }
.pp-session { padding-top:12px;border-top:1px solid var(--rule); }
.pp-receipt,.daily-feedback>.pp-receipt { border:1px solid var(--rule);border-left:3px solid var(--rank-brass);background:var(--paper);border-radius:8px;padding:16px 18px;gap:14px;box-shadow:none; }
.pp-receipt.is-milestone { border-color:var(--rank-brass);background:var(--rank-wash); }
.pp-receipt-copy>strong { font:500 1rem/1.35 var(--serif); }
.pp-receipt-copy>span { font:600 .75rem/1.5 var(--sans);color:var(--rank-brass); }
.pp-receipt-progress { line-height:1.5; }
.pp-receipt-progress .pp-meter { height:4px;margin:7px 0; }
.pp-receipt.is-live { animation:ledger-arrive 220ms var(--rank-ease) both; }
.pp-receipt.is-live .pp-meter>span { animation:practice-reward-fill 440ms var(--rank-ease) both; }
.pp-receipt.is-live.is-milestone .pp-receipt-mark { animation:ledger-seal 420ms var(--rank-ease) both; }
@keyframes ledger-arrive { from{opacity:0;transform:translateY(5px)}to{opacity:1;transform:none} }
@keyframes ledger-seal { from{opacity:.4;transform:scale(.9)}to{opacity:1;transform:scale(1)} }

/* Profile: one status, one useful next action, one compact week. Details
   stay available without making every visitor read a lifetime dossier. */
#v-profile { background:var(--paper); }
#profile-dossier { max-width:860px;margin:0 auto;padding:28px 24px 48px; }
#profile-dossier>.room-head { padding:0 0 22px!important;margin:0 0 26px!important;border-bottom:1px solid var(--rule)!important;background:none; }
.profile-room-label { font:500 .75rem/1.5 var(--sans);color:var(--steel);margin-bottom:8px; }
#profile-dossier>.room-head h2 { font:500 clamp(1.7rem,4vw,2.25rem)/1.2 var(--serif)!important;color:var(--carbon)!important;margin:0!important;letter-spacing:-.025em; }
.profile-play { border-radius:0;overflow:visible;background:none;border:0;box-shadow:none;padding:0;margin:0; }
.practice-progress.is-profile { padding:24px 28px;margin:0 0 20px;border:1px solid var(--rule);border-radius:10px;box-shadow:none; }
.is-profile .pp-heading { margin-bottom:20px; }
.is-profile .pp-level { font:500 1.5rem/1.2 var(--serif); }
.is-profile .pp-level-mark { width:60px;min-width:60px;height:66px; }
.is-profile .pp-explainer { margin:16px 0 18px;font:.8rem/1.5 var(--sans); }
.pp-rank-ladder,.pp-collection,.pp-rules { margin:0;border:0;border-top:1px solid var(--rule); }
.pp-rank-ladder>summary,.pp-collection>summary,.pp-rules>summary { min-height:44px;padding:13px 0;font:500 .8rem/1.4 var(--sans);cursor:pointer;color:var(--carbon);text-decoration:none; }
.pp-rules>summary { display:list-item; }
.pp-collection>summary>span { float:right;color:var(--steel);font-variant-numeric:tabular-nums; }
.pp-rank-ladder li { border-radius:4px;border-color:transparent;border-bottom-color:var(--rule); }
.pp-rank-ladder .is-current { border-color:var(--rank-brass);background:var(--rank-wash); }
.pp-milestones { gap:0;grid-template-columns:repeat(2,minmax(0,1fr));padding:0 0 16px; }
.pp-badge,.pp-badge.is-earned { background:none;border:0;border-radius:0;padding:16px 16px 16px 0; }
.pp-badge>span:first-child { font:500 1rem/1.2 var(--serif);color:var(--steel); }
.pp-badge.is-earned>span:first-child { color:var(--rank-brass); }
.pp-badge strong { font:500 1rem/1.35 var(--serif); }
.pp-badge>span:last-child { color:var(--rank-brass); }
.profile-next { display:flex;align-items:center;justify-content:space-between;gap:24px;padding:24px 28px;margin:0 0 20px;background:var(--rank-wash);border-radius:10px;border:1px solid transparent; }
.profile-next .smallcaps { font:600 .6875rem/1.5 var(--sans);letter-spacing:.08em;color:var(--rank-brass); }
.profile-next h3 { font:500 1.35rem/1.25 var(--serif);margin:8px 0;color:var(--carbon);letter-spacing:0;text-transform:none; }
.profile-next p { font:.82rem/1.6 var(--sans);margin:0;color:var(--steel);max-width:46ch; }
.profile-next>.btn { flex:none;max-width:45%;min-height:44px;border-radius:6px;background:var(--play);color:var(--on-play);border:1px solid var(--play);box-shadow:none;font:600 .8rem/1.4 var(--sans);text-transform:none;letter-spacing:0; }
.profile-week { padding:20px 0;margin:0;border:0;background:none;box-shadow:none; }
.profile-week-head h3 { font:500 1.2rem/1.3 var(--serif);letter-spacing:0;text-transform:none;margin:0 0 6px; }
.profile-week-head p { font:.8rem/1.5 var(--sans);margin:0; }
.profile-week-head label { font:.72rem/1.5 var(--sans);color:var(--steel); }
.profile-week-pages { gap:6px;margin:18px 0 0; }
.profile-day { padding:10px 4px;min-height:76px;border:0;border-radius:6px;background:none;box-shadow:none; }
.profile-day b { font:500 1.35rem/1.3 var(--serif); }
.profile-day.is-earned { background:var(--rank-wash);border:0;color:var(--carbon);box-shadow:none; }
.profile-day.is-today { outline:1px solid var(--rank-brass);outline-offset:0; }
.profile-day>span { font-size:.6875rem; }
.profile-week-note { display:none; }
.profile-quests,.profile-ticket,.profile-history { margin:0;border:0;border-top:1px solid var(--rule);padding:0; }
.profile-quests>summary,.profile-ticket>summary,.profile-history>summary { cursor:pointer;min-height:48px;padding:16px 0;color:var(--carbon);font:500 .85rem/1.4 var(--sans); }
.profile-quest-grid { padding-bottom:20px; }
.profile-quest { border-radius:6px;box-shadow:none; }
#profile-dossier .pxx-foot { margin:16px 0 0;padding:20px 0;border-top:1px solid var(--rule); }
#profile-dossier .pxx-if { font:.9rem/1.6 var(--serif);margin:0 0 16px; }
#profile-dossier .pxx-if.is-empty { font-size:0;margin-bottom:12px; }
#profile-dossier .pxx-if.is-empty button { font:.78rem/1.5 var(--sans); }
#profile-dossier .pxx-utils { gap:10px 18px; }
#profile-dossier .px-verb { font:.78rem/1.5 var(--sans);min-height:36px; }
#profile-dossier .pxx-fine { font:.72rem/1.5 var(--sans);margin-top:16px; }
.reader-passport progress,.profile-quest progress { accent-color:var(--rank-brass); }
.reader-passport progress::-webkit-progress-bar,.profile-quest progress::-webkit-progress-bar { background:var(--rank-track); }
.reader-passport progress::-webkit-progress-value,.profile-quest progress::-webkit-progress-value { background:var(--rank-brass); }
.reader-passport progress::-moz-progress-bar,.profile-quest progress::-moz-progress-bar { background:var(--rank-brass); }

@media(max-width:600px) {
 #profile-dossier { padding:24px 16px 32px; }
 .practice-progress.is-profile { padding:20px; }
 :is(#practice-rewards,.course-rank-progress) .practice-progress { grid-template-columns:minmax(0,1fr);gap:8px;padding:16px; }
 :is(#practice-rewards,.course-rank-progress) .pp-level { grid-column:1;grid-row:1; }
 :is(#practice-rewards,.course-rank-progress) .pp-streak { grid-column:1;grid-row:2;padding-left:54px;max-width:none; }
 :is(#practice-rewards,.course-rank-progress) .pp-track { grid-column:1;grid-row:3;margin-top:10px; }
 .is-profile .pp-heading { align-items:flex-start;flex-wrap:wrap;gap:12px; }
 .is-profile .pp-level { font-size:1.3rem; }
 .is-profile .pp-streak { max-width:none;text-align:left; }
 .profile-next { align-items:stretch;flex-direction:column;padding:20px;gap:18px; }
 .profile-next>.btn { max-width:none;align-self:flex-start; }
 .profile-week-head { gap:12px;flex-wrap:wrap; }
 .profile-day { min-height:70px;padding:8px 2px; }
 .profile-day>span { font-size:.6875rem; }
 .profile-day b { font-size:1.15rem; }
 .pp-receipt,.daily-feedback>.pp-receipt { padding:14px;gap:10px; }
 #ov-key .panel { padding:18px 20px 24px; }
 #key-panel .rank-promotion { padding:0; }
 .rank-promotion .btnrow>.btn { flex:1 1 100%; }
}
@media(prefers-reduced-motion:reduce) {
 .pp-receipt,.pp-receipt *,.rank-promotion *,#ov-key.show .panel { animation:none!important;transition:none!important; }
}
body.motion-low .pp-receipt,body.motion-low .pp-receipt *,body.motion-low .rank-promotion * { animation:none!important;transition:none!important; }

.pp-overview { margin-top:20px;border-top:1px solid var(--rule); }
.pp-overview>summary { cursor:pointer;padding:14px 0 0;font:500 .8rem/1.5 var(--sans);color:var(--steel); }

@media(prefers-reduced-motion:no-preference) {
 body:not(.motion-low) .rank-promotion-seal { animation:ledger-seal 420ms var(--rank-ease) both; }
}
@media(max-width:360px) {
 .is-profile .pp-level { display:block;font-size:1.15rem; }
 .is-profile .pp-level-mark { width:44px;min-width:44px;height:48px;margin-bottom:10px; }
 .profile-week-head label { width:100%; }
}
/* Exercise progress and answer feedback use the same material vocabulary. */
.daily-meter progress { height:6px;border-radius:2px;accent-color:var(--rank-brass);background:var(--rank-track); }
.daily-meter progress::-webkit-progress-bar { background:var(--rank-track); }
.daily-meter progress::-webkit-progress-value { background:var(--rank-brass);transition:width 440ms var(--rank-ease); }
.daily-meter progress::-moz-progress-bar { background:var(--rank-brass); }
.daily-feedback,.daily-feedback.is-learning { padding:20px 0 0;border:0;border-top:1px solid var(--rule);border-radius:0;background:none; }
.daily-feedback>.pp-receipt { margin-bottom:16px; }
.daily-feedback .btn { box-shadow:none;border-radius:6px; }
#profile-dossier>.room-head::before { content:none; }
/* Translated account actions may wrap; they must never cover the wordmark. */
@media(max-width:600px) {
 body>header .bar { grid-template-columns:minmax(88px,max-content) minmax(0,1fr); }
 body>header .bar .nav-ident { min-width:0;flex-wrap:wrap;gap:4px; }
 body>header .bar .nav-ident button { min-width:0;max-width:100%;white-space:normal;overflow-wrap:anywhere;font-size:.75rem;line-height:1.3;letter-spacing:0; }
}
@media(max-width:400px) {
 .desk-paths { grid-template-columns:minmax(0,1fr); }
 .desk-paths>button { display:grid;grid-template-columns:80px minmax(0,1fr);align-items:center;gap:12px;min-height:58px; }
 .desk-paths span { overflow-wrap:normal; }
}

/* Show the destination before the distance: the next bookplate connects
   each earned receipt to the same illustrated collection. */
.pp-next-rank { display:flex;align-items:center;gap:10px;margin-bottom:10px; }
.pp-next-rank .pp-destination { margin:0;font-size:var(--type-meta,.875rem);line-height:1.45; }
.pp-next-mark { display:block;flex:0 0 36px;width:36px;height:42px; }
.pp-next-mark svg { display:block;width:100%;height:100%; }
.practice-progress .pp-meter { height:8px; }
.practice-progress .pp-meter>span { background:linear-gradient(90deg,var(--rank-brass),var(--signal,#f8c537)); }
.is-profile .pp-next-rank { padding:14px 16px;background:var(--rank-wash);border-radius:8px; }
.is-profile .pp-next-mark { flex-basis:52px;width:52px;height:58px; }
.is-profile .pp-level-mark { width:76px;min-width:76px;height:84px; }
.pp-rank-ladder ol { position:relative; }
.pp-rank-ladder li { position:relative;transition:background-color 180ms ease; }
.pp-rank-ladder li.is-earned { background:color-mix(in srgb,var(--rank-wash) 50%,var(--paper)); }
.pp-rank-ladder li.is-current { border-left:4px solid var(--rank-brass);box-shadow:0 3px 0 color-mix(in srgb,var(--rank-brass) 16%,transparent); }
.pp-rank-ladder li:not(.is-current):not(.is-earned) { color:var(--steel); }
.pp-rank-ladder li:hover { background:var(--rank-wash); }
#key-panel .rank-promotion-seal { width:112px;height:124px;padding:14px;border-radius:50%;background:var(--rank-wash);box-shadow:0 0 0 1px color-mix(in srgb,var(--rank-brass) 35%,transparent); }
@media(max-width:600px) {
 .pp-next-mark { flex-basis:28px;width:28px;height:34px; }
 .pp-next-rank { gap:7px; }
 .is-profile .pp-level-mark { width:60px;min-width:60px;height:66px; }
}
@media(prefers-reduced-motion:reduce) { .pp-rank-ladder li { transition:none; } }
body.motion-low .pp-rank-ladder li { transition:none; }


/* ===== 110-practice-stage.css ===== */
/* Practice is a page on the reading desk: a small bookplate, an ink rule,
   one exercise and an answer that becomes a receipt in the same place. */
#v-review { --practice-width:780px;overflow-anchor:none; }
#v-review #practice-rewards { max-width:var(--practice-width);margin:12px auto; }
#v-review #practice-rewards .practice-progress.is-compact {
 display:grid;grid-template-columns:minmax(0,1.2fr) minmax(0,1fr);gap:6px 24px;
 padding:12px 16px;border:0;border-bottom:1px solid var(--rule);border-radius:0;background:transparent;
}
#practice-rewards .is-compact .pp-heading { display:contents; }
#practice-rewards .is-compact .pp-level { grid-column:1;grid-row:1/3;gap:10px;font:500 .96rem/1.3 var(--serif); }
#practice-rewards .is-compact .pp-level>span:last-child { min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap; }
#practice-rewards .is-compact .pp-level-mark { width:34px;min-width:34px;height:42px; }
#practice-rewards .is-compact .pp-level small { font:500 var(--type-meta)/1.5 var(--sans); }
#practice-rewards .is-compact .pp-level small:last-child:not(:first-of-type) { display:none; }
#practice-rewards .is-compact .pp-track { grid-column:2;grid-row:1;align-self:center;margin:0; }
#practice-rewards .is-compact .pp-destination { display:block;font-size:var(--type-meta);line-height:1.4;margin:0; }
#practice-rewards .is-compact .pp-numbers { font-size:var(--type-meta);margin-top:5px;gap:2px 8px; }
#practice-rewards .is-compact .pp-numbers>span:last-child { display:none; }
#practice-rewards .is-compact .pp-streak { grid-column:2;grid-row:2;padding:0;font-size:var(--type-meta);line-height:1.4; }
#practice-rewards .is-compact .pp-session { grid-column:1/-1;padding:5px 0 0;border:0;font-size:var(--type-meta);min-height:20px; }
#v-review.practice-active #practice-rewards .pp-streak { display:none; }
#v-review.practice-active #practice-rewards .pp-session { grid-column:2;grid-row:2;padding:0;min-height:20px;gap:8px; }
#v-review.practice-active #practice-rewards .pp-session>span { display:block; }
#v-review #practice-welcome,.daily-adventure { max-width:var(--practice-width);margin-inline:auto; }
#v-review.daily-mode #practice-welcome { max-width:var(--practice-width);margin:8px auto 28px; }
#v-review .practice-modes { max-width:var(--practice-width);margin:8px auto 16px; }
#v-review .daily-top { padding:0 2px 8px;gap:12px; }
#v-review .daily-sound { min-height:40px;border-radius:6px;padding:7px 12px;background:transparent; }
#v-review.practice-active .daily-route { margin:4px 0 10px; }
#v-review.practice-active .daily-route li { font-size:var(--type-meta); }
#v-review.practice-active .daily-route li>span { width:22px;height:22px;box-shadow:none;font-size:var(--type-meta); }
#v-review .daily-meter { margin:8px 0 14px;gap:12px;font-variant-numeric:tabular-nums; }
#v-review .daily-task { position:relative;padding:24px 28px;max-width:none;border:1px solid var(--rule);border-radius:8px;box-shadow:0 4px 0 color-mix(in srgb,var(--rule) 40%,transparent);animation:practice-page-in 180ms ease-out;overflow-anchor:none; }
#v-review .daily-task::before { content:'';position:absolute;inset:0 auto 0 0;width:3px;background:var(--rank-brass);border-radius:8px 0 0 8px;opacity:.65; }
#v-review .daily-task-head { min-height:28px;gap:12px; }
#v-review .daily-task-head .smallcaps { font:600 var(--type-meta)/1.4 var(--sans);letter-spacing:.1em; }
#v-review .daily-mate { width:32px;height:36px; }
#v-review .daily-task h2 { font:500 var(--type-heading)/1.25 var(--serif);margin:8px 0 16px; }
#v-review .daily-line { font:500 var(--type-prompt)/1.45 var(--serif);padding:12px 16px;margin:12px 0 16px;border-radius:0;background:var(--rank-wash); }
#v-review .daily-source { margin:6px 0 12px; }
#v-review .daily-response-space { display:flex;flex-direction:column;min-height:210px;overflow-anchor:none; }
#v-review #daily-answer>p { font-size:var(--type-body);line-height:1.5; }
#v-review .daily-word-meaning { padding:10px 14px;border-radius:4px; }
#v-review .daily-choices { margin:12px 0 8px;gap:9px; }
#v-review .daily-choices button { min-height:48px;padding:12px 16px;border-radius:6px;box-shadow:0 2px 0 var(--rule);font-size:var(--type-control);transition:background 140ms,border-color 140ms; }
#v-review .daily-choices button:hover:not(:disabled) { transform:none; }
#v-review .daily-clue { min-height:40px;font-size:var(--type-meta); }
#v-review .daily-self-check { gap:10px; }
#v-review .daily-self-check .btn { flex:1; }
#v-review .daily-adventure .btn { border-radius:6px;padding:12px 18px;min-height:48px;font:600 var(--type-control)/1.35 var(--sans); }
#v-review .daily-adventure .btn.primary { background:var(--signal);color:var(--on-signal);border-color:var(--signal-ink);box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent); }
#v-review .daily-adventure .btn.primary:hover { background:color-mix(in srgb,var(--signal) 90%,var(--sheet));color:var(--on-signal);filter:none; }
#v-review .daily-adventure .btn.primary:active { transform:translateY(1px);box-shadow:0 1px 0 var(--signal-ink); }
#v-review .daily-feedback { display:flex;flex:1;flex-direction:column;gap:10px;margin:0;padding:0;border:0;animation:practice-ink-in 200ms ease-out; }
#v-review .daily-feedback h3 { font-size:1.1rem;margin:0; }
#v-review .daily-feedback .daily-correction { font:500 var(--type-prompt)/1.5 var(--serif);margin:0;padding:10px 14px;border-left:2px solid var(--rank-brass);background:var(--rank-wash);overflow-wrap:anywhere; }
#v-review .daily-feedback .daily-why { font:var(--type-body)/1.6 var(--sans);margin:0; }
#v-review .daily-explanation { font:var(--type-body)/1.6 var(--sans); }
#v-review .daily-explanation>summary { cursor:pointer;padding:10px 0;min-height:40px; }
#v-review #daily-next { align-self:stretch;margin:auto 0 0;flex:0 0 auto; }
#v-review .pp-receipt { display:flex;align-items:center;gap:12px;padding:12px 14px;margin:0;border:0;border-radius:5px;background:var(--rank-wash); }
#v-review .pp-receipt-mark { width:38px;min-width:38px;height:44px; }
#v-review .pp-receipt-copy { flex:1;min-width:0; }
#v-review .pp-receipt-copy>strong { font:500 1.05rem/1.3 var(--serif); }
#v-review .pp-receipt-copy>span { display:block;margin-top:3px; }
#v-review .pp-receipt-progress { display:none; }
#v-review .pp-receipt.is-milestone { box-shadow:inset 0 0 0 1px var(--rank-brass); }
#v-review .pp-receipt.is-live.is-independent .pp-receipt-mark { animation:practice-seal 380ms var(--rank-ease); }
#v-review .practice-answer-glow { animation:practice-ink-in 200ms ease-out; }
#v-review .daily-invitation { border-radius:var(--learning-radius);border-top:3px solid var(--pine);padding:24px;gap:24px;grid-template-columns:minmax(0,1fr) 160px;box-shadow:0 3px 0 var(--rule); }
#v-review .daily-copy h2 { font-size:clamp(1.4rem,3vw,2rem); }
#v-review .daily-copy>p { font-size:var(--type-body);line-height:1.6; }
#v-review .daily-landscape { width:160px; }
#v-review .practice-invitation { border-radius:var(--learning-radius);border-top:3px solid var(--pine); }
#v-review:not(.practice-lobby):not(.practice-finished) #practice-workbench { min-height:0;max-width:var(--practice-width);margin-inline:auto; }
#v-review:not(.practice-lobby):not(.practice-finished) #rev-card { flex:0 0 auto; }
#v-review:not(.practice-lobby):not(.practice-finished) #rev-btns { min-height:60px;margin:10px 0 0;padding:8px 0; }
#v-review #practice-answer-reward:not([hidden]) { margin-top:12px; }
#v-review .rev-head { max-width:var(--practice-width);margin-inline:auto; }
#v-review .practice-format { margin:12px auto; }
#v-review :is(.daily-task,#practice-workbench) :focus-visible { outline:3px solid var(--rank-brass);outline-offset:4px; }
@keyframes practice-page-in { from{opacity:.65}to{opacity:1} }
@keyframes practice-ink-in { from{opacity:.4}to{opacity:1} }
@keyframes practice-seal { 0%{transform:scale(.86) rotate(-5deg)}65%{transform:scale(1.04) rotate(1deg)}100%{transform:none} }
@media(max-width:600px) {
 body:has(#v-review.show.practice-active)>header .nav-ident #nav-signup { display:none; }
 body:has(#v-review.show.practice-active)>header .bar .nav-rooms { flex-wrap:wrap;gap:2px; }
 body:has(#v-review.show.practice-active)>header .bar .nav-rooms button { flex:1 1 auto;padding-inline:4px;font-size:var(--type-meta);white-space:normal; }
 #v-review #practice-rewards .practice-progress.is-compact { padding:8px 0;gap:4px 12px;grid-template-columns:minmax(0,1.3fr) minmax(0,1fr); }
 #practice-rewards .is-compact .pp-level { font-size:var(--type-meta);gap:7px; }
 #practice-rewards .is-compact .pp-level-mark { width:28px;min-width:28px;height:34px; }
 #v-review .daily-task { padding:16px; }
 #v-review .daily-task h2 { margin:6px 0 12px; }
 #v-review .daily-invitation { grid-template-columns:1fr;gap:12px;padding:20px; }
 #v-review .daily-landscape { grid-row:auto;justify-self:end;width:100px;margin:0; }
 #v-review .daily-copy>p { margin-bottom:12px; }
 #v-review .daily-invitation .daily-route { margin:16px 0; }
 #v-review .daily-task-head .smallcaps { font-size:var(--type-meta); }
 #v-review #daily-form { margin:12px 0;gap:8px; }
 #v-review .daily-tile-target { margin:12px 0 8px; }
 #v-review .daily-tile-target,#v-review .daily-tiles { min-height:54px;gap:6px;padding:10px; }
 #v-review .daily-tile-target button,#v-review .daily-tiles button { font-size:1rem;padding:8px; }
}
@media(max-height:520px) and (min-width:600px) {
 #v-review .daily-task { padding:16px 24px; }
 #v-review .daily-task-head { min-height:24px; }
 #v-review .daily-task h2 { margin:4px 0 10px; }
}
@media(prefers-reduced-motion:reduce) {
 #v-review *,#v-review *::before,#v-review *::after { animation:none!important;transition:none!important;scroll-behavior:auto!important; }
}
body.motion-low #v-review *,body.motion-low #v-review *::before,body.motion-low #v-review *::after { animation:none!important;transition:none!important; }

#v-review.practice-active #practice-rewards { position:static;background:var(--paper);box-shadow:0 8px 12px -12px var(--carbon); }
#v-review .practice-start-actions .btn.primary,#v-review #rev-btns .primary { background:var(--signal);color:var(--on-signal);border-color:var(--signal-ink);border-radius:6px;box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent); }
#v-review #daily-answer { flex:1;display:flex;flex-direction:column; }
#v-review #daily-answer>#daily-reveal { margin-top:auto; }
#v-review #daily-answer>.btn { flex:0 0 auto; }

/* The word reviser uses the same paper, controls and readable type as the mix. */
#v-review.practice-active>.rev-head { display:none!important; }
#v-review #practice-workbench { width:100%;box-sizing:border-box; }
#v-review #rev-card { width:100%;min-width:0;box-sizing:border-box;padding:24px 28px;border-radius:8px;border:1px solid var(--rule);border-left:3px solid var(--rank-brass);background:var(--paper);box-shadow:0 4px 0 color-mix(in srgb,var(--rule) 40%,transparent); }
#v-review #rev-card .cloze,#v-review #rev-card .ex-main { font:500 var(--type-prompt)/1.6 var(--serif);padding:12px 16px;margin:16px 0;border:0;border-left:2px solid var(--rank-brass);border-radius:0;box-shadow:none;background:var(--rank-wash);overflow-wrap:anywhere; }
#v-review #rev-card .conf-row { display:flex;flex-wrap:wrap;justify-content:center;gap:8px;max-width:100%; }
#v-review #rev-card .conf-row button,#v-review #rev-btns button { flex:0 1 auto;min-width:0;min-height:44px;padding:10px 14px;border-radius:6px;border:1px solid var(--rule);background:var(--paper);color:var(--carbon);font:600 var(--type-control)/1.4 var(--sans);letter-spacing:0;text-transform:none;white-space:normal;box-shadow:0 2px 0 var(--rule); }
#v-review #rev-card input { width:100%;max-width:100%;min-width:0;box-sizing:border-box;border:1px solid var(--rule);border-radius:6px;padding:12px;font:500 1.25rem/1.4 var(--serif); }
#v-review #rev-card input:focus { outline:2px solid var(--rank-brass);outline-offset:2px; }
#v-review #rev-btns { flex-wrap:wrap;gap:10px; }
#v-review #rev-btns .primary,#v-review #rev-btns .primary:hover { flex:1 1 100%;background:var(--signal);color:var(--on-signal);border-color:var(--signal-ink); }
#v-review #rev-card #rev-check { background:var(--signal);color:var(--on-signal)!important;border-color:var(--signal-ink)!important;box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent); }
#v-review #rev-card #rev-check:hover:not(:disabled) { background:color-mix(in srgb,var(--signal) 90%,var(--sheet));color:var(--on-signal)!important;filter:none; }
#v-review #rev-card #rev-check:active:not(:disabled) { box-shadow:0 1px 0 var(--signal-ink); }
#v-review #rev-card .reveal-word { font-size:clamp(1.7rem,5vw,2.3rem);line-height:1.3;overflow-wrap:anywhere; }
#v-review #rev-card .opt { border-radius:6px;box-shadow:0 2px 0 var(--rule);font:500 1rem/1.5 var(--serif);padding:12px 16px;min-height:48px; }
#v-review #practice-position { font:500 var(--type-meta)/1.4 var(--sans);margin:8px 0;color:var(--steel); }
@media(max-width:600px) { #v-review #rev-card { padding:16px; } #v-review #rev-card .conf-row button { padding:8px 10px; } }
@media(max-height:520px) { #v-review.practice-active #practice-rewards { position:static; } }

/* Recorded pronunciation stays beside the prompt, also after its answer. */
#v-review .practice-listening { display:flex;flex-wrap:wrap;align-items:center;gap:8px 14px;margin:12px 0 18px; }
#v-review .practice-listening .daily-listen { min-height:44px;padding:9px 16px;font:600 var(--type-meta)/1.4 var(--sans); }
#v-review .practice-listening small { flex-basis:100%;font:400 var(--type-meta)/1.5 var(--sans);color:var(--steel); }

/* A shared route palette: reading ink, learning pine, practice brass.
   Washes distinguish destinations while all actions retain house gold. */
#focus-read,.desk-host[data-journey="read"] { --journey-ink:var(--play); }
#focus-learn,.desk-host[data-journey="learn"] { --journey-ink:var(--pine); }
#focus-practice,.desk-host[data-journey="practice"] { --journey-ink:var(--rank-brass); }
.desk-paths>button {
 position:relative;border-top:3px solid var(--journey-ink);padding-bottom:26px;
 background:color-mix(in srgb,var(--journey-ink) 6%,var(--paper));
 transition:transform 180ms ease,box-shadow 180ms ease,background-color 180ms ease;
}
.desk-paths>button::after { content:'→';position:absolute;right:12px;bottom:5px;color:var(--journey-ink);font-size:1.15rem; }
.desk-paths>button:is(:hover,:focus-visible) {
 border-color:var(--journey-ink);background:color-mix(in srgb,var(--journey-ink) 11%,var(--paper));
 box-shadow:0 4px 0 color-mix(in srgb,var(--journey-ink) 18%,transparent);transform:translateY(-2px);
}
#v-home.desk-focused .desk-host[data-journey] {
 border-top-color:var(--journey-ink);
 background:linear-gradient(120deg,color-mix(in srgb,var(--journey-ink) 7%,var(--paper)),var(--paper) 65%);
}
#v-review .daily-route li.is-current { color:var(--rank-brass); }
#v-review .daily-completion-summary {
 border-top:3px solid var(--rank-brass);
 background:linear-gradient(135deg,var(--rank-wash),var(--paper) 55%);
}
#v-review .daily-completion-summary .daily-more {
 padding:14px 18px;border:1px solid var(--rule);border-left:3px solid var(--pine);
 border-radius:8px;background:color-mix(in srgb,var(--pine) 5%,var(--paper));
}
#v-review .daily-more>summary { min-height:32px;font:600 var(--type-body)/1.5 var(--sans); }
#v-review .daily-finish-actions>.btn { min-height:48px; }
@media(max-width:600px) {
 .desk-paths>button { padding-right:28px; }
 .desk-paths>button::after { bottom:10px; }
}
@media(prefers-reduced-motion:reduce) { .desk-paths>button { transition:none;transform:none!important; } }
body.motion-low .desk-paths>button,body.calm .desk-paths>button { transition:none;transform:none!important; }

#v-review .daily-route li.is-current>span { outline:2px solid var(--rank-brass);outline-offset:3px; }
#v-review .daily-route li.is-done>span { background:var(--rank-wash);color:var(--rank-brass);border-color:var(--rank-brass); }
#v-course .course-continuation { border-top:3px solid var(--pine); }
#v-course .course-continuation-copy { background:linear-gradient(135deg,color-mix(in srgb,var(--pine) 5%,var(--paper)),var(--paper) 70%); }

/* Secondary pigments are reserved for destination cues, never long prose.
   Gold continues to mean the primary action; brass continues to mean XP. */
:root { --study-teal:#326e69;--study-plum:#69517d; }
[data-theme="dark"] { --study-teal:#8dc8bd;--study-plum:#c7afe0; }
#focus-learn,.desk-host[data-journey="learn"] { --journey-ink:var(--study-teal); }
#v-course .course-continuation { border-top-color:var(--study-teal); }
#v-course .course-volume-heading { background:color-mix(in srgb,var(--study-teal) 8%,var(--paper));color:var(--study-teal); }
#v-course .course-continuation-copy { background:var(--paper); }
#v-course .course-facing-page { background:color-mix(in srgb,var(--study-teal) 5%,var(--sheet)); }
#v-course .course-volume-progress progress::-webkit-progress-value { background:var(--study-teal); }
#v-course .course-volume-progress progress::-moz-progress-bar { background:var(--study-teal); }
#v-words>.study-orientation {
 padding:14px 18px;margin:12px auto 18px;border-left-color:var(--study-plum);
 background:color-mix(in srgb,var(--study-plum) 5%,var(--paper));box-shadow:none;
}
#v-words .study-orientation .study-suggestion { flex-direction:row;align-items:center;gap:12px; }
#v-words .study-orientation .study-suggestion strong { display:block;font:500 1.1rem/1.4 var(--serif);margin:4px 0 0; }
#v-words .study-orientation .smallcaps { color:var(--study-plum);font-size:.75rem; }
#v-words .study-orientation .study-suggestion>.btn {
 width:auto;min-height:44px;padding:8px 12px;background:var(--paper);border:1px solid var(--study-plum);
 box-shadow:none;color:var(--carbon);font-size:var(--type-meta);flex-shrink:1;
}
#wordlist .wl-cap { border-bottom-color:color-mix(in srgb,var(--study-plum) 30%,var(--rule));color:var(--study-plum); }
#wordlist li:not(.wl-cap):focus-within { background:color-mix(in srgb,var(--study-plum) 6%,var(--paper)); }
#word-filters button.active { border-bottom-color:var(--study-plum); }
#v-review .daily-completion-summary .daily-more { border-left-color:var(--study-teal);background:color-mix(in srgb,var(--study-teal) 5%,var(--paper)); }
@media(max-width:380px) {
 #v-words .study-orientation .study-suggestion { flex-wrap:wrap; }
 #v-words .study-orientation .study-suggestion>.btn { width:100%; }
}


/* ===== 111-study-delight.css ===== */
/* A living printer's mark: the house resident, five leaves of a run and the
   actual rank seal. Geometry is reserved; only the ink and character move. */
.study-delight-slot { min-width:0;min-height:80px;contain:layout style; }
.study-delight-slot[hidden] { display:none!important; }
.study-delight {
 --sd-ink:var(--play,#244177);--sd-tint:color-mix(in srgb,var(--sd-ink) 8%,var(--paper));
 display:flex;align-items:center;gap:18px;min-height:80px;padding:0 6px;
 border-bottom:1px solid var(--rule);color:var(--sd-ink);isolation:isolate;
}
.sd-companion { position:relative;display:block;flex:0 0 88px;width:88px;height:92px;transform-origin:50% 86%; }
.sd-companion::before { content:'';position:absolute;left:8px;right:8px;bottom:5px;height:6px;border-radius:50%;background:color-mix(in srgb,var(--sd-ink) 12%,transparent);z-index:-1; }
.sd-seat { display:block;width:88px;height:88px; }
.sd-fanfare { position:absolute;inset:0;pointer-events:none;z-index:-1; }
.sd-ray { position:absolute;left:50%;top:50%;width:4px;height:10px;margin:-5px -2px;background:var(--rank-brass);border-radius:2px;opacity:0;transform:rotate(calc(var(--ray) * 45deg)) translateY(-42px); }
.sd-ray:nth-child(even) { background:var(--play);height:6px; }
.study-delight:not([data-energy="0"]) .sd-seat { border-radius:50%;background:color-mix(in srgb,var(--rank-brass) 8%,transparent);box-shadow:0 0 0 2px color-mix(in srgb,var(--rank-brass) 24%,transparent); }
.study-delight:not([data-energy="0"]) { --sd-ink:var(--rank-brass); }
.study-delight[data-energy="2"] .sd-seat { box-shadow:0 0 0 3px color-mix(in srgb,var(--rank-brass) 40%,transparent); }
.study-delight[data-energy="3"] .sd-seat { box-shadow:0 0 0 3px var(--rank-brass),0 0 0 7px color-mix(in srgb,var(--rank-brass) 12%,transparent); }
.sd-seat>svg { display:block;width:100%;height:100%;overflow:visible; }
.sd-rank { position:absolute;right:0;bottom:5px;display:grid;place-items:center;width:25px;height:28px;box-sizing:border-box;border:1px solid var(--rank-brass);border-radius:3px 3px 9px 9px;background:var(--paper);color:var(--rank-brass);font:600 var(--type-meta,.875rem)/1 var(--sans);font-variant-numeric:tabular-nums; }
.sd-ledger { min-width:0;flex:1;display:flex;flex-direction:column;justify-content:center;gap:6px; }
.sd-marks { display:flex;align-items:flex-end;gap:5px;height:29px; }
.sd-mark { position:relative;display:block;width:17px;height:23px;box-sizing:border-box;background:var(--sd-tint);border:1px solid color-mix(in srgb,var(--sd-ink) 24%,var(--rule));border-radius:1px 4px 1px 1px;transform-origin:50% 100%; }
.sd-mark::after { content:'';position:absolute;left:3px;right:3px;bottom:5px;height:1px;background:var(--sd-ink);opacity:.2;box-shadow:0 -4px 0 currentColor; }
.sd-mark:nth-child(2),.sd-mark:nth-child(4) { height:25px; }
.sd-mark:nth-child(3) { height:28px; }
.sd-mark.is-filled { background:var(--sd-ink);border-color:var(--sd-ink); }
.sd-mark.is-filled::after { background:var(--paper);color:var(--paper);opacity:.6; }
.sd-label { min-height:1.4em;color:var(--sd-ink);font:600 var(--type-meta,.875rem)/1.4 var(--sans);font-variant-numeric:tabular-nums; }
.sd-round { align-self:center;color:var(--steel);font:500 var(--type-meta,.875rem)/1.4 var(--sans);font-variant-numeric:tabular-nums;white-space:nowrap; }
[data-theme="dark"] .study-delight { --sd-ink:#becbed; }
.study-delight[data-state="supported"],.study-delight[data-state="retry"] { --sd-ink:var(--steel); }
.study-delight:is([data-state="run"],[data-state="milestone"],[data-state="complete"]) { --sd-ink:var(--rank-brass);border-bottom-color:var(--rank-brass); }
.study-delight:is([data-state="run"],[data-state="milestone"]) .sd-rank { color:var(--paper);background:var(--rank-brass); }
.study-delight[data-state="complete"] .sd-ledger { gap:8px; }
.study-delight[data-state="complete"] .sd-round { font:500 1.6rem/1 var(--serif);color:var(--rank-brass); }

/* The resident stays with Continue, following the answer and its receipt. */
#v-review #daily-delight { margin:0 0 12px; }
#v-review .daily-feedback #daily-delight { margin:auto 0 0; }
#v-review .daily-feedback:has(.study-delight) #daily-next { margin-top:0; }
#v-review #word-delight { margin:8px 0 0; }
#v-review:has(.study-delight) #rev-btns { position:static;bottom:auto; }
#v-review.practice-active:has(.study-delight) #practice-rewards .pp-session {
 position:absolute;width:1px;height:1px;min-height:0;padding:0;overflow:hidden;
 clip-path:inset(50%);white-space:nowrap;
}
#v-review:has(.study-delight) .pp-receipt-mark { display:none; }
#v-review:has(.study-delight) .pp-receipt { min-height:50px;padding:9px 12px; }
#v-review:has(.study-delight) .pp-receipt-copy { display:flex;align-items:center;justify-content:space-between;gap:14px; }
#v-review:has(.study-delight) .pp-receipt-copy>strong { font:500 .95rem/1.35 var(--serif); }
#v-review:has(.study-delight) .pp-receipt-copy>span { margin:0;flex-shrink:0;font:600 .8rem/1.4 var(--sans); }
#v-review:has(.study-delight[data-state="run"]) .daily-task::before,
#v-review:has(.study-delight[data-state="milestone"]) .daily-task::before { width:4px;opacity:1; }
#v-review .daily-flow-finish:has(.study-delight) { grid-template-columns:1fr;gap:16px; }
#v-review .daily-flow-finish .is-completion { grid-column:1/-1;width:100%;margin:0; }
#v-review .daily-flow-finish .study-delight { min-height:110px;gap:24px;padding:0 12px; }
#v-review .daily-flow-finish .sd-companion { width:96px;height:104px;flex-basis:96px; }
#v-review .daily-flow-finish .sd-seat { width:96px;height:100px; }
#v-review .daily-flow-finish .sd-mark { width:22px; }
#v-review .daily-flow-finish .daily-flow-copy { min-width:0; }
#v-review .daily-flow-finish .daily-flow-copy h2 { margin:4px 0 12px; }
#v-review .daily-flow-finish .daily-finish-actions { grid-column:1/-1; }
#v-review .daily-finish-actions .primary { flex:1 1 100%; }
#v-review.practice-finished:has(.study-delight) .rev-head { display:none!important; }

@media(max-width:420px) {
 .study-delight { gap:12px;padding-inline:0; }
 .sd-mark { width:15px; }
 .sd-marks { gap:4px; }
 #v-review:has(.study-delight) .pp-receipt-copy { gap:8px;flex-wrap:wrap; }
 #v-review:has(.study-delight) .pp-receipt-copy>span { font-size:var(--type-meta,.875rem); }
}
/* A real second column uses the spare desktop measure. The DOM controller
   moves the one live rail here and restores its exact inline position on
   smaller screens; neither task nor controls sit underneath the character. */
@media(min-width:1200px) {
 .sd-with-companion { display:grid;grid-template-columns:minmax(0,1fr) 196px;column-gap:32px;align-items:start; }
 .sd-with-companion>* { grid-column:1;min-width:0; }
 .sd-with-companion>.sd-side {
  grid-column:2;grid-row:var(--sd-side-row,1);align-self:start;
  position:sticky;top:24px;width:196px;min-width:0;box-sizing:border-box;margin:0!important;contain:style;
 }
 .sd-side .study-delight {
  flex-direction:column;align-items:center;gap:16px;padding:12px 14px 20px;
  border:0;border-left:1px solid var(--rule);text-align:center;min-height:0;
 }
 .sd-side .sd-companion { width:168px;height:176px;flex-basis:auto;--sd-orbit:80px; }
 .sd-side .sd-seat { width:168px;height:168px; }
 .sd-side .sd-rank { width:30px;height:34px;right:6px;bottom:8px; }
 .sd-side .sd-ledger { flex:none;align-items:center;gap:10px; }
 .sd-side .sd-marks { gap:7px;height:32px; }
 .sd-side .sd-mark { width:19px;height:27px; }
 .sd-side .sd-mark:nth-child(2),.sd-side .sd-mark:nth-child(4) { height:29px; }
 .sd-side .sd-mark:nth-child(3) { height:32px; }
 .sd-side .sd-round { font-size:var(--type-meta,.875rem); }
 #v-review.daily-mode #practice-welcome:has(.sd-with-companion) { max-width:1040px; }
 #v-review .daily-adventure.sd-with-companion { max-width:1040px;column-gap:32px; }
 #v-review .daily-adventure.sd-with-companion>.daily-task { width:100%;margin:0; }
 #v-review>div:has(>#practice-workbench.sd-with-companion) { max-width:1040px!important; }
 #v-review #practice-workbench.sd-with-companion { display:grid;max-width:1040px;grid-template-columns:minmax(0,1fr) 196px;column-gap:32px; }
 #v-review #practice-workbench.sd-with-companion>#rev-card { width:100%;max-width:none; }
 #v-review .sd-with-companion>.sd-side { top:calc(var(--practice-header-top,76px) + 20px); }
 #v-review #practice-workbench.sd-with-companion>.sd-side { grid-row:1 / span 3; }
 #v-review .sd-side.is-completion .study-delight { gap:16px;padding:12px 14px 20px;min-height:0; }
}
@media(prefers-reduced-motion:reduce) {
 .study-delight,.study-delight * { animation:none!important;transition:none!important; }
}
body.motion-low .study-delight,body.motion-low .study-delight * { animation:none!important;transition:none!important; }

/* Earned color belongs to the run, not the transient answer celebration.
   These shared tokens carry through next questions, rounds and lessons. */
.study-delight[data-streak="warm"],
.study-delight[data-streak="glowing"],
.study-delight[data-streak="flourishing"] {
 --sd-ink:var(--rank-brass);--sd-accent:var(--signal,#f8c537);
 border-color:color-mix(in srgb,var(--sd-ink) 35%,var(--rule));
}
.study-delight[data-streak="glowing"] { --sd-accent:var(--rank-brass); }
.study-delight[data-streak="flourishing"] { --sd-accent:var(--play,#244177); }
.study-delight:not([data-streak="starting"]) .sd-seat {
 border-radius:50%;background:color-mix(in srgb,var(--sd-accent) 12%,transparent);
 box-shadow:0 0 0 2px color-mix(in srgb,var(--sd-ink) 22%,transparent);
}
.study-delight[data-streak="glowing"] .sd-seat {
 box-shadow:0 0 0 3px color-mix(in srgb,var(--sd-ink) 42%,transparent);
}
.study-delight[data-streak="flourishing"] .sd-seat {
 box-shadow:0 0 0 3px var(--sd-ink),0 0 0 7px color-mix(in srgb,var(--sd-accent) 16%,transparent);
}
.study-delight:not([data-streak="starting"]) .sd-rank { background:var(--sd-ink);color:var(--paper); }
.study-delight[data-streak="glowing"] .sd-mark.is-filled,
.study-delight[data-streak="flourishing"] .sd-mark.is-filled {
 box-shadow:inset 0 3px 0 var(--signal,#f8c537);
}
/* Later lesson styles may choose a room ink; earned streaks take precedence. */
.living-delight .study-delight:not([data-streak="starting"]) { --sd-ink:var(--rank-brass); }
[data-theme="dark"] .study-delight:not([data-streak="starting"]),
[data-theme="dark"] .living-delight .study-delight:not([data-streak="starting"]) { --sd-ink:var(--signal,#f8c537); }


/* ===== 112-learning-delight.css ===== */
/* The lesson guide belongs to the hand's next action, not a distant header.
   The response rail is ordinary flow: it never floats over a line or button. */
.gx-stage.has-study-delight .lesson-delight {
  display:grid; grid-template-columns:minmax(0,1fr); align-items:center; gap:14px;
  min-height:88px; min-width:0; width:100%; box-sizing:border-box;
  padding:8px 14px 8px 8px; margin:0;
  border-block:1px solid var(--rule); background:var(--paper);
  border-radius:0; position:relative; isolation:isolate;
}
.gx-stage.has-study-delight .lesson-delight.has-guide { grid-template-columns:88px minmax(0,1fr); }
.gx-stage.has-study-delight .lesson-delight-actor {
  display:none; position:relative; width:88px; height:88px;
  border-radius:50%; background:color-mix(in srgb,var(--world-deep) 8%,var(--paper));
  overflow:visible; pointer-events:none;
}
.gx-stage.has-study-delight .has-guide .lesson-delight-actor { display:block; }
.gx-stage.has-study-delight .lesson-delight-actor .gx-mate {
  display:grid!important; place-items:center; position:absolute!important;
  inset:0!important; width:88px!important; height:88px!important;
  min-width:0; margin:0!important; padding:0!important; border:0!important;
  background:transparent!important; box-shadow:none!important; transform:none!important;
  overflow:visible; z-index:1; pointer-events:none;
}
.gx-stage.has-study-delight .lesson-delight-actor .gx-mate > svg {
  width:100%!important; height:100%!important; max-width:none; overflow:visible;
  filter:none;
}
.gx-stage.has-study-delight .lesson-delight-actor .gx-mate::before,
.gx-stage.has-study-delight .lesson-delight-actor .gx-mate::after,
.gx-stage.has-study-delight .lesson-delight-actor :is(.gx-mate-line,.gx-slip) { display:none!important; }
.gx-stage.has-study-delight .lesson-delight-ledger { min-width:0; }
.gx-stage.has-study-delight .lesson-delight[hidden] { display:none!important; }
.gx-stage.has-study-delight .lesson-guide-still { display:none; position:absolute; inset:2px; }
.gx-stage.has-study-delight .lesson-guide-still > svg { width:100%; height:100%; }
.gx-stage.has-study-delight .lesson-delight.is-still .gx-mate { display:none!important; }
.gx-stage.has-study-delight .lesson-delight.is-still .lesson-guide-still { display:block; }
.gx-stage.has-study-delight .lesson-delight:has(.lesson-delight-ledger[hidden]) { display:none; }
.gx-stage.has-study-delight .lesson-delight .study-delight {
  min-height:64px; padding:8px 0; background:transparent; border:0; box-shadow:none;
  grid-template-columns:minmax(0,1fr) auto;
}
.gx-stage.has-study-delight .lesson-delight .sd-ledger { min-width:0; }
.gx-stage.has-study-delight .lesson-delight .sd-marks { max-width:150px; }
.gx-stage.has-study-delight .lesson-delight .sd-label { min-height:1.4em; }
.gx-stage.has-study-delight .lesson-delight .sd-round { font-size:var(--type-meta,.875rem); white-space:nowrap; }
.gx-stage.has-study-delight #gx-after { display:grid; align-items:start; }
.gx-stage.has-study-delight #gx-after > .lesson-delight { order:8; }
.gx-stage.has-study-delight #gx-after > #gx-next { order:9; }
.gx-stage.has-study-delight .btnrow > .lesson-delight { flex-basis:100%; grid-column:1/-1; }
.gx-stage.has-study-delight .wk-footer { display:grid; gap:12px; }
.gx-stage.has-study-delight :is(#gx-next,#wk-next) {
  min-height:50px; width:100%; margin:0; border-radius:8px;
  font:700 1rem/1.3 var(--sans); text-transform:none; letter-spacing:0;
}
/* The older game skin uses !important navy buttons. Keep the study action
   in the same yellow signal role as Practice, including hover and press. */
.gx-stage.has-study-delight :is(.btn.primary,#gx-next,#wk-next,#gx-got,#gx-check) {
  background:var(--signal)!important;color:var(--on-signal)!important;
  border:1px solid var(--signal-ink);border-radius:6px;
  box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent)!important;
  font:600 var(--type-control,1rem)/1.5 var(--sans);text-transform:none;letter-spacing:0;
}
.gx-stage.has-study-delight :is(.btn.primary,#gx-next,#wk-next,#gx-got,#gx-check):hover {
  background:color-mix(in srgb,var(--signal) 90%,var(--sheet))!important;
  box-shadow:0 3px 0 color-mix(in srgb,var(--signal-ink) 65%,transparent)!important;transform:none;
}
.gx-stage.has-study-delight :is(.btn.primary,#gx-next,#wk-next,#gx-got,#gx-check):active {
  box-shadow:0 1px 0 var(--signal-ink)!important;transform:translateY(1px);
}
.gx-stage.has-study-delight .gx-eye { min-height:44px; }
.gx-stage.has-study-delight .gx-eye span { font-size:var(--type-body,1rem); }
.gx-stage.has-study-delight .gx-eye-cue { font:500 var(--type-meta,.875rem)/1.5 var(--sans);text-transform:none;letter-spacing:0; }
/* The changing run is legible in the response rail. Retire the duplicate
   hidden-header chip; its existing evidence still drives the real guide. */
.gx-stage.has-study-delight .gx-top .gx-runn { display:none; }
.gx-stage.has-study-delight .lesson-compass {
  grid-template-columns:62px minmax(0,1fr); gap:14px; padding:12px 16px;
  border-radius:8px; background:var(--paper); border-bottom-width:1px;
}
.gx-stage.has-study-delight .lesson-window.lesson-emblem { min-height:56px; height:56px; max-height:56px; }
.gx-stage.has-study-delight .lesson-emblem > .brand-art { width:52px; height:52px; opacity:1; }
.gx-stage.has-study-delight .lesson-compass h2 { font:600 clamp(1.15rem,2vw,1.5rem)/1.2 var(--serif); }
.gx-stage.has-study-delight .lesson-phase { font-size:var(--type-meta,.875rem); }
.gx-stage.has-study-delight .lesson-progress { gap:5px; font-size:var(--type-meta,.875rem); }
.gx-stage.has-study-delight .lesson-compass-copy { gap:5px; }
/* Rules remain one disclosure away; the visible challenge is a small ink
   inventory. No repeated pep paragraph stands between a learner and a task. */
.gx-stage.has-study-delight .ink-challenge {
  display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:center;
  gap:0 14px; padding:6px 12px; border:1px solid var(--rule);
  border-left:3px solid var(--world-deep); border-radius:4px;
  background:var(--paper); font-size:var(--type-meta,.875rem);
}
.gx-stage.has-study-delight .ink-challenge-heading { grid-column:1; gap:12px; font-size:var(--type-meta,.875rem); }
.gx-stage.has-study-delight .ink-drops { gap:3px; }
.gx-stage.has-study-delight .ink-drops svg { width:13px; height:19px; }
.gx-stage.has-study-delight .ink-rules { grid-column:2; margin:0; font-size:var(--type-meta,.875rem); }
.gx-stage.has-study-delight .ink-rules summary { min-height:32px; display:flex; align-items:center; }
.gx-stage.has-study-delight .ink-rules[open] { grid-column:1/-1; }
.gx-stage.has-study-delight .ink-rules[open] summary { margin-top:4px; }
.gx-stage.has-study-delight .ink-message,
.gx-stage.has-study-delight .challenge-earned {
  position:absolute; width:1px; height:1px; padding:0; margin:-1px!important;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}
.gx-stage.has-study-delight .ink-tally,
.gx-stage.has-study-delight .wk-encouragement,
.gx-stage.has-study-delight .wk-feedback-symbol { display:none; }
.gx-stage.has-study-delight .wk-feedback-heading { margin-bottom:12px; }
.gx-stage.has-study-delight .gx-verdict:is(.gx-ok,.gx-no,.gx-supported) {
  font:600 var(--type-body,1rem)/1.5 var(--sans);text-transform:none;letter-spacing:0;
  padding:12px 14px;min-height:0;margin:0;border:0;border-left:2px solid var(--rank-brass);
  border-radius:5px;background:color-mix(in srgb,var(--rank-brass) 8%,var(--paper));
  color:var(--carbon);box-shadow:none;
}
.gx-stage.has-study-delight .gx-verdict .gx-v-say {
  font:500 var(--type-prompt,1.375rem)/1.4 var(--serif);text-transform:none;letter-spacing:0;color:var(--carbon);
}
.gx-stage.has-study-delight .gx-verdict .gx-v-gloss {
  font:400 var(--type-body,1rem)/1.5 var(--sans);text-transform:none;letter-spacing:0;color:var(--steel);
}
.gx-stage.has-study-delight .wk-sheet[data-phase="question"] .wk-journey { display:none; }
.gx-stage.has-study-delight .wk-sheet[data-phase="question"] .wk-page-slots { display:none; }
.gx-stage.has-study-delight .wk-sheet .wk-body { border-radius:10px; }
/* An earned impression spreads out from the guide. It is one finite gesture,
   never a looping demand for attention, and never changes layout geometry. */
.gx-stage.has-study-delight .lesson-delight-actor::after {
  content:""; position:absolute; inset:3px; border:2px solid var(--rank-brass,var(--g-gold));
  border-radius:50%; opacity:0; pointer-events:none;
}
.gx-stage.has-study-delight .lesson-delight[data-run="earned"] .lesson-delight-actor {
  background:color-mix(in srgb,var(--rank-brass,var(--g-gold)) 15%,var(--paper));
}
@media(prefers-reduced-motion:no-preference) {
  body.motion-full .lesson-delight[data-run="earned"] .lesson-delight-actor::after { animation:lesson-impression 640ms cubic-bezier(.18,.7,.25,1) both; }
}
@keyframes lesson-impression {
  0% { opacity:.7; transform:scale(.72); }
  75% { opacity:.28; transform:scale(1.18); }
  100% { opacity:0; transform:scale(1.3); }
}
@media(max-width:560px) {
  .gx-stage.has-study-delight .lesson-compass { grid-template-columns:44px minmax(0,1fr); padding:10px 12px; gap:10px; }
  .gx-stage.has-study-delight .lesson-window.lesson-emblem { min-height:42px; height:42px; max-height:42px; }
  .gx-stage.has-study-delight .lesson-emblem > .brand-art { width:40px; height:40px; }
  .gx-stage.has-study-delight .lesson-delight { min-height:76px; gap:10px; padding:5px 8px 5px 0; }
  .gx-stage.has-study-delight .lesson-delight.has-guide { grid-template-columns:88px minmax(0,1fr); }
  .gx-stage.has-study-delight .lesson-delight .study-delight { min-height:58px; gap:8px; }
  .gx-stage.has-study-delight .ink-challenge { column-gap:8px; }
  .gx-stage.has-study-delight .ink-challenge-heading { font-size:var(--type-meta,.875rem); gap:6px; }
}
@media(min-width:1200px) {
 .gx-stage.has-study-delight .gx.sd-with-companion {
  width:min(1040px,100%);grid-template-columns:minmax(0,1fr) 196px;
  column-gap:28px;padding:28px;box-sizing:border-box;
 }
 .gx-stage.has-study-delight .gx.sd-with-companion>.gx-mid { width:100%;max-width:none; }
 .gx-stage.has-study-delight .lesson-delight.sd-side {
  grid-template-columns:minmax(0,1fr);justify-items:center;gap:16px;
  position:sticky;top:24px;
  width:196px;padding:12px 14px 20px;border:0;border-left:1px solid var(--rule);
  background:transparent;
 }
 .gx-stage.has-study-delight .lesson-delight.sd-side .lesson-delight-actor { width:168px;height:168px; }
 .gx-stage.has-study-delight .lesson-delight.sd-side .gx-mate { width:168px!important;height:168px!important; }
 .gx-stage.has-study-delight .lesson-delight.sd-side .lesson-delight-ledger { width:100%; }
 .gx-stage.has-study-delight .lesson-delight.sd-side .study-delight { min-height:0;padding:0;gap:16px;border:0; }
}
body.motion-low .lesson-delight-actor::after,
.lesson-delight[data-calm="true"] .lesson-delight-actor::after { animation:none!important; }
@media(prefers-reduced-motion:reduce) { .lesson-delight-actor::after { animation:none!important; } }


/* ===== 113-theatre-delight.css ===== */
/* The first lesson uses LearningLab. Its real answers share Practice's ink
   feedback; the preceding conversation remains ungraded supported input. */
.living-content.is-interactive { width:min(100%,700px);margin-inline:auto; }
.living-content.is-interactive .living-notice { display:block; }
.living-content.is-interactive .living-notice h2 { font-size:clamp(1.65rem,4vw,2.5rem);margin:10px 0; }
.living-content.is-interactive .living-notice p { font-size:var(--type-body,1rem);line-height:1.6;margin:10px 0; }
.living-content.is-interactive>.living-example { margin:16px 0;padding:14px 18px;font-size:1.3rem;line-height:1.5; }
.living-content.is-interactive>h3 { font-size:1.2rem;line-height:1.45;margin:18px 0 12px; }
.living-response-space { display:flex;flex-direction:column;gap:12px;min-height:280px;box-sizing:border-box;background:var(--paper);border:1px solid var(--rule);border-left:3px solid var(--rank-brass);border-radius:7px;padding:16px 20px;overflow-anchor:none; }
.living-response-space>[hidden],.living-response-space [hidden] { display:none!important; }
.living-delight { flex:0 0 auto; }
.living-delight .study-delight { --sd-ink:var(--scene-ink);padding-top:0; }
[data-theme="dark"] .living-delight .study-delight { --sd-ink:#becbed; }
.living-delight .study-delight:is([data-state="run"],[data-state="milestone"],[data-state="complete"]) { --sd-ink:var(--rank-brass); }
.living-response-space .living-options { gap:9px;flex:1; }
.living-response-space .living-options button { min-height:48px;padding:11px 16px;font-size:var(--type-control,1rem);line-height:1.5;box-shadow:none;border-radius:6px; }
.living-response-space .living-options button:active { transform:translateY(1px); }
.living-response-space .living-feedback { display:flex;flex:1;flex-direction:column;gap:9px;margin:0;padding:8px 0 0;border:0;border-radius:0;background:transparent; }
.living-response-space .living-feedback h3 { margin:0;font-size:1.2rem;line-height:1.4;color:var(--carbon); }
.living-response-space .living-feedback.is-right h3 { color:var(--scene-ink); }
.living-response-space .living-feedback p { margin:0;font-size:var(--type-body,1rem);line-height:1.6; }
.living-response-space .living-feedback .living-answer { font:500 1.35rem/1.4 var(--serif);color:var(--scene-ink); }
.living-response-space .living-feedback .btn { width:100%;min-height:48px;flex:0 0 auto;margin:12px 0 0;margin-top:auto; }
.living-response-space .living-feedback>.living-delight { margin-top:auto; }
.living-response-space .living-feedback>.living-delight+.btn { margin-top:0; }
.living-content.is-interactive .living-recall { padding:24px;border-radius:8px; }
.living-recall .living-response-space { padding:0;border:0; }
.living-recall h2 { margin:10px 0;font-size:1.75rem; }
.living-recall .living-recall-line { margin:8px 0;padding:10px 0;font-size:1.4rem;line-height:1.5; }
.living-recall .living-support { margin-top:4px;gap:14px; }
.living-recall #living-clue-text:empty { display:none; }
.living-recall #living-recall-input { min-height:50px;font-size:1.2rem; }
.living-footer>.living-delight { flex:0 0 100%;width:100%;margin:0;text-align:left; }
.living-footer>.living-delight .study-delight { min-height:112px; }
.living-footer>.living-delight .sd-companion { width:96px;height:104px;flex-basis:96px; }
.living-footer>.living-delight .sd-seat { width:96px;height:100px; }
.living-footer>.living-delight .sd-mark { width:22px; }
.living-stage:has(.living-content.is-interactive) .living-top { padding-bottom:14px; }
.living-stage:has(.living-content.is-interactive) .living-steps { padding-bottom:12px;margin-bottom:18px; }
.living-stage:has(.living-finish) .living-footer { width:min(100%,560px);margin-inline:auto;justify-content:center; }
.living-stage:has(.living-finish) .living-footer>.primary { flex:1; }
/* Dialogue feedback uses the night palette; illustrated scenery keeps its
   own pigments inside the scene. */
[data-theme="dark"] .living-response-space .living-feedback.is-right h3,
[data-theme="dark"] .living-response-space .living-feedback .living-answer,
[data-theme="dark"] .living-recall-line span { color:#becbed; }
/* A scene position is not a correct-answer streak. Turn navigation keeps
   the language stage's own progression and leaves the earned ink unclaimed. */
.theatre-turns button { width:44px;height:44px;min-height:44px; }
.theatre-footer .primary { min-height:48px; }
@media(max-width:600px) {
 .living-response-space { padding:12px 14px; }
 .living-content.is-interactive .living-recall { padding:18px 14px; }
 .living-content.is-interactive .living-notice h2 { font-size:1.8rem; }
 .living-stage:has(.living-content.is-interactive) { padding-inline:14px; }
 .living-stage:has(.living-content.is-interactive) .living-steps { margin-bottom:14px; }
 .living-finish { padding:20px 0; }
 .living-footer>.living-delight .sd-companion { width:88px;height:92px;flex-basis:88px; }
 .living-footer>.living-delight .sd-seat { width:88px;height:88px; }
 .theatre-footer { align-items:stretch; }
 .theatre-footer .primary { flex:1; }
}
@media(min-width:1200px) {
 .living-sheet.sd-with-companion { width:min(1040px,100%);grid-template-columns:minmax(0,1fr) 196px;column-gap:32px; }
 .living-sheet.sd-with-companion>.living-content { width:100%;max-width:none;margin:0; }
 .living-sheet.sd-with-companion>.living-delight { width:196px; }
 .living-sheet.sd-with-companion>.living-footer { padding-top:20px; }
}
@media(max-width:420px) {
 .living-stage:has(.living-finish) .living-footer>.btn { flex:1 0 100%;width:100%;min-height:48px; }
}
@media(prefers-reduced-motion:reduce) {
 .living-response-space .living-options button:active { transform:none; }
}
body.motion-low .living-response-space .living-options button:active,
body.calm .living-response-space .living-options button:active { transform:none; }

