/* DADI - the game page's dress, whole and in one place.
   Split out of index.html on 2026-08-20 so the DESIGN thread owns a
   file of its own: everything visual lives here, everything
   functional stays in the page. The contract is DESIGN_HANDOFF.md:
   ids are the game thread's, classes are shared, and nothing in this
   file may change what the game DOES - only how it looks doing it. */

/* ==========================================================================
   DADI — the illuminated skin.

   Everything you see is drawn by the browser: no image files, no icon fonts,
   no libraries. Parchment and stone are SVG turbulence filters, the vines and
   rosettes are SVG patterns defined once at the top of <body>, gold leaf is a
   layered gradient, and the dice are CSS 3D cubes.

   Read the CSS in this order:
     TOKENS        colours, metals, type
     MATERIALS     parchment, gold, stone, incised edges — reusable surfaces
     FRAME         the page as a page of a book
     S1 … S5       one section per screen
     DICE          the 3D cube and the resting tiles
     ANIMATION     the beats
   ========================================================================== */

/* --------------------------------------------------------------------------
   TOKENS
   Spec §9.2 fixes the three that matter: ground #1A140F, parchment #EDE3CF,
   gold #C9A227. The rest are the pigments a manuscript painter had — lapis,
   vermilion, verdigris, iron gall — mixed to sit beside them.
   -------------------------------------------------------------------------- */
:root {
  --bg:         #1A140F;   /* the dark ground: tooled leather */
  --parchment:  #EDE3CF;
  --gold:       #C9A227;

  --vellum:     #e9dcb8;   /* the writing surface, a shade warmer than parchment */
  --vellum-dim: #cdb98d;
  --lapis:      #24406b;   /* blue for capitals and cell inlay */
  --lapis-deep: #182c4b;
  --vermilion:  #8c3b2e;   /* red for rubrics and berries */
  --verdigris:  #4a6b4f;
  --ink:        #2b2018;   /* iron gall, for letters */
  --ink-soft:   #5b4a35;

  --leather:    #2a1d13;
  --leather-lo: #150e08;
  --hairline:   #6b5a3f;

  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia,
           "Times New Roman", serif;
  --sans:  system-ui, -apple-system, "Segoe UI", sans-serif;

  /* Gold leaf: a bright edge, a body, a dark burnish line, a bounce light.
     Used on every frame, rule and letter that should read as metal. */
  --leaf: linear-gradient(160deg,
            #f6e6ae 0%, #e2c469 18%, #c9a227 42%,
            #8a6a14 58%, #d8ba57 78%, #f2dfa2 100%);
  --leaf-dim: linear-gradient(160deg, #c9b273 0%, #9a7d29 50%, #6f5512 100%);
}

/* --------------------------------------------------------------------------
   MATERIALS
   Surfaces reused across every screen. Each is one class you can drop onto
   any element.
   -------------------------------------------------------------------------- */

/* Parchment: a warm base wash, two soft stains, then the grain filter layer
   on top in `::after`. The stains are plain radial gradients — irregular
   enough to read as age once the grain sits over them. */
/* The page itself. The bottom layer is a wash of real vellum, taken from the
   reference plates: the paper's true colour and the slow drift of its stains,
   blurred past any recognisable letter or leaf. Everything above it is light
   and age painted back on in CSS. */
.parchment {
  position: relative;
  background:
    /* a vignette, as if the sheet were lit from above the middle */
    radial-gradient(105% 78% at 50% 34%, transparent 40%, rgba(74,48,18,.42) 100%),
    /* foxing: old stains, irregular enough to read as age */
    radial-gradient(58% 40% at 84% 12%, rgba(126,86,38,.24), transparent 66%),
    radial-gradient(46% 34% at 14% 72%, rgba(126,86,38,.20), transparent 68%),
    radial-gradient(38% 26% at 62% 90%, rgba(108,72,30,.16), transparent 70%),
    /* the light falling on the page */
    radial-gradient(115% 85% at 40% 8%, rgba(255,252,238,.72), rgba(255,247,224,.22) 55%,
                    transparent 78%),
    /* the skin itself — warm, and its own colour, not the sample's */
    linear-gradient(176deg, var(--vellum) 0%, var(--vellum-dim) 100%);
}
/* Over the skin, a blurred wash of the real vellum from the plates. It is
   here only for the slow drift of its staining, so it is kept faint and
   blended rather than laid on flat — at full strength its average brown
   swamps the page. */
.parchment::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: url("/static/art/parchment.png") center/540px auto repeat;
  mix-blend-mode: soft-light; opacity: .55;
}
.parchment::after { opacity: .55; z-index: 0; }   /* then the tooth of it */
/* Everything bound into the page sits above the marginalia. `.sheet` opts
   out: the verdict and the two drawers position themselves against the
   viewport, and this rule outweighs a plain #id selector, so without the
   exemption it would quietly drag them back into the flow — which put the
   verdict below the fold on a tall board and made a finished match look as
   though it had simply stopped. */
#page > *:not(.edge):not(.corner):not(.beast):not(.sheet) {
  position: relative; z-index: 1;
}
.parchment::after {          /* the tooth of the paper */
  content: ""; position: absolute; inset: 0; pointer-events: none;
  filter: url(#grain);
  opacity: .5; mix-blend-mode: multiply;
  background: #fff;   /* the filter paints the noise; this is only its canvas */
}

/* Gold leaf on text and on rules. */
.gilt {
  background: var(--leaf);
  -webkit-background-clip: text; background-clip: text;
  color: transparent;
  filter: drop-shadow(0 1px 0 rgba(60,40,10,.45));
}
.rule-gold { height: 3px; border-radius: 2px; background: var(--leaf);
             box-shadow: 0 1px 2px rgba(0,0,0,.45); }

/* An incised edge: the illusion that a panel is cut into the page. */
.incised {
  box-shadow:
    inset 0 1px 0 rgba(255,247,225,.55),
    inset 0 -1px 0 rgba(60,40,15,.35),
    inset 0 0 0 1px rgba(120,95,55,.35);
}

/* --------------------------------------------------------------------------
   FRAME — the app as a bound page
   -------------------------------------------------------------------------- */
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { height: 100%; }
body {
  background:
    radial-gradient(120% 80% at 50% 0%, #241a11 0%, var(--leather-lo) 70%),
    var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  display: flex; justify-content: center;
  user-select: none;          /* a game, not a document */
  touch-action: manipulation; /* no double-tap zoom delay */
}

/* #wrap is the leather board; #page is the sheet bound into it. */
#wrap {
  width: 100%; max-width: 430px; min-height: 100%;
  position: relative; display: flex; flex-direction: column;
  padding: 6px;
  background:
    linear-gradient(180deg, #332417 0%, var(--leather) 40%, #1d1309 100%);
  box-shadow: inset 0 0 0 1px rgba(201,162,39,.28), 0 0 40px rgba(0,0,0,.6);
}

/* The illuminated border: a gold fillet, then the vine, then the page.
   The vine is four real <svg> strips (see THE ORNAMENT SHEET in the markup),
   each one a rectangle filled with a tiling pattern — CSS cannot reference an
   SVG pattern from `border-image`, so the strips do the work. */
#page {
  position: relative; flex: 1; display: flex; flex-direction: column;
  border: 2px solid transparent;
  border-image: var(--leaf) 1;
  padding: 18px 17px;          /* clears the 14px vine on every side */
  overflow: hidden;
}
.edge { position: absolute; pointer-events: none; z-index: 2; opacity: .95; }
.edge.top    { top: 0; left: 0; right: 0; height: 14px; }
.edge.bottom { bottom: 0; left: 0; right: 0; height: 14px; transform: scaleY(-1); }
.edge.left   { top: 0; bottom: 0; left: 0; width: 14px; }
.edge.right  { top: 0; bottom: 0; right: 0; width: 14px; transform: scaleX(-1); }

.corner {                       /* a rosette pinned at each corner */
  position: absolute; width: 24px; height: 24px; pointer-events: none; z-index: 3;
}
.corner.tl { top: 0;   left: 0; }
.corner.tr { top: 0;   right: 0; }
.corner.bl { bottom: 0; left: 0; }
.corner.br { bottom: 0; right: 0; }

/* MARGINALIA — the beasts that live in the margins of a real manuscript:
   monkeys, imps and horned devils, cut from the reference plates and lifted
   off the paper they were painted on, so only the ink lands on our page.
   They sit behind everything and swallow no taps.

   They keep to the very edge and hang half off it, the way a real marginal
   beast crowds the gutter — the board must never share room with them. */
.beast {
  position: absolute; z-index: 1; pointer-events: none; opacity: .92;
  width: 48px;
  filter: drop-shadow(0 1px 1px rgba(70,46,16,.35));
}
.beast.b1 { left: -19px; top: 5%; }
.beast.b2 { left: -21px; top: 29%; transform: scaleX(-1); }
.beast.b3 { left: -17px; top: 57%; }
.beast.b4 { left: -19px; bottom: 4%; transform: scaleX(-1); }
.beast.b5 { right: -19px; top: 4%; }
.beast.b6 { right: -21px; top: 32%; transform: scaleX(-1); }
.beast.b7 { right: -17px; top: 60%; }
.beast.b8 { right: -19px; bottom: 5%; transform: scaleX(-1); }

/* One screen is `on` at a time; S3 and S4 are overlays above the page. */
.screen { display: none; }
.screen.on { display: flex; flex-direction: column; flex: 1; }

/* --------------------------------------------------------------------------
   SHARED FURNITURE — buttons, rows, headings
   -------------------------------------------------------------------------- */
h1 { font-family: var(--serif); letter-spacing: 0.3em; text-align: center; }

/* A button is a small plaque of gilded vellum. */
button {
  font-family: var(--serif); font-size: 17px; letter-spacing: .12em;
  color: var(--ink); cursor: pointer;
  min-height: 44px; min-width: 44px;      /* thumb-sized, brief law 2.1 */
  border: 1px solid #7b6534; border-radius: 3px;
  background:
    linear-gradient(180deg, rgba(255,252,240,.75), rgba(214,196,158,.55)),
    linear-gradient(180deg, var(--vellum), var(--vellum-dim));
  box-shadow: 0 1px 0 rgba(255,250,235,.6) inset,
              0 -2px 4px rgba(80,55,20,.18) inset,
              0 2px 4px rgba(0,0,0,.4);
}
button:active { transform: translateY(1px); box-shadow: 0 1px 3px rgba(0,0,0,.5); }
button:disabled { opacity: .42; filter: saturate(.5); }
/* THE PICKED STATE READS AS A SEAL, NOT AS MORE GOLD (founder, 2026-08-15:
   "gold on gold is not readable"). Dark iron-gall fill, parchment-gilt
   letters, a gilt star to mark the choice — against the vellum buttons it
   cannot be mistaken at a glance, which is the whole job. */
button.picked {
  color: #f4e6bf;
  background:
    radial-gradient(130% 110% at 50% 0%, rgba(216,182,74,.30), rgba(0,0,0,0) 58%),
    linear-gradient(180deg, #4c3312, #2a1c0a);
  border-color: #c9a227;
  box-shadow: 0 0 0 1px rgba(201,162,39,.5), 0 2px 6px rgba(30,18,6,.4);
  text-shadow: 0 1px 2px rgba(0,0,0,.65);
}
button.picked::before { content: "✦ "; color: #d8b64a; }
.row { display: flex; gap: 10px; justify-content: center; }
.row button { flex: 1; max-width: 132px; padding: 9px 6px; }

/* A line ruled on the vellum to be written on, not a box laid over it. */
input[type="text"] {
  flex: 1; max-width: 274px; min-height: 42px; padding: 6px 10px;
  font-family: var(--serif); font-size: 17px; letter-spacing: .08em;
  text-align: center; color: var(--ink);
  background: rgba(255,252,240,.42);
  border: 0; border-bottom: 1px solid #7b6534; border-radius: 2px;
  box-shadow: inset 0 2px 5px rgba(80,55,20,.18);
}
input[type="text"]::placeholder { color: var(--ink-soft); opacity: .55; }
input[type="text"]:focus { outline: 1px solid var(--leaf); outline-offset: 2px; }

/* --- the roster: who you sit down against ---------------------------------
   A row of medallions. The chosen one lifts and gilds; the rest sit back. */
#roster[hidden] { display: none; }
#rosterRow {
  display: flex; gap: 6px; justify-content: center; flex-wrap: wrap;
  padding: 4px 0 2px;
}
.champ {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 0; min-height: 0; padding: 5px 4px 4px; width: 74px;
  background: none; border: 1px solid transparent; border-radius: 5px;
  box-shadow: none; color: var(--ink-soft);
}
.champ img {
  width: 40px; height: 40px; border-radius: 50%;
  filter: saturate(.55) brightness(.86);
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
}
.champ .nm {
  font-family: var(--serif); font-size: 9px; letter-spacing: .1em;
  text-transform: uppercase; white-space: nowrap;
}
/* What this one is after, not how good it is — the roster is a cast list,
   not a difficulty menu, so it names an appetite rather than counting stars. */
.champ .pips {
  font-size: 7.5px; letter-spacing: .1em; color: #8a6f28;
  font-style: italic; text-transform: lowercase;
}
.champ.picked {
  border-color: #c9a227;
  background: linear-gradient(180deg, #4c3312, #2a1c0a);
  color: #f4e6bf;
  box-shadow: 0 0 0 1px rgba(201,162,39,.5), 0 2px 6px rgba(30,18,6,.4);
}
.champ.picked img { filter: none; box-shadow: 0 0 10px rgba(201,162,39,.6); }
#championBlurb {
  text-align: center; min-height: 12px; font-style: italic;
  letter-spacing: .06em; text-transform: none; font-size: 10.5px;
}

/* Small caps label, the manuscript's voice for anything administrative. */
.legend {
  font-family: var(--serif); font-size: 10.5px; letter-spacing: .28em;
  text-transform: uppercase; color: #4a3826;
}


/* ==========================================================================
   S1 — THE TITLE PAGE
   ========================================================================== */
/* THE SETUP SCREEN HAS TO FIT, OR GIVE WAY.

   It was centred and unscrollable, which is right until it is not: picking a
   champion opens the roster, the roster is fifty-one pixels tall, and on a
   short phone that is enough to push START off the bottom of a screen that
   could not be scrolled to reach it. The founder found it the first time
   they opened the game on a small iPhone — you could choose an opponent, or
   you could start a match, but not both.

   `justify-content: center` is the culprit as much as the height: on a
   screen that overflows it centres the overflow too, so the content hangs
   off BOTH ends and the bottom is unreachable even with scrolling. `safe`
   tells the browser to stop centring the moment it would overflow and align
   to the start instead — which is exactly the rule you would write by hand.

   The bottom padding clears the home indicator on a notched phone, where
   the last few millimetres of the viewport are not really yours. */
#s1 {
  justify-content: safe center;
  gap: 20px;
  padding: 18px 14px calc(18px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

/* Short screens give up the ornament before they give up the controls: the
   drop cap and the title are the first things a 4-inch phone can spare. */
@media (max-height: 700px) {
  #s1 { gap: 12px; }
  #s1 h1 { font-size: 32px; }
  .dropcap { width: 52px; height: 52px; font-size: 36px; }
  #s1 .subtitle { display: none; }
}
@media (max-height: 600px) {
  #s1 .incipit { display: none; }
}

/* The drop cap and the title share a line, as an incipit would. */
.incipit { display: flex; align-items: center; justify-content: center; gap: 12px; }
.dropcap {
  width: 74px; height: 74px; flex: none; position: relative;
  display: flex; align-items: center; justify-content: center;
  font-family: var(--serif); font-size: 52px; line-height: 1;
  color: #f4e7c0; text-shadow: 0 2px 3px rgba(0,0,0,.5);
  background:
    linear-gradient(140deg, rgba(255,255,255,.16), transparent 55%),
    linear-gradient(180deg, var(--lapis) 0%, var(--lapis-deep) 100%);
  border: 2px solid transparent; border-image: var(--leaf) 1;
  box-shadow: 0 3px 8px rgba(0,0,0,.5);
}
#s1 h1 { font-size: 44px; }
.subtitle {
  font-family: var(--serif); font-style: italic; font-size: 13px;
  text-align: center; color: var(--ink-soft); letter-spacing: .05em;
}
#start { padding: 13px; font-size: 21px; letter-spacing: .3em; }
#start.busy { animation: shimmer 1.1s infinite; }

/* ==========================================================================
   S2 — THE MATCH
   Top to bottom: their board, the score band, the offer stage and its guide
   line, WICK's line, my board, the gear (and RETRY, when a cast is lost).
   ========================================================================== */
/* The playing cluster (boards, strip, stage) sits centred in the page; the
   CAST bar keeps its own place at the bottom edge. */
#s2 { padding: 2px 0 0; gap: 4px; justify-content: center; }

/* --- the two boards ---------------------------------------------------------
   Mirrored vertically, never horizontally (answer 15): column 2 of the top
   board sits directly above column 2 of the bottom one, because a cast only
   destroys dice in the column of the SAME INDEX. Keeping that a straight
   vertical eye-line is what makes a cancel readable.

   Both stacks fill downward: the first die of a column takes its topmost
   cell and each one after lands below the last, on either board. Dice fall,
   so they fall the same way on both sides of the table.                     */
.board { display: flex; gap: 6px; justify-content: center; padding: 4px 0; }
.col { display: flex; flex-direction: column; gap: 5px; border-radius: 14px; }
.board.inert { opacity: .72; filter: saturate(.75); }

/* The column being aimed at, from the first tap until the cast or a move
   elsewhere: the whole hollow warms, so it is obvious where the die is
   going even when the ghost is small. */
.col.aimed {
  background: rgba(201,162,39,.16);
  box-shadow: 0 0 0 1px rgba(201,162,39,.5), 0 0 12px rgba(201,162,39,.28);
}

/* --- the bribe button, during the beat -------------------------------------
   The reroll's clothes in the bribed column's yellow: a power of the moment,
   sitting in the same bar, gone the instant the moment is. */
#bribeBtn {
  flex: none; min-width: 118px; padding: 6px 12px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  font-size: 15px; letter-spacing: .22em; color: #2a1d06;
  background: linear-gradient(180deg, rgba(255,214,10,.92), rgba(201,162,39,.92));
  border: 1px solid #8a6f18; border-radius: 4px;
  box-shadow: 0 0 12px rgba(255,214,10,.35), inset 0 1px 0 rgba(255,250,220,.7);
  animation: bribepulse 1.4s ease-in-out infinite;
}
#bribeBtn[hidden] { display: none; }
#bribeBtn .oath { font-size: 8px; letter-spacing: .2em; opacity: .75; font-style: italic; }

/* --- the simony fuse --------------------------------------------------------
   One thin line, burning down. The width is set from the server's own count
   of seconds left, then a linear transition carries it to nothing — so a
   refreshed page draws the same remaining fuse as the page that lit it. */
/* THE BRIBE'S FUSE WEARS THE ROPE'S DRESS, IN YELLOW (founder, 2026-09-14):
   the same cord, ash and riding spark as the round's rope, gilded, under
   the dice; lit for the server's seconds it burns right to left, and its
   end bursts as the rope's does */
#simonyFuse {
  position: absolute; left: 10%; right: 10%; bottom: 2px; height: 7px;
  z-index: 4; pointer-events: none; overflow: visible;
  filter: sepia(1) saturate(4) brightness(1.15);
}
#simonyFuse[hidden] { display: none; }
#simonyFuse .rope {
  position: absolute; left: 0; top: 0; bottom: 0; width: 100%;
  background-image: url("/static/art/corners/fuse_rope.webp");
  background-repeat: repeat-x; background-size: auto 100%;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px);
  mask-image: linear-gradient(to right, transparent 0, #000 20px);
}
#simonyFuse .ash {
  position: absolute; right: 0; top: 0; bottom: 0; width: 12px;
  background-image: url("/static/art/corners/fuse_ash.webp");
  background-repeat: no-repeat; background-size: 100% 100%;
}
#simonyFuse .spark {
  position: absolute; left: 100%; top: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px;
  background-image: url("/static/art/corners/fuse_spark.webp");
  background-repeat: no-repeat; background-size: 300% 100%; background-position-x: 0%;
}
#simonyFuse.lit .rope  { animation: fuse-burn var(--fuse-ms, 20000ms) linear forwards; }
#simonyFuse.lit .spark { animation: fuse-ride var(--fuse-ms, 20000ms) linear forwards, fuse-flicker 240ms steps(3) infinite; }
#simonyFuse.lit { animation: fuse-out 420ms linear var(--fuse-ms, 20000ms) forwards; }
#bribeBar .fig[hidden] { display: none; }

/* --- the timer: a number nobody can miss ----------------------------------- */
#bribeTimer {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
  z-index: 4; pointer-events: none;
  font-family: Georgia, serif; font-size: 20px; font-weight: bold;
  letter-spacing: .08em; color: #ffd60a;
  text-shadow: 0 0 8px rgba(255,214,10,.45), 0 1px 2px rgba(0,0,0,.6);
  font-variant-numeric: tabular-nums;
}
#bribeTimer[hidden] { display: none; }

/* --- tables still standing --------------------------------------------------

   The way back to a match. One row each, and the row IS the button: a table
   is a place you return to, not a setting you configure, so there is nothing
   to choose on it and one tap takes you there.

   `waiting` is the one that has to catch the eye — it is your move and the
   table is sitting there wanting you — so it carries the same gold the game
   uses for your own intention everywhere else. A table waiting on the OTHER
   player is deliberately quieter: there is nothing to be done about it. */
#resume { margin: 0 auto 18px; max-width: 26em; }
.table-row {
  display: flex; align-items: center; gap: 10px;
  width: 100%; margin: 6px 0; padding: 9px 12px;
  font-family: Georgia, serif; font-size: 13px; text-align: left;
  color: #d9cdb0; cursor: pointer;
  background: rgba(30,24,16,.5);
  border: 1px solid rgba(201,162,39,.28); border-radius: 3px;
}
.table-row:hover { background: rgba(46,36,22,.62); border-color: rgba(201,162,39,.5); }
.table-row.waiting {
  border-color: rgba(201,162,39,.7);
  box-shadow: 0 0 10px rgba(201,162,39,.18);
}
.table-row .who { flex: 1 1 auto; }
.table-row .who b { color: #efe2c0; font-weight: normal; }
.table-row .when { flex: 0 0 auto; opacity: .62; font-size: 11px; }
.table-row .purse { flex: 0 0 auto; color: #d8b24a; font-size: 12px; }
/* A table this browser has no seat token for. It is still yours — the server
   says so — but the proof of the seat lives in the storage of the device that
   opened it, so this one cannot sit down. Said plainly rather than hidden:
   the table is why START is refusing, and a player owed that explanation. */
.table-row.locked { opacity: .55; cursor: default; }
.table-row.locked:hover { background: rgba(30,24,16,.5); border-color: rgba(201,162,39,.28); }

/* Why START did nothing. */
#startNote {
  margin: 8px auto 0; max-width: 26em;
  font-family: Georgia, serif; font-size: 13px; line-height: 1.45;
  text-align: center; color: #e0a898;
}

/* --- bribery ----------------------------------------------------------------

   A column somebody is paying you to use. YELLOW, and loudly (founder,
   2026-08-14). This was a cold, faintly sickly green, on the argument that
   `.aimed`'s gold means "your own intention" and an offer is somebody else's,
   so the two should not share a colour. The argument was better on paper than
   on a board: the green sat back into the table and the one mark a player has
   five seconds to notice was the quietest thing on screen.

   It is still not `.aimed`, and does not need a different hue to say so.
   `.aimed` is a muted antique gold, low and still; this is a bright saturated
   yellow with a heavier ring and a pulse, and the pulse is the real
   difference — an aim never moves, and an offer breathes at you. One of them
   is also always on the OTHER player's board, which settles any remaining
   doubt about whose intention it is.

   Deliberately not one of the classes `clearAim()` sweeps: this mark belongs
   to the position, not to the finger, and must survive taking a die, aiming
   elsewhere, and every repaint. */
.col.bribed {
  background: rgba(255,214,10,.22);
  box-shadow: 0 0 0 2px rgba(255,214,10,.85), 0 0 18px rgba(255,214,10,.45);
  animation: bribepulse 2.2s ease-in-out infinite;
}
@keyframes bribepulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(255,214,10,.70), 0 0 16px rgba(255,214,10,.35); }
  50%      { box-shadow: 0 0 0 2px rgba(255,236,120,1),  0 0 26px rgba(255,214,10,.62); }
}
/* Waiting for the figure: marked, but with nothing named yet. The founder's
   "the player sees his row highlighted and understands a bribe is being
   offered so he waits to see the offer." */
.col.bribed.unnamed { animation-duration: 1.1s; }

/* The bag of coins, with the value written on it. Drawn rather than
   fetched — no image assets in this lab — so it costs one element and
   scales with the cell it sits in. */
.bribe-bag {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%,-50%) rotate(-4deg);
  width: 2.5em; height: 2.5em; pointer-events: none; z-index: 3;
  display: flex; align-items: center; justify-content: center;
  font-family: Georgia, serif; font-size: 11px; font-weight: bold;
  color: #2a1d05; text-shadow: 0 1px 0 rgba(255,235,170,.6);
  background: radial-gradient(circle at 38% 30%, #f0cf6a 0%, #c9a227 52%, #8a6f18 100%);
  border-radius: 46% 46% 50% 50% / 62% 62% 38% 38%;
  box-shadow: 0 2px 6px rgba(0,0,0,.45), inset 0 -3px 6px rgba(0,0,0,.28);
  animation: bagdrop 260ms cubic-bezier(.2,1.4,.5,1);
}
/* A SPOT bag is green (founder's color ruling, 2026-08-20): certain coin
   straight to the bribed player's wallet. The gold bag above stays the POT
   kind — money that swells what the table is fighting for. Same bag, new
   dye; the design thread may retheme both from here. */
.bribe-bag.spot {
  background: radial-gradient(circle at 38% 30%, #9fd47a 0%, #5d9c3b 52%, #33591d 100%);
  color: #12240a; text-shadow: 0 1px 0 rgba(210,245,180,.6);
}
/* The tie at the neck of the bag. */
.bribe-bag::before {
  content: ""; position: absolute; top: -3px; left: 50%;
  transform: translateX(-50%);
  width: 46%; height: 6px; border-radius: 3px;
  background: linear-gradient(#6b5512, #3c2f0c);
  box-shadow: 0 1px 0 rgba(255,220,140,.25);
}
@keyframes bagdrop {
  from { transform: translate(-50%,-140%) rotate(-14deg) scale(.7); opacity: 0; }
  to   { transform: translate(-50%,-50%) rotate(-4deg) scale(1); opacity: 1; }
}
.cell { position: relative; }

/* The briber's own row of figures, floated over the board while they choose.
   Small, fast and close to the thumb: the whole point is that the offer can
   be made before the other player commits. */
#bribeBar {
  /* THE PILL, FITTED TO A PHONE (founder, 2026-09-14: with its slider it
     ran past the screen; "a small, inline slick" tumbler in its place):
     the kinds as two small seals one above the other, the drum, OFFER
     with its figure, the way out -- about three hundred pixels in all */
  /* FULL WIDTH, JUST BELOW THE DICE (founder, 2026-09-14): its own place
     in the flow -- right after the dice stage, over the top of the near
     board -- but taken out of it, so nothing moves when it opens */
  position: absolute; left: 2.5%; right: 2.5%; top: auto; transform: none;   /* 95% of the width (founder, 2026-09-14) */
  box-sizing: border-box;
  display: none; gap: 8px; align-items: center; z-index: 40;
  background: rgba(20,15,8,.96); border: 1px solid #6b5512;
  border-radius: 18px; padding: 8px 10px;    /* iPhone corners (founder, 2026-09-14): a rounded square, not an ellipse */
  box-shadow: 0 6px 22px rgba(0,0,0,.5);
}
#bribeBar.on { display: flex; }
#bribeBar .fig { font-family: Georgia, serif; font-size: 15px; font-weight: 700; margin-left: 4px; font-variant-numeric: tabular-nums; }
/* The kind is the briber's move (2026-08-20): two small seals beside the
   dial, green for SPOT, gold for POT, shown only when the table enables
   both. Hidden is hidden ([hidden] wins over flex). */
#bribeKinds { display: flex; flex-direction: row; gap: 4px; }   /* side by side: the pill has the width now */
#bribeKinds[hidden] { display: none; }
#bribeKinds .kind {
  font-size: 10px; letter-spacing: .12em; padding: 6px 9px;
  opacity: .45; border-radius: 4px;
}
#bribeKinds .kind.picked { opacity: 1; }
#bribeKinds .kind.spot.picked { background: #5d9c3b; color: #eaf5df; }
#bribeKinds .kind.pot.picked  { background: #c9a227; color: #2a1d05; }

/* THE TUMBLER (founder, 2026-09-14): a drum five rows tall, the row at
   rest in the band the offer; native scrolling with snap does the swipe
   and the settling, the rows lean away from the band. The figure is a
   number you ARRIVE at, and quickly: the other player is deciding where to
   cast while you do. */
#bribeBar .wheel {
  /* two rows of height (founder, 2026-09-14): the row in the band whole,
     the neighbours above and below by half; no sideways drag */
  position: relative; flex: 1 1 auto; min-width: 92px; max-width: 176px; height: 52px; overflow: hidden; border-radius: 10px;
  background: rgba(0, 0, 0, .38);
  box-shadow: inset 0 0 0 1px rgba(201, 162, 39, .3), inset 0 6px 10px rgba(0, 0, 0, .45), inset 0 -6px 10px rgba(0, 0, 0, .45);
}
#bribeBar .wheel:focus-visible { outline: 2px solid rgba(201, 162, 39, .6); outline-offset: 1px; }
#bribeBar .wheel-list {
  height: 100%; margin: 0; padding: 13px 0; list-style: none; box-sizing: border-box;
  overflow-y: auto; overflow-x: hidden; scroll-snap-type: y mandatory; overscroll-behavior: contain;
  touch-action: pan-y; scrollbar-width: none; -webkit-overflow-scrolling: touch; perspective: 200px;
}
#bribeBar .wheel-list::-webkit-scrollbar { display: none; }
#bribeBar .wheel-list li {
  height: 26px; box-sizing: border-box; scroll-snap-align: center;
  display: flex; align-items: baseline; justify-content: center; gap: 6px;
  font-family: Georgia, serif; font-size: 14px; line-height: 26px; color: #f0cf6a;
  font-variant-numeric: tabular-nums; cursor: pointer; white-space: nowrap;
}
#bribeBar .wheel-list li small { font-size: 10px; opacity: .6; letter-spacing: .04em; }
#bribeBar .wheel-list li.picked { color: #fff1c0; font-size: 16px; }
#bribeBar .wheel .band {
  position: absolute; left: 6px; right: 6px; top: 13px; height: 26px; pointer-events: none;
  border-top: 1px solid rgba(201, 162, 39, .75); border-bottom: 1px solid rgba(201, 162, 39, .75);
  background: linear-gradient(90deg, rgba(201, 162, 39, 0), rgba(201, 162, 39, .14), rgba(201, 162, 39, 0));
}
#bribeBar .wheel::before, #bribeBar .wheel::after { content: ""; position: absolute; left: 0; right: 0; height: 12px; pointer-events: none; z-index: 1; }
#bribeBar .wheel::before { top: 0; background: linear-gradient(180deg, rgba(20, 15, 8, .96), rgba(20, 15, 8, 0)); }
#bribeBar .wheel::after { bottom: 0; background: linear-gradient(0deg, rgba(20, 15, 8, .96), rgba(20, 15, 8, 0)); }
#bribeBar #bribeGo {
  flex: none; height: 36px; padding: 0 14px; border-radius: 10px; white-space: nowrap;
  font-size: 12px; letter-spacing: .12em; font-weight: 700; color: #2a1d05;
  background: linear-gradient(180deg, #f4d977, #c9a227); border: 1px solid #8a6f18;
}
#bribeBar button {
  font-family: inherit; font-size: 13px; letter-spacing: .04em;
  color: #f0e2bd; background: #2c2213; border: 1px solid #6b5512;
  border-radius: 10px; padding: 6px 11px; cursor: pointer;
}
#bribeBar button:disabled { opacity: .4; }
#bribeBar button.drop { border-color: #6b3a2a; color: #e0a898; }

/* SET IN STONE (founder, 2026-08-19; the look and its moment, 2026-09-14:
   "cool, distinctive, not shouty"). THE LOOK: the dice go grey and hard as
   carved stone, the column takes a chiselled frame with a lit top edge
   and a shaded foot, and its sum is cut in. THE MOMENT: a cold light runs
   up the column, each die turning to stone as it passes from the bottom
   up, the frame's chisel arriving with it, and the column settles. */
.col.cemented {
  border-radius: 4px;
  /* MARBLE, NOT METAL (founder, 2026-09-15). The slab is pale warm stone,
     and what shows between the cells is its mortar, cut from the same
     block a shade darker. */
  background: linear-gradient(176deg, #bfb6a2, #9c927c);
  box-shadow: inset 0 1px 0 rgba(255, 252, 244, .5), inset 0 -1px 0 rgba(60, 48, 26, .3),
              0 0 0 2px #b8ac92, 0 0 0 3px rgba(70, 58, 34, .45);
}
/* PETRIFIED, ON ANY STONE (founder, 2026-09-14, over Mako's grey marble:
   "how do we know it's set in stone? Petrify it"). Greying alone cannot
   show on grey; so a cemented die goes cold slate whatever it was cut
   from (a colour blend over it), takes the grain of cut stone (a speckle
   multiplied in), squares off and sinks into the slab. */
.col.cemented .die {
  /* CUT FROM THE SAME MARBLE (founder, 2026-09-15): the die goes ivory
     rather than dark grey -- lifted and softened, never darkened, so the
     pips stay black on white and the stone reads as stone.
     THE TINT IS THE VEIL'S, NOT THE FILTER'S (measured, 2026-09-15): a
     grayscale() here is applied to the whole group, veil and all, and ate
     the ivory -- every stone landed on the same neutral grey, which is
     what read as metal. The veil below does the desaturating, taking hue
     and saturation from the ivory and luminosity from the stone; the
     filter only lifts and softens what is left. */
  position: relative; border-radius: 3px;
  filter: brightness(1.32) contrast(.86) saturate(.9);
  box-shadow: inset 0 1px 0 rgba(255, 253, 247, .75), inset 0 -2px 3px rgba(92, 78, 50, .28),
              inset 0 0 0 1px rgba(122, 106, 74, .5), 0 1px 0 rgba(255, 252, 242, .35);
}
.col.cemented .die::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(168deg, #f7f1e1, #e6dcc4); mix-blend-mode: color; opacity: 1;
}
.col.cemented .die::after {
  /* one faint vein across the face, and the stone's own bloom -- no
     speckle: it was the speckle that read as brushed metal */
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  background: linear-gradient(107deg, transparent 0 42%, rgba(120, 104, 74, .5) 45%, rgba(120, 104, 74, .12) 47%, transparent 49%),
              radial-gradient(70% 55% at 26% 20%, rgba(255, 255, 255, .5), transparent 72%);
  opacity: .5;
}
/* THE WHOLE COLUMN (founder, 2026-09-14: "the entire column, not just the
   dice; the board as well"): each cell's slab, under the die and the
   crossing, shown when the column is set in stone; the rules through the
   column go grey with it */
.cell > .stone {
  /* MARBLE (founder, 2026-09-15): pale warm stone lit from the upper left,
     with a few faint veins wandering across it -- drawn, not speckled, at
     the sizes a cell actually gets on a phone. */
  position: absolute; inset: 0; z-index: 0; opacity: 0; pointer-events: none;
  background:
    linear-gradient(101deg, transparent 0 28%, rgba(126, 110, 78, .30) 30%, rgba(126, 110, 78, .07) 32.5%, transparent 34%),
    linear-gradient(72deg, transparent 0 62%, rgba(104, 90, 64, .22) 63.5%, transparent 66%),
    linear-gradient(122deg, transparent 0 76%, rgba(140, 124, 92, .16) 77.5%, transparent 79%),
    radial-gradient(76% 62% at 24% 18%, rgba(255, 255, 255, .62), transparent 74%),
    linear-gradient(168deg, #efe9da 0%, #ded7c4 44%, #e9e3d4 60%, #cfc7b0 100%);
  box-shadow: inset 0 1px 0 rgba(255, 253, 247, .7), inset 0 -1px 0 rgba(92, 78, 50, .22), inset 0 0 0 1px rgba(122, 106, 74, .28);
}
.cell > .die { position: relative; }
.col.cemented .cell > .stone { opacity: 1; }
.col.cemented .cell::after, .col.cemented::after { filter: grayscale(1) brightness(1.12) contrast(.85) opacity(.75); }   /* the painted rules pale into the marble */
.col.cemented .colsum {
  color: #6f6758; font-weight: 700; letter-spacing: .1em;
  text-shadow: 0 1px 0 rgba(255, 255, 255, .4), 0 -1px 0 rgba(0, 0, 0, .7);
}
#s2 .board-frame:has(.theirs) .col.cemented .colsum, #s2 .board-frame:has(.mine) .col.cemented .colsum {
  text-shadow: 0 1px 0 rgba(255, 255, 255, .4), 0 -1px 0 rgba(0, 0, 0, .7);   /* carved, whichever ink the board writes in */
}
.col.setting { position: relative; animation: stone-settle 520ms cubic-bezier(.2, .7, .3, 1) 420ms; }
.col.setting::after {
  content: ""; position: absolute; inset: -3px; border-radius: 6px; pointer-events: none; z-index: 3;
  background: linear-gradient(180deg, rgba(235, 240, 255, 0) 40%, rgba(225, 235, 255, .55) 48%, rgba(255, 250, 235, .9) 50%, rgba(225, 235, 255, .5) 52%, rgba(235, 240, 255, 0) 60%) 0 0 / 100% 320% no-repeat;
  mix-blend-mode: screen; animation: stone-sweep 900ms cubic-bezier(.4, 0, .6, 1) forwards;
}
.col.setting .die { animation: petrify 620ms ease-out both; }
.col.setting .die::before, .col.setting .die::after, .col.setting .cell > .stone { animation: petrify-veil 620ms ease-out both; }
.col.setting .cell:nth-last-child(1) .die, .col.setting .cell:nth-last-child(1) .die::before, .col.setting .cell:nth-last-child(1) .die::after, .col.setting .cell:nth-last-child(1) > .stone { animation-delay: 80ms; }
.col.setting .cell:nth-last-child(2) .die, .col.setting .cell:nth-last-child(2) .die::before, .col.setting .cell:nth-last-child(2) .die::after, .col.setting .cell:nth-last-child(2) > .stone { animation-delay: 260ms; }
.col.setting .cell:nth-last-child(3) .die, .col.setting .cell:nth-last-child(3) .die::before, .col.setting .cell:nth-last-child(3) .die::after, .col.setting .cell:nth-last-child(3) > .stone { animation-delay: 440ms; }
.col.setting .cell:nth-last-child(4) .die, .col.setting .cell:nth-last-child(4) .die::before, .col.setting .cell:nth-last-child(4) .die::after, .col.setting .cell:nth-last-child(4) > .stone { animation-delay: 620ms; }
@keyframes petrify-veil { 0% { opacity: 0; } 45% { opacity: 0; } 100% { opacity: 1; } }
@keyframes stone-sweep { from { background-position: 0 0%; } to { background-position: 0 100%; } }
@keyframes petrify {
  0%   { filter: brightness(1) contrast(1) saturate(1); }
  45%  { filter: brightness(1.6) contrast(1.05) saturate(.95); }
  100% { filter: brightness(1.32) contrast(.86) saturate(.9); }
}
@keyframes stone-settle {
  0% { transform: translateY(0); } 35% { transform: translateY(2px); } 65% { transform: translateY(-1px); } 100% { transform: translateY(0); }
}

/* --- a column's own score ---------------------------------------------------
   Every number here is the server's (brief §0.3): the client is told each
   column's total and prints it. A column with nothing in it says nothing. */
.colsum {
  font-family: var(--serif); font-size: 11px; letter-spacing: .1em;
  text-align: center; color: var(--ink-soft); min-height: 13px;
  padding-top: 1px;
}
.colsum:empty { visibility: hidden; }
/* On my board the sum sits under the column, on theirs above it — each one
   on the outer edge, so neither crowds the gap the cancel is read across. */
.board.theirs .col { flex-direction: column-reverse; }
.board.theirs .col .cells { flex-direction: column; }
.cells { display: flex; flex-direction: column; gap: 5px; }

/* Each board is a single slab of marble — the same rock the dice are cut
   from, painted by dice.js and set on --marble at boot. Its corners are worn
   round and its edge is stone, not metal: a bright top lip where the light
   catches, a shadowed underside, and no hard line anywhere. */
.board-frame {
  align-self: center; padding: 9px; border-radius: 16px;
  /* A warm wash over the stone: it ties the slab to the vellum it lies on,
     and darkens it just enough that a pale marble die still reads against it. */
  background:
    linear-gradient(168deg, rgba(190,150,86,.20), rgba(140,102,46,.30)),
    var(--marble) center/cover no-repeat,
    #ddd3bd;
  /* the far board is cut from another part of the same slab */
}
.board-frame:has(.theirs) {
  background-position: center, right bottom, center;
  box-shadow:
    0 6px 14px rgba(48,32,10,.42),          /* the slab's own shadow */
    inset 0 2px 0 rgba(255,252,242,.65),    /* the lit top lip */
    inset 0 -3px 6px rgba(70,50,20,.30),    /* the shaded underside */
    inset 0 0 0 1px rgba(120,98,58,.35);    /* the softest edge */
}

/* A cell is a hollow ground out of the slab, not a tile laid on it — so it
   has no fill of its own. The marble simply shows through, darkened a little
   and shadowed under its upper lip, the way a shallow depression in stone
   catches light. Nothing here is drawn with a hard edge. */
.cell {
  width: var(--cell, 50px); height: var(--cell, 50px); position: relative;
  display: flex; align-items: center; justify-content: center;
  border-radius: 12px;
  background: rgba(58,42,18,.13);
  box-shadow:
    inset 0 4px 7px rgba(56,38,14,.42),      /* shadow cast by the near rim */
    inset 0 -3px 5px rgba(255,250,236,.42),  /* light pooling at the far rim */
    0 1px 0 rgba(255,252,242,.35);           /* the worn lip around it */
}

/* --- the score band ---------------------------------------------------------
   Two cartouches with a rubric between them. */
#strip {
  display: flex; align-items: stretch; justify-content: space-between; gap: 6px;
  padding: 4px 4px;
  border-top: 1px solid rgba(120,95,55,.45);
  border-bottom: 1px solid rgba(120,95,55,.45);
  background: linear-gradient(180deg, rgba(255,250,235,.30), rgba(150,115,60,.12));
}
.total {
  position: relative; min-width: 84px; padding: 4px 6px 5px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  border: 1px solid #8a7038; border-radius: 4px;
  background: linear-gradient(180deg, rgba(255,252,240,.75), rgba(214,196,158,.45));
  box-shadow: inset 0 1px 0 rgba(255,250,235,.7), 0 1px 3px rgba(0,0,0,.35);
}
.total .who {
  font-family: var(--serif); font-size: 9px; letter-spacing: .2em;
  color: var(--ink-soft); text-transform: uppercase;
}
.total .n {
  font-family: var(--serif); font-size: 27px; line-height: 1.05;
  color: var(--ink); text-shadow: 0 1px 0 rgba(255,250,235,.8);
}
/* The medallion of the beast each duelist fights under, cut from the roster
   down the edge of the reference plate. `multiply` again, to sink the little
   square of vellum it was painted on into the cartouche. */
.mark { width: 27px; height: 27px; margin-bottom: 1px;
        filter: drop-shadow(0 1px 1px rgba(70,46,16,.4)); }

/* The turn banner, set as a rubric. Now the top line of the offer stage. */
#banner {
  align-self: center; text-align: center; min-height: 14px;
  font-family: var(--serif); font-size: 12px; letter-spacing: .16em;
  color: var(--vermilion); text-transform: uppercase;
  text-shadow: 0 1px 0 rgba(255,250,235,.7);
}
#banner.shimmer { animation: shimmer 1.1s infinite; }

/* --- the offer stage: where a rolled die waits ---------------------------- */
/* It lives INSIDE the score band, in the gap between the two name tags: the
   banner above, the dice, and the guide line below. Keeping it to one band
   of height is what lets a 4x4 board fit a phone without scrolling. */
#offerStage {
  flex: 1; min-width: 0; gap: 1px; padding: 2px 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  /* The beat's furniture — his take, the fuse, the timer — floats OVER this
     stage instead of stacking inside it. Everything but the boards lives on
     a fixed height budget (sizeCells' CHROME), and the first cut of the beat
     grew it: the bar slid off the bottom of a page that deliberately never
     scrolls, and there was no way back to it. Overlays cost nothing. */
  position: relative;
}
#offerArea {
  position: relative;              /* the slot picker is laid over the stage */
  display: flex; align-items: center; justify-content: center;
  min-height: 50px; gap: 8px;
  perspective: 520px;              /* the depth the 3D dice are seen through */
}

/* --- A1: choosing between the two dice ------------------------------------ */
/* The pair lives inside one canvas, so the taps need targets of their own:
   two invisible halves laid exactly over it. */
#slotPicker { position: absolute; inset: 0; display: flex; }
.slotHalf {
  flex: 1; padding: 0; background: none; border: 0; border-radius: 6px;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
.slotHalf:focus-visible { outline: 1px solid var(--leaf); outline-offset: -3px; }
/* Choosing is the whole point of the turn, so an unchosen pair says so. */
#offerArea.undecided { animation: waiting 1.9s ease-in-out infinite; }
@keyframes waiting {
  0%, 100% { filter: none; }
  50%      { filter: drop-shadow(0 0 7px rgba(201,162,39,.55)); }
}

/* --- WICK's line, mask mode only (brief §4.5) ----------------------------- */
#talkStrip {
  display: none; min-height: 21px; padding: 0 14px; text-align: center;
  font-family: var(--serif); font-style: italic; font-size: 13.5px;
  color: var(--ink-soft);
}
.mask-mode #talkStrip { display: block; }

/* --- the bottom bar: the gear, and the RETRY plaque when it is needed ------ */
#bar {
  display: flex; gap: 8px; align-items: stretch; margin-top: auto;
  justify-content: flex-end;         /* the gear sits right when alone */
  padding: 8px 2px calc(6px + env(safe-area-inset-bottom));
  position: relative; z-index: 30;   /* above the end overlay, so the gear
                                        stays reachable there (answer 30) */
  /* A CONSTANT BAND, whatever is standing in it. CAST comes and goes every
     turn and REROLL comes and goes once a match; without this the bar swung
     by 23 pixels each time and shoved both boards up and down the screen —
     and on a 4x4 phone it pushed itself off the bottom, which is the very
     fault this layout was rebuilt to cure. Reserving the height here rather
     than on the buttons means nothing has to stay in the flow to hold its
     place, so CAST can leave outright and REROLL gets the whole width. */
  box-sizing: border-box; min-height: 81px;
  /* And when a narrow screen cannot hold everything standing in it at once
     — purse, CAST, BRIBE, the gear — the bar wraps to a second row rather
     than clipping a button off the right edge. The taller bar is caught by
     the resize observer and the boards re-fit around it. */
  flex-wrap: wrap;
}
#retry, #cast {
  flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px; padding: 8px 10px;
  font-size: 27px; letter-spacing: .34em; color: #2a1d06;
  background: var(--leaf);
  border: 1px solid #6b5512; border-radius: 4px;
  box-shadow: 0 0 14px rgba(201,162,39,.30), inset 0 1px 0 rgba(255,250,220,.7),
              inset 0 -3px 6px rgba(90,65,10,.35), 0 3px 5px rgba(0,0,0,.45);
}
#retry[hidden], #cast[hidden] { display: none; }  /* [hidden] loses to flex */
#retry .oath, #cast .oath {          /* the mockup's line under the word */
  font-size: 8.5px; letter-spacing: .24em; opacity: .72; font-style: italic;
}
/* Waiting on the sky. The plaque stays put and dims rather than vanishing,
   so the thumb has nothing to chase and a second tap has nothing to hit. */
#cast.summoning {
  pointer-events: none; color: #4a3a12;
  background: linear-gradient(180deg, rgba(150,124,44,.85), rgba(108,88,28,.85));
  /* no breathing glow (2026-09-28): the dice in the air are the tap's one
     effect, and the glow was a filter the browser redrew every frame */
}

/* The auto-cast toggle, beside CAST — the same request, fired for you. Small
   and quiet: it is a preference, not a move. */
#auto {
  flex: none; min-width: 62px; padding: 6px 8px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  font-size: 12px; letter-spacing: .16em; color: #e6d7ad;
  background: linear-gradient(180deg, rgba(72,58,28,.92), rgba(46,36,16,.92));
  border: 1px solid #6b5512; border-radius: 4px;
  box-shadow: inset 0 1px 0 rgba(255,246,205,.22), 0 2px 4px rgba(0,0,0,.45);
}
#auto[hidden] { display: none; }
#auto .oath { font-size: 8px; letter-spacing: .18em; opacity: .7; font-style: italic; }
#auto.on {
  color: #2a1d06; background: var(--leaf); border-color: #8a6f18;
  box-shadow: inset 0 1px 0 rgba(255,250,220,.7), 0 2px 4px rgba(0,0,0,.45);
}

/* The round the dice on the table fell out of, said under them in the
   smallest voice on the screen. It is a receipt, not an announcement — but
   it is there every turn, and it is checkable while the match is still on. */
#roundLine {
  font-size: 8.5px; letter-spacing: .16em; font-style: italic;
  opacity: .55; min-height: 11px; text-align: center;
  font-variant-numeric: tabular-nums;
  /* Tappable: it is the way into the receipt mid-match, so it is dotted
     underneath rather than silently clickable. */
  cursor: pointer;
}
#roundLine:not(:empty) { border-bottom: 1px dotted rgba(120,95,55,.5);
                         display: inline-block; align-self: center; }

/* --- A2: the reroll, beside the gear (brief §8) ---------------------------
   Smaller than a cast plaque and quieter until armed: this is a power you
   spend once a match, not the thing you do every turn. */
#reroll {
  flex: none; min-width: 118px; padding: 6px 12px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  font-size: 15px; letter-spacing: .22em; color: #e6d7ad;
  background: linear-gradient(180deg, rgba(72,58,28,.92), rgba(46,36,16,.92));
  border: 1px solid #6b5512; border-radius: 4px;
  box-shadow: inset 0 1px 0 rgba(255,246,205,.22), 0 2px 4px rgba(0,0,0,.45);
}
#reroll[hidden] { display: none; }
#reroll .oath { font-size: 8px; letter-spacing: .2em; opacity: .7; font-style: italic; }
/* Armed: it asks, and it is the brightest thing on the bar until answered. */
#reroll.armed {
  color: #2a1d06; background: var(--leaf); border-color: #8a6f18;
  box-shadow: 0 0 14px rgba(201,162,39,.45), inset 0 1px 0 rgba(255,250,220,.7);
  animation: waiting 1.5s ease-in-out infinite;
}
#gear {
  width: 50px; flex: none; font-size: 20px; padding: 0;
  display: flex; align-items: center; justify-content: center;
}

/* The money, at the left of the bar the buttons keep to the right of.
   `margin-right: auto` is what pushes it there — the bar itself stays
   justify-end, so with nothing else shown the gear still sits alone.

   LOUDER THAN IT WAS (founder, 2026-08-15): "I can barely see them." The
   figures were 12px of dim gold on dark leather; now they are the brightest
   gold on the page, sized with the buttons, with a shadow lifting them off
   the ground. The purse is half of why anyone stakes anything. */
#barPurse {
  margin-right: auto; align-self: center;
  display: flex; flex-direction: column; gap: 2px;
  font-family: Georgia, serif; font-size: 16px; letter-spacing: .04em;
  color: #f2cf6b; font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 3px rgba(0,0,0,.85), 0 0 12px rgba(216,178,74,.25);
}
#barPurse[hidden] { display: none; }
#barPurse .riding { opacity: .85; font-size: 13px; }

/* The purse on the title page (founder, 2026-08-15, twice): visible before
   any table is chosen — and in INK, because the bar's gold belongs on dark
   leather and was unreadable laid on parchment ("gold on gold is not
   readable"). Numerals big and iron-gall; the words small caps beneath the
   manuscript's own soft brown. */
#s1Purse {
  text-align: center; font-family: var(--serif);
  color: var(--ink-soft); font-variant-numeric: tabular-nums;
}
#s1Purse[hidden] { display: none; }
#s1Purse .held, #s1Purse .dust .n {
  font-size: 26px; letter-spacing: .04em; color: #241a08; font-weight: 600;
}
#s1Purse .word { font-size: 10px; letter-spacing: .22em;
                 text-transform: uppercase; }
#s1Purse .dust { font-size: 10px; letter-spacing: .22em;
                 text-transform: uppercase; }

/* The custom stake (answer 40): one field, one MAX. The field matches the
   buttons around it rather than the browser's idea of a number input. */
#wagerCustomRow input {
  flex: 1; min-width: 0; padding: 10px 12px;
  font-family: Georgia, serif; font-size: 18px; text-align: center;
  color: var(--ink); background: rgba(0,0,0,.18);
  border: 1px solid rgba(120,95,55,.55); border-radius: 4px;
  font-variant-numeric: tabular-nums;
}
#wagerCustomRow input:focus { outline: none; border-color: #d8b24a; }
#wagerCustomRow button { flex: 0 0 auto; }

/* Conceding, in the gear sheet. The reroll's shape — a word above an oath —
   because it is the same grammar: a power that arms on one tap and spends on
   the next. Armed, it goes ember rather than gold: giving the match away is
   not a plan, and must not wear the colour of one. */
#concede {
  min-width: 150px; padding: 8px 14px; margin: 0 auto;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  font-size: 14px; letter-spacing: .22em; color: #d9b8a8;
  background: linear-gradient(180deg, rgba(66,36,24,.92), rgba(40,22,14,.92));
  border: 1px solid #6b3a2a; border-radius: 4px;
}
#concede[hidden] { display: none; }
#concede .oath { font-size: 8px; letter-spacing: .2em; opacity: .7; font-style: italic; }
#concede.armed {
  color: #fff1e8;
  background: linear-gradient(180deg, rgba(122,52,32,.96), rgba(82,32,21,.96));
  border-color: #a04a30;
  box-shadow: 0 0 14px rgba(200,80,40,.4), inset 0 1px 0 rgba(255,220,200,.35);
  animation: waiting 1.5s ease-in-out infinite;
}

/* --- the guide line: what to do next, said under the dice ------------------ */
#guide {
  /* Two lines' worth, always. The longest hint — the one that teaches CAST —
     wraps at phone width, and letting the box breathe with it moved both
     boards 14px every time the line changed. */
  min-height: 27px; font-style: italic; opacity: .8;
  transition: opacity .18s;
}
#guide:empty { opacity: 0; }

/* ==========================================================================
   S3 — THE VERDICT   ·   S4 — THE DIALS SHEET   ·   S5 — LOST PAGE
   ========================================================================== */
/* Fixed to the VIEWPORT, not to the page. If the page is ever taller than
   the screen, an absolutely-positioned verdict would be sitting below the
   fold — the match would look as though it had simply stopped. */
#s3 {
  position: fixed; inset: 0; z-index: 20; display: none;
  flex-direction: column; justify-content: space-between; text-align: center;
  padding: 34px 18px 92px;
  background:
    radial-gradient(80% 55% at 50% 50%, rgba(26,20,15,.55), rgba(16,11,7,.93));
  backdrop-filter: blur(1.5px);
}
#s3.on { display: flex; }
.verdict {
  font-family: var(--serif); font-size: 42px; letter-spacing: .04em;
  filter: drop-shadow(0 3px 6px rgba(0,0,0,.8));
}
#s3 .scroll {                        /* the little panel holding the totals */
  align-self: center; padding: 12px 26px; border-radius: 4px;
  border: 2px solid transparent; border-image: var(--leaf) 1;
}
#s3 .totals { font-family: var(--serif); font-size: 26px; color: var(--ink); }
#s3 .row { margin-top: 16px; }
#s3 .row button { padding: 11px; }

/* THE TABLE'S MENU, MID-SCREEN (founder, 2026-09-13). The scrim is the
   verdict's; the card is the wager sheet's parchment, centred, bound in
   gold, scrolling inside itself when the phone is short. It rises as the
   rules do, with @starting-style where the browser knows it. */
/* THE TABLE'S MENU IS A wa-dialog (founder, 2026-09-13: "make it into a
   modal overlay; use a js modal library; give the modal controls, X and
   close"). The library's dialog stands in the top layer with its own
   backdrop, focus trap, Escape and light dismiss, and draws the X in its
   header; its parts are dressed here as the page's parchment, bound in
   gold, the title in the manuscript's serif. */
/* the doors: a word over an oath, as the bar's hands read, on parchment */

/* S8 — the receipt. Taller than the other drawers because it is a ledger:
   one line per throw, scrolling, with the verdict pinned under it. */
#s8 {
  position: fixed; inset: auto 0 0 0; z-index: 46; display: none;
  gap: 8px; padding: 14px 14px calc(14px + env(safe-area-inset-bottom));
  max-height: 86vh;
  border-top: 2px solid transparent; border-image: var(--leaf) 1;
  box-shadow: 0 -10px 30px rgba(0,0,0,.6);
}
#s8.on { display: flex; flex-direction: column; }
#receiptWhat { text-align: center; line-height: 1.5; text-transform: none;
               letter-spacing: .06em; }
#receiptRows {
  overflow-y: auto; flex: 1; min-height: 90px;
  font-variant-numeric: tabular-nums;
}
.dieRow {
  display: flex; align-items: baseline; gap: 8px; padding: 3px 2px;
  font-size: 11px; letter-spacing: .04em; color: var(--ink);
  border-bottom: 1px solid rgba(120,95,55,.18);
}
.dieRow .turn  { width: 44px; font-size: 9px; opacity: .6; letter-spacing: .16em; }
.dieRow .round { flex: 1; font-size: 11px; opacity: .85; }
.dieRow .dice  { font-family: var(--serif); font-size: 14px; }
.dieRow .mark  { width: 20px; text-align: right; font-size: 13px; }
.dieRow.ok    .mark { color: #2f6b34; }
.dieRow.bad   .mark { color: var(--vermilion); font-weight: bold; }
.dieRow.busy  .mark { opacity: .45; }
#receiptVerdict { text-align: center; text-transform: none; letter-spacing: .06em;
                  line-height: 1.5; min-height: 15px; }
#receiptVerdict.good { color: #2f6b34; }
#receiptVerdict.bad  { color: var(--vermilion); }
/* A button that offers rather than insists. */
button.quiet {
  font-size: 12px; letter-spacing: .18em; max-width: 100%;
  background: linear-gradient(180deg, rgba(72,58,28,.9), rgba(46,36,16,.9));
  color: #e6d7ad; border: 1px solid #6b5512;
}

#s5 { justify-content: center; align-items: center; gap: 22px; padding: 20px; }

/* S7 — the antechamber. Same furniture as the title page, so arriving by
   link feels like arriving at DADI rather than at a dialog. */
#s7 { justify-content: center; align-items: center; gap: 16px; padding: 20px 16px; }
#s7 .scroll { width: 100%; max-width: 320px; text-align: center; }
#s7 .row { width: 100%; max-width: 320px; }
#s7Title { font-size: 22px; }
#linkText {
  width: 100%; max-width: 100%; margin-top: 6px;
  font-size: 12px; letter-spacing: .02em;
}
#s7 .note { margin-top: 5px; opacity: .7; }
#wagerAmount { font-family: var(--serif); font-size: 34px; color: var(--ink); }
/* Each part shows only when this player has something to do with it. */
#s7Link, #s7Wager, #s7Sit, #s7Leave { display: none; }
#s7Link.on, #s7Wager.on { display: block; }
#s7Sit.on, #s7Leave.on { display: flex; }
#s5 .verdict { font-size: 27px; color: var(--vermilion); }

/* ==========================================================================
   DICE
   Both kinds are built by dice.js — see that file for how the stone is cut.
   All this stylesheet does is give them somewhere to sit.
     .die    a die at rest in a cell: one element, its face painted on
     .die3d  the thrown die: a WebGL canvas, lit and tumbling
   ========================================================================== */
.die {
  width: calc(var(--cell, 50px) - 8px); height: calc(var(--cell, 50px) - 8px);
  border-radius: 6px;
  background-size: 100% 100%;
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.22),   /* the lit top edge of the stone */
    inset 0 -3px 5px rgba(0,0,0,.30),      /* its shaded underside */
    inset 0 0 0 1px rgba(50,34,16,.55),    /* the cut edge */
    0 2px 3px rgba(0,0,0,.5);              /* and its shadow in the niche */
}
.die.ghost { opacity: .42; }               /* aimed, not yet confirmed */

.die3d { position: relative; line-height: 0; }
.die3d canvas { display: block; }
.die-flat-hero { width: 48px; height: 48px; }   /* if WebGL is unavailable */
.flat-stage { display: flex; gap: 12px; }       /* ...and there are two of them */

/* ==========================================================================
   ANIMATION — the beat budget lives in JS; these are the looks
   ========================================================================== */
@keyframes shimmer { 50% { opacity: .45; } }
@keyframes land    { from { transform: scale(1.4); opacity: .4; } }
@keyframes shake   { 25% { transform: translateX(-5px); }
                     75% { transform: translateX(5px); } }
@keyframes tick    { 50% { transform: scale(1.22); } }

.die.land         { animation: land 150ms ease-out; }
.col.shake        { animation: shake 200ms; }
#offerArea.shake  { animation: shake 200ms; }   /* A1: "take a die first" */
.total.tick .n    { animation: tick 200ms; color: var(--vermilion); }
.cell.doomed      { box-shadow: inset 0 0 0 2px var(--gold),
                                0 0 12px rgba(201,162,39,.65); }
.cell.doomed .die { filter: brightness(1.25) saturate(1.3); }
.cell.fading .die { transition: opacity 250ms, transform 250ms;
                    opacity: 0; transform: scale(.7) rotate(-8deg); }
.col.slide  .die  { transition: transform 150ms; }

/* The receipt's door (2026-08-21): small, quiet, always on the bar. */
#receiptLink {
  font-size: 10px; letter-spacing: .14em; text-transform: uppercase;
  color: #7a5a12; text-decoration: underline; margin-left: 10px;
}

/* ==========================================================================
   DEMO POLISH (game thread, 2026-08-21, at the founder's direct ask before
   an evening showing; the design thread may rework all of it from here).
   Three complaints answered: the gold rules cut the view into slices, the
   tab restructure had crushed S1's breathing room, and the tab bar wore
   button dress instead of tab dress.
   ========================================================================== */

/* The founder's words: "remove the visible scroll lines, they really cut
   into the view." Gone, not thinned — the class stays for the design
   thread, the bars stop printing. Spacing that leaned on them is carried
   by the rhythm rules below. */
.rule-gold { display: none; }

/* The tab wrappers took S1's gap away from what they wrap: the screen's
   own flex gap reaches only its direct children, and the restructure put
   whole sections one level deeper. Each wrapper breathes the same 16px
   the page used to, and the hidden attribute still wins. */
#tabLink, #tabRival, #tabChampion, #brackets, #tableSetup {
  display: flex; flex-direction: column; gap: 16px;
}
#tabLink[hidden], #tabRival[hidden], #tabChampion[hidden],
#brackets[hidden], #tableSetup[hidden] { display: none; }

/* The tab bar reads as tabs, not as four more plaques: quiet text on the
   vellum, the chosen one underlined in gold. The seal-star belongs to
   picked OPTIONS, not to navigation, so it is taken off here. */
#modeTabs {
  gap: 0; margin-top: 2px;
  border-bottom: 1px solid rgba(123, 101, 52, .38);
}
#modeTabs button {
  flex: 1; max-width: none; min-height: 40px; padding: 8px 2px;
  font-size: 10.5px; letter-spacing: .13em;
  color: var(--ink-soft);
  border: 0; border-radius: 0; border-bottom: 2px solid transparent;
  background: none; box-shadow: none;
}
#modeTabs button.picked {
  color: var(--ink);
  background: none; box-shadow: none; text-shadow: none;
  border: 0; border-bottom: 2px solid var(--gold);
}
#modeTabs button.picked::before { content: ""; }
#modeTabs button:active { transform: none; }

/* The sharing row: four small doors, not four commandments. */
#shareRow button {
  font-size: 10px; letter-spacing: .1em; min-height: 36px; padding: 6px 4px;
}

/* The section legends carry the structure now that the bars are gone:
   a touch more air above each, a touch less shout. */
#s1 .legend { margin-bottom: -8px; opacity: .82; }
#s1 #queueNote, #s1 #bracketNote, #s1 #championBlurb { margin-bottom: 0; }

/* The marginal ornament sits back a little so the middle of the page,
   where the founder's eye complained, is the brightest thing on it. */
.edge { opacity: .7; }
.beast { opacity: .78; }

/* --------------------------------------------------------------------------
   GLYPH BUTTONS + THE COLOPHON (founder, 2026-08-21, second demo pass:
   "exceedingly cool glyph... rather than long boring writing and boring
   brown bg"). The option buttons wear a large mark and a whispered
   caption; the picked state trades the brown wax for the masthead's own
   lapis and gold — the one panel on the page the founder's eye liked.
   -------------------------------------------------------------------------- */
button.glyphed {
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px; padding: 8px 4px 7px;
}
button.glyphed .glyph {
  font-size: 27px; line-height: 1;
  font-family: var(--serif);
}
button.glyphed .glyph .ghost { opacity: .32; }
button.glyphed .cap {
  font-size: 8px; letter-spacing: .13em; text-transform: uppercase;
  opacity: .72; line-height: 1.25;
}
button.glyphed.picked::before { content: ""; }   /* the glyph IS the seal */

/* The picked state, re-dressed: lapis under gold, the dropcap's own
   livery. Loud enough to read across a table, and never gold-on-gold. */
button.picked {
  color: #f2e6c0;
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(120, 160, 255, .18), transparent 60%),
    linear-gradient(180deg, var(--lapis) 0%, var(--lapis-deep) 100%);
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(201, 162, 39, .55), 0 2px 8px rgba(10, 16, 34, .5);
  text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
}
button.glyphed.picked .glyph { color: #f0d98a; text-shadow: 0 1px 3px rgba(0,0,0,.55); }
button.glyphed.picked .cap { opacity: .85; }
.spot-mark { color: #4e7c33; }
.pot-mark  { color: #8a6a14; }
button.picked .spot-mark { color: #9fd47a; }
button.picked .pot-mark  { color: #f0d98a; }


/* The roster medallions follow the same livery when chosen. */
.champ.picked {
  background:
    radial-gradient(120% 90% at 50% 0%, rgba(120, 160, 255, .18), transparent 60%),
    linear-gradient(180deg, var(--lapis) 0%, var(--lapis-deep) 100%);
  border-color: var(--gold);
  box-shadow: 0 0 0 1px rgba(201, 162, 39, .55), 0 2px 8px rgba(10, 16, 34, .5);
}

/* The wordmark (plate 090, keyed to alpha): the masthead is the founder's
   own plate, only its ink — no blend tricks needed, the ground is gone. */
.incipit img.wordmark { width: min(330px, 84%); display: block; }
#s7 .incipit img.wordmark { width: min(240px, 70%); margin: 0 auto; }

/* --------------------------------------------------------------------------
   S0 — THE LANDING (founder, 2026-08-22): three doors, the mark above,
   the crest below. And the rules sheet: 90% of the window, centered,
   scrolling inside itself.
   -------------------------------------------------------------------------- */
#s0 {
  justify-content: safe center; align-items: center; gap: 22px;
  padding: 28px 16px calc(20px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto;
}
#s0 .row.landing { width: 100%; }
#s0 .row.landing button {
  flex: 1; max-width: 240px; min-height: 54px;
  font-size: 19px; letter-spacing: .22em;
}

/* The old overlay's #rulesPop rules lived here and OUTLIVED it: the id
   selector kept forcing display:flex onto the closed native <dialog>,
   which WebKit then took into the layout — the invisible 90dvh corpse
   that crushed the landing. Removed 2026-08-22, found by a real WebKit.
   The dialog's own rules live in the native-dialog block below. */
.rules-sheet {
  width: 90vw; height: 90vh; max-width: 560px;
  display: flex; flex-direction: column; gap: 10px;
  padding: 20px 18px;
  border: 2px solid transparent; border-image: var(--leaf) 1;
  box-shadow: 0 12px 48px rgba(0, 0, 0, .6);
}
#rulesBody {
  flex: 1; overflow-y: auto; -webkit-overflow-scrolling: touch;
  font-family: var(--serif); font-size: 14px; line-height: 1.55;
  padding-right: 6px;
}
#rulesBody p { margin: 0 0 12px; }
#rulesBody b { color: #6b4a10; letter-spacing: .04em; }

/* --------------------------------------------------------------------------
   WEB AWESOME IN MANUSCRIPT DRESS (2026-08-22). The library arrives in
   its own neutral livery; these tokens re-pigment every component from
   our palette in one place, and the parts below dress the two components
   the game uses today (the rules dialog, the dial tooltips). The design
   thread inherits this section as the component-theming surface.
   -------------------------------------------------------------------------- */
:root, .wa-theme-default {
  --wa-font-family-body: var(--serif);
  --wa-font-family-heading: var(--serif);
  --wa-color-surface-default: var(--vellum);
  --wa-color-surface-raised: var(--vellum);
  --wa-color-surface-lowered: var(--vellum-dim);
  --wa-color-surface-border: #7b6534;
  --wa-color-text-normal: var(--ink);
  --wa-color-text-quiet: var(--ink-soft);
  --wa-color-text-link: #7a5a12;
  --wa-color-overlay-modal: rgba(12, 8, 4, .62);
  --wa-border-radius-m: 4px;
  --wa-border-radius-l: 6px;
}

/* The rules dialog: 90% of the window (founder's figure), the sheet
   itself already wears parchment — the dialog frame goes transparent
   and lets it. */
.rules-dialog {
  --width: min(90vw, 560px);
}
.rules-dialog::part(dialog) {
  width: min(90vw, 560px); height: 90vh; max-height: 90vh; min-height: 90vh;
  background: none; border: none; box-shadow: none;
  padding: 0; overflow: hidden;
}
.rules-dialog::part(body) { padding: 0; height: 100%; display: flex; }
.rules-dialog .rules-sheet {
  width: 100%; height: 100%;
  border: 2px solid transparent; border-image: var(--leaf) 1;
  box-shadow: 0 12px 48px rgba(0, 0, 0, .6);
}

/* The tooltips speak in the manuscript's whisper. */
wa-tooltip::part(base), wa-tooltip::part(body) {
  font-family: var(--serif); font-size: 11.5px; letter-spacing: .04em;
  background: #2a1c0a; color: #f0e3bd;
  border: 1px solid rgba(201, 162, 39, .55); border-radius: 4px;
  max-width: 230px; text-align: center;
}

/* THE DIALOG THAT WOULD NOT CLOSE (founder's phone, 2026-08-22). Three
   defenses, so no failure mode ever strands the player again:
   1. An un-upgraded component renders NOTHING — before the library
      loads (or if it never does), the dialog's contents and the
      tooltips' texts must not spill into the page as plain text.  */
wa-dialog:not(:defined), wa-tooltip:not(:defined) { display: none !important; }

/* 2. If the library is dead entirely, RULES still works: the bare
      element with [open] becomes the overlay by CSS alone.           */
wa-dialog[open]:not(:defined) {
  display: flex !important; position: fixed; inset: 0; z-index: 90;
  align-items: center; justify-content: center;
  background: rgba(12, 8, 4, .6);
}
wa-dialog[open]:not(:defined) .rules-sheet {
  width: min(90vw, 560px); height: 90vh;
  border: 2px solid transparent; border-image: var(--leaf) 1;
}

/* 3. Reduce Motion (an iOS accessibility setting, common) must not
      freeze the close: the component awaits an animationend, so the
      animation may shrink to a millisecond but never to none.        */
@media (prefers-reduced-motion: reduce) {
  .rules-dialog::part(dialog) { animation-duration: 1ms !important; }
}

/* ==========================================================================
   THE MODERN PASS (founder, 2026-08-22: "rounded corners, beautiful,
   modern"). The manuscript keeps its pigments and its serif voice; this
   layer re-tailors the SHAPES — a radius scale, soft layered depth,
   motion on touch, pill navigation, card rows, glowing focus. Appended
   last on purpose: it outvotes earlier geometry without touching it.
   ========================================================================== */
:root {
  --radius-s: 10px; --radius-m: 14px; --radius-l: 20px; --radius-pill: 999px;
  --shadow-soft: 0 1px 2px rgba(43, 32, 24, .16), 0 5px 16px rgba(43, 32, 24, .20);
  --shadow-lift: 0 2px 5px rgba(43, 32, 24, .18), 0 12px 30px rgba(43, 32, 24, .28);
  --ring: 0 0 0 3px rgba(201, 162, 39, .38);
}

/* Every plaque becomes a soft card that answers the finger. */
button {
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-soft);
  transition: transform .16s ease, box-shadow .16s ease, filter .16s ease;
}
/* A TAP IS NOT A HOVER (2026-09-28). On a phone the tap leaves the button
   "hovered" until the next tap elsewhere, so every plaque pressed and then
   lifted and stayed lifted -- two movements from one tap. The lift answers
   a pointer that really hovers, and only that. */
@media (hover: hover) and (pointer: fine) {
  button:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); }
}
button:active { transform: translateY(1px); box-shadow: 0 1px 3px rgba(43,32,24,.3); }
button:focus-visible { outline: none; box-shadow: var(--shadow-soft), var(--ring); }
button.picked { box-shadow: var(--shadow-soft), 0 0 0 1px rgba(201, 162, 39, .55); }
button.glyphed { border-radius: var(--radius-l); }

/* The tab bar becomes a floating pill track with a lapis pill inside. */
#modeTabs {
  border-bottom: none;
  background: rgba(43, 32, 24, .08);
  border-radius: var(--radius-pill);
  padding: 4px; gap: 2px;
  box-shadow: inset 0 1px 3px rgba(43, 32, 24, .18);
}
#modeTabs button {
  border: 0; border-radius: var(--radius-pill);
  min-height: 36px; box-shadow: none; transform: none;
}
#modeTabs button:hover { transform: none; box-shadow: none; color: var(--ink); }
#modeTabs button.picked {
  border: 0; color: #f2e6c0;
  background: linear-gradient(180deg, var(--lapis) 0%, var(--lapis-deep) 100%);
  box-shadow: 0 2px 8px rgba(16, 24, 44, .45);
}

/* Writing surfaces: rounded, filled, and lit when chosen. */
input[type="text"], input[type="number"] {
  border: 1px solid rgba(123, 101, 52, .45); border-radius: var(--radius-m);
  background: rgba(255, 252, 240, .6);
  box-shadow: inset 0 1px 3px rgba(80, 55, 20, .12);
  transition: box-shadow .16s ease, border-color .16s ease;
}
input[type="text"]:focus, input[type="number"]:focus {
  outline: none; border-color: var(--gold);
  box-shadow: inset 0 1px 3px rgba(80, 55, 20, .12), var(--ring);
}

/* List rows — the lobby, the brackets — sit up as cards. */
.lobby-row {
  background: rgba(255, 252, 240, .55);
  border: 1px solid rgba(123, 101, 52, .25);
  border-radius: var(--radius-m);
  padding: 10px 12px; margin-bottom: 8px;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  box-shadow: var(--shadow-soft);
  transition: transform .16s ease, box-shadow .16s ease;
}
.lobby-row:hover { transform: translateY(-1px); box-shadow: var(--shadow-lift); }

/* The purse reads as a chip. */
#s1Purse {
  display: inline-flex; align-items: baseline; gap: 6px;
  align-self: center;
  background: rgba(255, 252, 240, .6);
  border: 1px solid rgba(123, 101, 52, .3);
  border-radius: var(--radius-pill);
  padding: 6px 16px; box-shadow: var(--shadow-soft);
}
#barPurse {
  border-radius: var(--radius-pill);
  background: rgba(255, 252, 240, .5);
  padding: 4px 12px; box-shadow: var(--shadow-soft);
}

/* The landing's three doors: tall, rounded, and PLAY wears the leaf. */
#s0 .row.landing button {
  border-radius: var(--radius-l); min-height: 56px;
}
#landPlay {
  background:
    radial-gradient(120% 100% at 50% 0%, rgba(255, 250, 230, .5), transparent 55%),
    var(--leaf);
  border-color: #8a6a14; color: #2a1d05;
  text-shadow: 0 1px 0 rgba(255, 240, 190, .6);
  font-weight: 700;
}
#landPlay:hover { filter: brightness(1.05); }

/* The champion medallions and the parchment scrolls soften. */
.champ { border-radius: var(--radius-m); }
.scroll.parchment { border-radius: var(--radius-l); overflow: hidden; }

/* The rules sheet: modern frame — a hairline of gold and real depth
   instead of the square border-image (which cannot round). */
.rules-dialog .rules-sheet, wa-dialog[open]:not(:defined) .rules-sheet {
  border: 1px solid rgba(201, 162, 39, .6); border-image: none;
  border-radius: var(--radius-l);
  box-shadow: 0 24px 70px rgba(0, 0, 0, .55);
  overflow: hidden;
}
.rules-dialog::part(dialog) { border-radius: var(--radius-l); }

/* The whispers round off too. */
wa-tooltip::part(base), wa-tooltip::part(body) { border-radius: var(--radius-s); }

/* The verdict sheet sits as a raised card. */
#s3 .scroll.parchment {
  border-radius: var(--radius-l); box-shadow: var(--shadow-lift);
}

/* --------------------------------------------------------------------------
   THE RULES DIALOG, NATIVE (2026-08-22, third form — final). Sized in
   dvh so it fits what the player actually SEES: vh counts the strip
   under the browser chrome, which is exactly how the sheet ran off the
   bottom of the founder's phone. Entrance and exit are transitions with
   @starting-style and allow-discrete — smooth where the browser knows
   them, instantaneous (and still correct) where it does not.
   -------------------------------------------------------------------------- */
dialog.rules-dialog {
  width: min(92vw, 560px);
  height: 90vh;              /* the fallback */
  height: 90dvh;             /* the truth, where dvh exists */
  max-height: 90dvh;
  padding: 0; border: none; background: transparent;
  margin: auto; overflow: visible;
  opacity: 0; transform: translateY(16px) scale(.97);
  /* Entry animates via @starting-style; the EXIT is instant on purpose.
     The display/overlay allow-discrete transition experiment made
     WebKit lay out the CLOSED dialog — invisible at opacity 0, 90dvh
     tall, squeezing the landing to its padding and eating every tap.
     Proven with a real WebKit (server Playwright, 2026-08-22): one
     line below restores the page. Reliability beats a exit flourish. */
  transition:
    opacity .22s ease,
    transform .26s cubic-bezier(.2, 1.1, .4, 1);
}
dialog.rules-dialog:not([open]) { display: none; }
dialog.rules-dialog[open] { opacity: 1; transform: none; }
@starting-style {
  dialog.rules-dialog[open] { opacity: 0; transform: translateY(16px) scale(.97); }
}
dialog.rules-dialog::backdrop {
  background: rgba(12, 8, 4, 0);
  transition: background .25s ease;
}
dialog.rules-dialog[open]::backdrop { background: rgba(12, 8, 4, .62); }
@starting-style {
  dialog.rules-dialog[open]::backdrop { background: rgba(12, 8, 4, 0); }
}
dialog.rules-dialog .rules-sheet {
  width: 100%; height: 100%;
  display: flex; flex-direction: column; gap: 10px;
  padding: 20px 18px;
}
body:has(dialog.rules-dialog[open]) { overflow: hidden; }

/* The × — a round door in the top corner, thumb-sized, above the sheet. */
.dialog-x {
  position: absolute; top: -12px; right: -8px; z-index: 2;
  width: 42px; height: 42px; min-width: 42px; min-height: 42px;
  padding: 0; border-radius: 50%;
  font-size: 24px; line-height: 1; font-family: var(--serif);
  display: flex; align-items: center; justify-content: center;
  border: 1px solid rgba(201, 162, 39, .6);
  box-shadow: var(--shadow-lift);
}

/* No writing ever escapes its plaque (founder, 2026-08-22: "very bad
   form" — correct). Flex children must be allowed to shrink (min-width:0
   or the text dictates the box), words wrap inside their button, long
   captions break rather than bleed, and the caps breathe a little less
   so three-across rows fit a 360px phone. */
.row button, #s0 .row.landing button, .lobby-row button {
  min-width: 0;
  white-space: normal;
  overflow-wrap: break-word;
  line-height: 1.25;
}
button.glyphed .cap {
  white-space: normal; overflow-wrap: break-word;
  text-wrap: balance;
  letter-spacing: .08em;
  max-width: 100%;
}
#modeTabs button {
  min-width: 0; white-space: normal;
  line-height: 1.2; padding: 6px 3px;
}

/* THE FIT PASS (2026-08-22): a sweep measured every visible button at
   360px and 21 plaques let their writing escape. The cures, by shape:
   words may always wrap; a dial is never squeezed below its glyph; a
   medallion grows to its name; presets and tabs breathe smaller air. */
button { min-width: 0; white-space: normal; overflow-wrap: break-word; }

button.glyphed { height: auto; min-height: 62px; }

/* The =/≤ stake dial was crushed to a sliver beside the field. */
#queueCustomRow input { flex: 1 1 auto; min-width: 0; }
#queueStakeMode { flex: 0 0 auto; min-width: 96px; max-width: 130px; }

/* The champion medallions grow to hold name and appetite. */
#rosterRow .champ { height: auto; min-height: 58px; }

/* The tab pills and the stake presets at a 360px width. */
#modeTabs button { font-size: 10px; letter-spacing: .1em; }
#queueStakes button, #bracketStakes button, #bracketSizes button {
  font-size: 13px; letter-spacing: .08em; padding: 9px 4px;
}

/* List-row actions keep their size; the wording column shrinks instead. */
.lobby-row button { flex: 0 0 auto; padding: 8px 12px; }
.lobby-row span:first-child { min-width: 0; overflow-wrap: break-word; }

/* SLIGHT corners (founder, 2026-08-22: "TOO rounded. Only the corners").
   The radius scale steps down across the board; the taste correction
   rides the same tokens, so one block turns every shape at once. */
:root {
  --radius-s: 4px; --radius-m: 6px; --radius-l: 8px; --radius-pill: 10px;
}
#modeTabs { border-radius: 12px; }
#modeTabs button, #modeTabs button.picked { border-radius: 8px; }
#s1Purse, #barPurse { border-radius: 10px; }
.dialog-x { border-radius: 50%; }   /* a round door stays a round door */

/* --------------------------------------------------------------------------
   THE SPECTACLE (staging, 2026-08-22): Web Awesome's showpieces in
   manuscript dress — the QR door, the champion deck, the accordion
   rules, the gilded patience, the toast rack.
   -------------------------------------------------------------------------- */
wa-carousel:not(:defined), wa-carousel-item:not(:defined),
wa-qr-code:not(:defined), wa-spinner:not(:defined),
wa-toast:not(:defined), wa-toast-item:not(:defined) { display: none; }
wa-details:not(:defined) { display: block; }  /* text readable pre-upgrade */

.qr-court {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  margin-top: 10px; padding: 12px;
  background: rgba(255, 252, 240, .55);
  border: 1px solid rgba(201, 162, 39, .5);
  border-radius: var(--radius-l);
  box-shadow: var(--shadow-soft);
}

#rosterRow { width: 100%; --aspect-ratio: auto; }
#rosterRow::part(pagination-item) { background: rgba(123, 101, 52, .35); }
#rosterRow::part(pagination-item--active) { background: var(--gold); }
#rosterRow wa-carousel-item { display: flex; justify-content: center; }

#rulesBody wa-details {
  border-bottom: 1px solid rgba(123, 101, 52, .3);
  font-size: 14px; line-height: 1.55;
}
#rulesBody wa-details::part(summary) {
  font-family: var(--serif); font-weight: 700; letter-spacing: .06em;
  color: #6b4a10; padding: 10px 2px;
}
#rulesBody wa-details::part(content) { padding: 0 2px 12px; }

wa-spinner {
  --track-color: rgba(123, 101, 52, .25);
  --indicator-color: var(--gold);
}

wa-toast-item::part(base) {
  font-family: var(--serif); letter-spacing: .04em;
  background: #2a1c0a; color: #f0e3bd;
  border: 1px solid rgba(201, 162, 39, .55);
  border-radius: var(--radius-m);
  box-shadow: var(--shadow-lift);
}

/* The accordion's pointer, hand-set (the default chevron is an icon-kit
   glyph this project does not ship): a small gold caret that says
   "this door opens". */
#rulesBody wa-details .quill-mark {
  font-family: var(--serif); font-size: 13px; color: var(--gold);
  opacity: .85; line-height: 1;
}


/* --------------------------------------------------------------------------
   THE FRESCO DOORS (staging, 2026-08-23): the founder's lapis button
   faces under the fresco. Each face is a 4:1 painting — the aspect
   ratio is locked so the goldwork never distorts, and the icon lives
   in the right fifth, so the label sits in the left four-fifths.
   -------------------------------------------------------------------------- */
#s0 .row.landing button {
  background: transparent center / 100% 100% no-repeat;
  border: none; border-radius: 0; box-shadow: none;
  /* Height is width/4 by arithmetic, not aspect-ratio: this WebKit
     will not transfer an aspect height from a flexed width, so the
     face size is stated twice and can never collapse. */
  flex: none; width: min(300px, 86vw); max-width: none;
  height: calc(min(300px, 86vw) / 4); min-height: 0;
  padding: 0 26% 0 8%;
  color: #f0d68a;
  font-family: var(--serif); font-size: 20px; letter-spacing: .24em;
  text-shadow: 0 1px 3px rgba(0, 0, 0, .8);
}
#s0 .row.landing button:active { filter: brightness(1.18); transform: none; }
/* Selector weight must beat the shared button block above, and
   align-self must break flex-stretch or the 4:1 lock never engages. */
#s0 .row.landing button { align-self: center; }
#s0 .row.landing button#landPlay  { background-image: url("/static/art/btn_die.webp"); }
#s0 .row.landing button#landRules { background-image: url("/static/art/btn_book.webp"); }
#s0 .row.landing button#landLogin { background-image: url("/static/art/btn_lion.webp"); }

/* The full logo carries its own tagline; give it more of the fresco. */
#s0 .incipit img.logo-full { width: min(360px, 88%); }






/* --------------------------------------------------------------------------
   THE PARCHMENT GROUND (founder's spec, 2026-08-24 — the rebuild starts
   at the background level). A neutral seamless tile, repeated at 512
   CSS px, image-set() serving the 1024 tile to high-density glass.
   No stretching, no JS, no per-viewport variants, no decoration on
   this layer — everything else rides above as its own layer.
   -------------------------------------------------------------------------- */
.dadi-page {
  background-color: #e7d1a5;
  background-image: -webkit-image-set(
    url("/static/art/dadi-parchment-neutral-seamless-web-512.webp") 1x,
    url("/static/art/dadi-parchment-neutral-seamless-web-1024.webp") 2x);
  background-image: image-set(
    url("/static/art/dadi-parchment-neutral-seamless-web-512.webp") 1x,
    url("/static/art/dadi-parchment-neutral-seamless-web-1024.webp") 2x);
  background-repeat: repeat;
  background-size: 512px 512px;
  background-position: top left;
}

/* --------------------------------------------------------------------------
   THE CLEAN SLATE (founder, 2026-08-24): the rebuild stands on the
   parchment ground alone. The leather board, the painted vellum sheet,
   the vine border, the rosettes and the marginalia beasts all stand
   down. Sheets that rise above the page (rules, gear, wager) keep a
   flat run of the same parchment tile so their words stay on paper.
   -------------------------------------------------------------------------- */
#wrap { background: none; box-shadow: none; }
#page { border: none; border-image: none; }
.edge, .corner, .beast { display: none; }
.parchment {
  background: #e7d1a5;
  background-image: -webkit-image-set(
    url("/static/art/dadi-parchment-neutral-seamless-web-512.webp") 1x,
    url("/static/art/dadi-parchment-neutral-seamless-web-1024.webp") 2x);
  background-image: image-set(
    url("/static/art/dadi-parchment-neutral-seamless-web-512.webp") 1x,
    url("/static/art/dadi-parchment-neutral-seamless-web-1024.webp") 2x);
  background-repeat: repeat; background-size: 512px 512px;
}
.parchment::before, .parchment::after { display: none; }
#s0 { background: none; }

/* The codex mark paints its own motto; the typed one stands down. */
.subtitle { display: none; }

/* --------------------------------------------------------------------------
   THE SPLASH, ONE DOOR (founder, 2026-08-24): the codex PLAY plaque,
   label painted into the art, alone under the mark. The other doors
   leave the splash; the codex set (start, enter, log-in, blank) is cut
   and banked as cbtn_*.webp for the screens being rebuilt next.
   -------------------------------------------------------------------------- */
#s0 .row.landing:has(#landRules), #s0 .row.landing:has(#landLogin) {
  display: none;
}
#s0 .row.landing button#landPlay {
  background: url("/static/art/cbtn_play.webp") center / 100% 100% no-repeat;
  border: none; border-radius: 0; box-shadow: none;
  width: min(295px, 74vw); height: calc(min(295px, 74vw) / 4.211);
  margin-top: 12px;
  max-width: none; min-height: 0; flex: none; align-self: center;
  padding: 0;
  color: transparent; font-size: 16px;  /* the art speaks; readers still hear PLAY */
  text-shadow: none;
}
#s0 .row.landing button#landPlay:active { filter: brightness(1.15); }

/* The splash procession (founder's mockup): mark, beast, pitch, door. */
#s0 #splashBeast { width: min(350px, 87%); margin: -10px auto 0; }
#s0 .pitch-art { width: min(330px, 82%); margin: 2px auto 0; }

/* The 3-line menu, in the manuscript's own ink. */
.burger {
  /* anchored to the page like the frame: it scrolls with the parchment.
     On the body, so the anchor is the page, not the game column. */
  position: absolute; top: 30px; right: 16px; z-index: 50;
  display: none;                 /* the splash gate below turns it on */
  width: 44px; height: 44px; padding: 10px 9px;
  flex-direction: column; justify-content: space-between;
  background: none; border: none; box-shadow: none; border-radius: 6px;
  min-height: 0; cursor: pointer;
}
.burger span {
  /* the shared button dress centers children, which collapses an
     empty span to zero — the bars state their width */
  display: block; width: 100%; height: 4px; border-radius: 2px;
  background: #332514;
}
.burger:active { filter: brightness(1.4); }

/* --------------------------------------------------------------------------
   THE FLOWERED FRAME (founder's kit, 2026-08-24, joints re-measured
   at low alpha threshold): the corner arms are 19-21px thick at 0-2px
   inset in the 1153-native plate — the rails now match that band
   exactly, and they run 24px UNDER each corner box (corners stack
   above) so the fatter arm ends swallow the joint. One knob: --fk is
   CSS px per native px; --fw grew for the mockup's mobile presence.
   -------------------------------------------------------------------------- */
:root { --fw: min(50vw, 40vh, 330px); --fk: calc(var(--fw) / 1153);
        /* the wire breathes: a few px between frame and screen edge */
        --fi: clamp(6px, 1.6vw, 14px); }
/* The frame belongs to the PAGE, not the window (founder, 2026-08-24):
   absolute within the document, so it wraps the full rendered length
   and scrolls with the parchment. The rails repeat along their run
   instead of stretching, so a long page keeps the wire's grain. */
html { height: auto; min-height: 100%; }
body.dadi-page { position: relative; height: auto; min-height: 100dvh; }
#wrap { min-height: 100dvh; }
.bframe {
  position: absolute; z-index: 40; pointer-events: none;
  background-repeat: no-repeat; background-size: 100% 100%;
}
.bf-l, .bf-r { background-size: 100% auto; background-repeat: repeat-y; }
.bf-t, .bf-b { background-size: auto 100%; background-repeat: repeat-x; }
.bf-tl, .bf-tr, .bf-bl, .bf-br { z-index: 41; }
/* The tl plate reads a touch small next to its siblings: +7%. */
.bf-tl { top: var(--fi); left: var(--fi);
  width: calc(var(--fw) * 1.07); height: calc(1162 * var(--fk) * 1.07);
  background-image: url("/static/art/bf_tl.webp"); }
/* The updated tr plate (1303x1207) bakes its wire 23-48 native deep;
   it shifts up 26 and right 34 so that wire lands on the shared line. */
.bf-tr { top: calc(var(--fi) - 26 * var(--fk));
  right: calc(var(--fi) - 34 * var(--fk));
  width: calc(1303 * var(--fk)); height: calc(1207 * var(--fk));
  background-image: url("/static/art/bf_tr.webp"); }
.bf-bl { bottom: var(--fi); left: var(--fi); width: calc(1100 * var(--fk));
  height: calc(1163 * var(--fk));
  background-image: url("/static/art/bf_bl.webp"); }
/* The br plate is drawn smaller (1079x961) than its three siblings;
   it is displayed at 1.2x so all four corners hold the same ground.
   Its wire is flush, so the joint tolerances survive the scaling. */
.bf-br { bottom: var(--fi); right: var(--fi); width: calc(1295 * var(--fk));
  height: calc(1153 * var(--fk));
  background-image: url("/static/art/bf_br.webp"); }
.bf-t, .bf-b { height: calc(19 * var(--fk));
  background-image: url("/static/art/bf_h.webp"); }
.bf-t { top: var(--fi);
  left: calc(var(--fi) + 1129 * var(--fk));
  right: calc(var(--fi) + 1245 * var(--fk)); }
.bf-b { bottom: calc(var(--fi) + 2 * var(--fk));
  left: calc(var(--fi) + 1076 * var(--fk));
  right: calc(var(--fi) + 1266 * var(--fk)); }
.bf-r { width: calc(19 * var(--fk)); }
.bf-l, .bf-r { background-image: url("/static/art/bf_v.webp"); }
/* the two left plates disagree (tl arm 19 native, bl arm 33); the left
   rail splits the difference and the corners' overlap eats the rest */
.bf-l { width: calc(24 * var(--fk)); }
.bf-l { left: calc(var(--fi) + 2 * var(--fk));
  top: calc(var(--fi) + 1138 * var(--fk));
  bottom: calc(var(--fi) + 1139 * var(--fk)); }
.bf-r { right: calc(var(--fi) + 2 * var(--fk));
  top: calc(var(--fi) + 1157 * var(--fk));
  bottom: calc(var(--fi) + 1124 * var(--fk)); }

/* The beast, 10% larger by decree. */
/* The beast bleeds past the COLUMN but never past the SCREEN: measured
   in viewport units, so on any phone he keeps a hand's width of
   parchment at each edge and centres true. */
#s0 #splashBeast {
  width: min(460px, calc(100vw - 30px)); max-width: none;
  margin: 14px auto 0;   /* air under the mark, then the beast BLEEDS */
}
#page { overflow: visible; }   /* the old sheet clipped; the beast bleeds */
/* #s0 carried overflow-y:auto, and CSS computes the other axis to auto
   with it — so the splash was its own horizontal scroller, which both
   made the right-hand scroll and shoved the beast off centre (a scroll
   box cannot scroll left of its origin, so the left bleed was eaten).
   The PAGE scrolls now, as the page-anchored frame already wants. */
#s0 { overflow: visible; }

/* The sample hangs the mark just under the top wire, not at center. */
#s0 { justify-content: flex-start; padding-top: 98px; gap: 14px; }

/* The sandwich serves only on the splash, for now. */
body:has(#s0.on) .burger { display: flex; }

/* The door sinks to the page's end, a hand's width above the garden:
   the row rides flex's auto margin down, and the padding is the gap. */
#s0 .pitch-art { margin-top: auto; }  /* spare space pools above the writing */
#s0 .row.landing:has(#landPlay) { margin-top: 0; }
#s0 { padding-bottom: calc(var(--fw) * 0.3); }

/* --------------------------------------------------------------------------
   THE SPLASH, FLUID (founder, 2026-08-24: "scalable and resizeable...
   never end up breaking"). Everything above this line accumulated as
   fixed pixels over a night of nudges — 98px here, 26px there — which
   is precisely what shatters on a screen nobody tested. This block is
   the single authority on splash measurement, and it states every
   dimension as a fraction of two knobs:

     --col   the column the page actually occupies, capped at its own
             maximum, so 320px and 430px phones scale the same picture
     --fw    the frame corner (already viewport- AND height-bound), so
             the mark always clears the sprays by the same proportion

   Nothing here is a bare pixel except the touch targets, which have a
   floor because a thumb does not shrink with the viewport.
   -------------------------------------------------------------------------- */
#s0 {
  --col: min(100vw, 430px);
  --door: min(295px, 74vw);      /* stated once, used for width AND height */

  justify-content: flex-start;
  padding-top: calc(var(--fw) * 0.5);      /* under the sprays, always */
  padding-bottom: calc(var(--fw) * 0.3);
  gap: calc(var(--col) * 0.035);
  min-height: 100dvh;
}
#s0 .incipit img.logo-full { width: min(360px, 88%); }
#s0 #splashBeast {
  width: min(460px, calc(100vw - 30px)); max-width: none;
  /* NOT margin:auto. An auto margin resolves to zero when the free
     space is negative, and auto margins outrank align-items — so a
     bleeding beast got pinned to the column's left edge and spilled
     every stolen pixel out the right, which is the scroll the founder
     saw. align-self does the centring and overflows both sides evenly. */
  align-self: center;
  margin: calc(var(--col) * 0.035) 0 0;
}
#s0 .pitch-art { width: min(330px, 82%); margin-top: auto; }
#s0 .row.landing button#landPlay {
  width: var(--door); height: calc(var(--door) / 4.211);
  margin-top: calc(var(--col) * 0.03);
  font-size: calc(var(--col) * 0.045);
}
.burger {
  top: calc(var(--fw) * 0.16);
  right: calc(var(--fi) + var(--fw) * 0.05);
  width: clamp(38px, 11vw, 48px);          /* a thumb does not scale */
  height: clamp(38px, 11vw, 48px);
  padding: calc(var(--fw) * 0.05) calc(var(--fw) * 0.045);
}
.burger span { height: clamp(3px, 0.9vw, 4px); }

/* --------------------------------------------------------------------------
   THE NINJA-OWL RIM (founder's second kit, 2026-08-24) — a heavier
   frame than the baboon's: a double wire, red banded in gold, with
   dense vines climbing it, and bottom corners drawn deliberately tall.

   The kit's plates are drawn at FOUR DIFFERENT native scales — their
   wires measure 40, 48, 28 and 31 native px — so displaying them at a
   common width would give the contour four different thicknesses. The
   scale of each piece is therefore derived from its own wire: one knob,
   --ow, is the wire's thickness in CSS px, and every width, height and
   offset below is that knob times the plate's own ratio. Each plate is
   then positioned by WHERE ITS WIRE SITS, not by its box, since the
   wires sit at different insets in their canvases (the bottom-left
   plate carries 17 native px of margin before its wire, the
   bottom-right none) — which is what makes the seam vanish.

   The baguettes are cropped to the wire itself, so those divs ARE the
   contour and simply run between the corner boxes.
   -------------------------------------------------------------------------- */
body.rim-owl {
  --ow: clamp(3.5px, 1.35vw, 9px);
  /* The lower corners run heavier than the upper ones (founder,
     2026-08-24) so their weeds climb higher up the page. Their wire
     scales with them — the whole plate is one drawing — so the bottom
     rail takes this knob too, and the side rails split the difference
     between the corner they meet at each end. */
  --owb: calc(var(--ow) * 1.30);
}

/* Every plate draws its VERTICAL wire 8-18% thicker than its
   horizontal one — consistently, in all four — so no single scale can
   match both. Each plate is therefore scaled by its HORIZONTAL wire,
   which makes the top and bottom rails exact at every junction, and
   the side rails are given the thickness the vertical wires actually
   render at (1.09 and 1.17 of the knob), splitting the difference
   between the two plates each one meets. Every junction now sits
   within ~1.5%. Offsets are each plate's own wire inset, so the
   contour line is continuous rather than the boxes being flush. */
body.rim-owl .bf-tl {
  width: calc(32.053 * var(--ow)); height: calc(32.711 * var(--ow));
  top: var(--fi); left: calc(var(--fi) - 0.026 * var(--ow));
  background-image: url("/static/art/ow_tl.webp");
}
body.rim-owl .bf-tr {
  width: calc(26.136 * var(--ow)); height: calc(30.727 * var(--ow));
  top: calc(var(--fi) - 0.091 * var(--ow));
  right: calc(var(--fi) - 0.068 * var(--ow));
  background-image: url("/static/art/ow_tr.webp");
}
body.rim-owl .bf-bl {
  width: calc(32.852 * var(--owb)); height: calc(65.704 * var(--owb));
  bottom: calc(var(--fi) - 0.222 * var(--owb));
  left: calc(var(--fi) - 0.630 * var(--owb));
  background-image: url("/static/art/ow_bl.webp");
}
body.rim-owl .bf-br {
  width: calc(29.821 * var(--owb)); height: calc(64.143 * var(--owb));
  bottom: calc(var(--fi) - 0.036 * var(--owb));
  right: var(--fi);
  background-image: url("/static/art/ow_br.webp");
}

/* The rails run between the corner boxes, a hair under each so no
   rounding error can open a hairline of parchment. */
/* The connectors are cut from the CORNERS' OWN WIRE, not from the
   supplied baguettes: those carry a different treatment — continuous
   gold stripes down both edges where the corners have marbled red
   inside a dark outline — so no alignment could ever blend them. The
   left rail is a slice of the bottom-left plate's wire, the right of
   the bottom-right's, the horizontal of the top-left's. The contour is
   now literally one line of paint. (ow_v.webp and the supplied
   baguettes remain in the tree if that brighter banding is wanted.) */
body.rim-owl .bf-l { background-image: url("/static/art/ow_vl.webp"); }
body.rim-owl .bf-r { background-image: url("/static/art/ow_vr.webp"); }
body.rim-owl .bf-l {
  width: calc(1.24 * var(--ow)); left: var(--fi);
  top: calc(var(--fi) + 32.711 * var(--ow) - 1px);
  bottom: calc(var(--fi) + 65.482 * var(--owb) - 1px);
}
body.rim-owl .bf-r {
  width: calc(1.33 * var(--ow)); right: var(--fi);
  top: calc(var(--fi) + 30.636 * var(--ow) - 1px);
  bottom: calc(var(--fi) + 64.107 * var(--owb) - 1px);
}
body.rim-owl .bf-t, body.rim-owl .bf-b {
  height: var(--ow); background-image: url("/static/art/ow_h.webp");
}
body.rim-owl .bf-t {
  top: var(--fi);
  left: calc(var(--fi) + 32.027 * var(--ow) - 1px);
  right: calc(var(--fi) + 26.068 * var(--ow) - 1px);
}
body.rim-owl .bf-b {
  bottom: var(--fi); height: var(--owb);
  left: calc(var(--fi) + 32.222 * var(--owb) - 1px);
  right: calc(var(--fi) + 29.821 * var(--owb) - 1px);
}

/* The owl's top-right plate wraps the menu rather than dodging it, so
   the sandwich rides a little lower and further in. */
body.rim-owl .burger {
  top: calc(var(--fi) + 3.4 * var(--ow));
  right: calc(var(--fi) + 3.2 * var(--ow));
}

/* The owl's composition (founder, 2026-08-24): its frame carries more
   marginalia than the baboon's, so the beast and the door step back to
   let the weeds have the room. The baboon's splash is untouched. */
body.rim-owl #s0 {
  --door: min(235px, 60vw);
  /* The weeds leave a channel of about 250px between them at the
     height the writing sits — measured off the plates' own alpha — so
     the pitch is cut to fit it rather than being overgrown. */
  /* The whole stack rides higher on this rim (founder, 2026-08-24):
     the mark by a shallower top, and the writing and the door — which
     hang off the bottom, not the top — by a deeper one. Both ends lift
     rather than only the head. */
  padding-top: calc(var(--fw) * 0.36);
  padding-bottom: calc(var(--owb) * 9);
  /* #s0 asks for a full 100dvh, but it sits inside 48px of ancestor
     padding, so the page has always been 48px taller than the glass —
     a phantom scroll on BOTH rims. The flex chain above already fills
     the height, so the owl simply stops asking. The baboon keeps its
     min-height: correcting it there would re-balance a composition the
     founder has already called finished. */
  min-height: 0;
}
body.rim-owl #s0 #splashBeast { width: min(365px, calc(100vw - 96px)); }
body.rim-owl #s0 .pitch-art { width: min(248px, 64%); }

/* --------------------------------------------------------------------------
   THE HOVERING DIE (founder, 2026-08-24). Four attempts to BUILD a die
   out of geometry — a textured cube, a cel-shaded one, twelve bevelled
   edge strips, a hundred and twenty drawn frames — all failed the same
   way: none had any bulge. The painted die swells at its faces, its ink
   thickens and thins, its corners are soft, and none of that survives
   reconstruction from planes. So the die is the illuminator's own,
   lifted whole off the plate and hung back in the same hand.

   CSS adds only what paint cannot: a rise, a lean on a different beat
   so the two never march together, and a warm aura — the plate already
   bursts gold rays off this die and the glow finishes the thought.

   EVERY NUMBER BELOW IS A DIAL. They are declared once here and read by
   the rules and the keyframes alike, so `?dials` can turn any of them
   live on the device doing the looking. Ornament, all of it: the dice
   that decide a match come from the sky.
   -------------------------------------------------------------------------- */
#s0 { --beastW: min(460px, calc(100vw - 30px)); }
body.rim-owl #s0 { --beastW: min(365px, calc(100vw - 96px)); }

.beast-stage {
  position: relative; align-self: center;
  width: var(--beastW); max-width: none;
  margin: calc(var(--col) * 0.035) 0 0;
}
#beastBody { position: relative; z-index: 1; width: 100%; }
#s0 #splashBeast { width: 100%; max-width: none; display: block; margin: 0; }
.beast-face-layer {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: contain; pointer-events: none;
}
.beast-face-layer { z-index: 2; opacity: 0; }
.beast-face-layer[hidden], .die-magic-piece[hidden] { display: none; }

#dieLev {
  /* the dials, with the values the founder settled on */
  --die-w: 17.6%;         /* deliberately larger: the die is the project's emblem */
  --die-grow: 1;          /* how much bigger than --die-w it is drawn (the size dial) */
  --die-x: 24.7%;         /* and sat here in it */
  --die-y: 21.9%;
  --die-bob: 60%;         /* how far it rises */
  --die-bob-beat: 0.85s;
  --die-motion: 1;        /* damped, not silenced, for reduced motion */
  --die-turn: 0;          /* the old squash cheat, kept at zero */
  --die-turn-beat: 5.5s;
  --die-lean: 4.5deg;     /* how far it leans */
  --die-lean-beat: 7.3s;
  --die-glow-core: 2px;   /* the halo that hugs the ink */
  --die-glow-bloom: 7px;
  --die-glow-hue: 255, 190, 74;
  --die-aura: 250%;       /* and the light it throws on the parchment */
  --die-aura-strength: 1;
  --die-glow-hot: 255, 247, 202;
  --die-glow-primary: 241, 184, 50;
  --die-glow-deep: 137, 79, 14;
  --die-glow-opacity: 1;
  --die-glow-brightness: 1;
  --die-glow-radius-scale: 1;
  --die-glow-core-scale: 1;
  --die-glow-ray-length-scale: 1;
  --die-glow-ray-width: 1.2px;
  --die-glow-pulse-duration: 4.6s;
  --die-glow-drift-duration: 120s;
  --die-glow-drift-turn: 1turn;
  --die-axis-tilt: 0deg;
  --die-spin-duration: 7.5s;

  position: absolute; left: var(--die-x); top: var(--die-y);
  width: calc(var(--die-w) * var(--die-grow)); transform: translate(-50%, -50%);
  pointer-events: none; display: none; z-index: 4; /* only the hand that lacks one */
}
body.beast-baboon #dieLev { display: block; }
body.beast-royal_baboon #dieLev { display: block; }

/* Two shells, two beats: nesting keeps the periods independent, so the
   rise and the lean drift in and out of step the way a floating thing
   does, instead of nodding in time like a metronome. */
#dieBob  { animation: dieBob var(--die-bob-beat) ease-in-out infinite alternate; }
#dieTilt { animation: dieTilt var(--die-lean-beat) ease-in-out infinite alternate; }
#dieArt {
  display: block; width: 100%;
  /* the halo follows the painted silhouette, because drop-shadow reads
     alpha rather than the box */
  filter: drop-shadow(0 0 var(--die-glow-core) rgba(255, 226, 150, .95))
          drop-shadow(0 0 var(--die-glow-bloom) rgba(var(--die-glow-hue), .60));
}

/* A real six-faced die. The old painted crop remains directly underneath as
   a no-3D fallback; living rigs use this cube when preserve-3d is available.
   The registered corner is top 1 / front 5 / right 3. Its opposites are
   bottom 6 / back 2 / left 4, so every opposing pair totals seven. */
#dieTurnly { min-height: 1px; }
#dieCubeScene {
  position: relative; z-index: 2; display: none;
  width: 100%; aspect-ratio: 1; overflow: visible;
  container-type: inline-size;
  perspective: 430cqw;
  perspective-origin: 50% 46%;
}
#dieIllustrationCanvas {
  position: absolute; inset: 0; z-index: 3;
  display: block; width: 100%; height: 100%;
  opacity: 0; pointer-events: none;
  transition: opacity 120ms linear;
}
body.die-living.die-illustrated #dieCubeScene { display: block; }
body.die-living.die-illustrated #dieIllustrationCanvas { opacity: 1; }
body.die-living.die-illustrated #dieAxisTilt { display: none; }
body.die-living.die-illustrated #dieArt { display: none; }
body.die-illustration-failed #dieIllustrationCanvas { display: none; }
#dieAxisTilt,
#dieSpinner,
#dieRestPose {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  transform-origin: 50% 50%;
}
#dieAxisTilt { transform: rotateZ(var(--die-axis-tilt)); }
#dieSpinner {
  /* Spell out the vector so zero tilt has one unambiguous meaning:
     rotation around the horizontal X axis, never the vertical Y axis. */
  animation: dieCubeSpinX var(--die-spin-duration) linear infinite;
  will-change: transform;
}
#dieRestPose { transform: rotateX(-18deg) rotateY(-32deg); }
#dieCube {
  --die-cube-edge: 78cqw;
  --die-cube-half: 39cqw;
  --die-surface:
    radial-gradient(ellipse at 24% 18%, rgba(255, 251, 224, .72) 0 13%, transparent 46%),
    linear-gradient(145deg, #fff2cf 0%, #e6c795 58%, #b98750 100%);
  --die-edge: #a77a38;
  --die-pip-fill: #352317;
  --die-pip-ring: #8d642e;
  position: absolute; left: 11cqw; top: 11cqw;
  width: var(--die-cube-edge); height: var(--die-cube-edge);
  transform-style: preserve-3d;
}
.die-face {
  position: absolute; inset: 0; display: block;
  overflow: hidden; border-radius: 13%;
  border: max(2px, 1.8cqw) solid #281b12;
  background: var(--die-surface);
  background-color: #e6c795;
  box-shadow:
    inset 0 0 0 max(1px, 1.1cqw) var(--die-edge),
    inset 2.5cqw 2.5cqw 4.5cqw rgba(255, 252, 224, .31),
    inset -3cqw -3cqw 5.4cqw rgba(36, 21, 10, .27),
    inset 0 0 5cqw 1.2cqw rgba(36, 21, 10, .12),
    0 .8cqw 1.8cqw rgba(31, 18, 8, .40);
  backface-visibility: hidden;
  transform-style: preserve-3d;
}
.die-face::before {
  content: ""; position: absolute; inset: 0; z-index: 0;
  border-radius: inherit; pointer-events: none;
  background:
    radial-gradient(ellipse at 35% 30%,
      rgba(255, 255, 240, .34) 0 9%,
      rgba(255, 255, 240, .12) 38%,
      transparent 60%),
    radial-gradient(ellipse at 50% 50%,
      transparent 43%,
      rgba(48, 27, 12, .08) 68%,
      rgba(48, 27, 12, .24) 100%),
    linear-gradient(128deg,
      rgba(255, 255, 255, .16), transparent 31% 67%, rgba(31, 18, 9, .14));
}
.die-face::after {
  content: ""; position: absolute; inset: 2.4cqw; z-index: 1;
  border: max(1px, .8cqw) solid var(--die-edge);
  border-radius: 10%; opacity: .74; pointer-events: none;
  box-shadow:
    inset 0 .7cqw 1.2cqw rgba(255, 247, 211, .18),
    0 .35cqw .55cqw rgba(36, 20, 9, .22);
}
.die-face-top    { transform: rotateX( 90deg) translateZ(var(--die-cube-half)); }
.die-face-bottom { transform: rotateX(-90deg) translateZ(var(--die-cube-half)); }
.die-face-front  { transform: rotateY(  0deg) translateZ(var(--die-cube-half)); }
.die-face-back   { transform: rotateY(180deg) translateZ(var(--die-cube-half)); }
.die-face-right  { transform: rotateY( 90deg) translateZ(var(--die-cube-half)); }
.die-face-left   { transform: rotateY(-90deg) translateZ(var(--die-cube-half)); }
.die-pip {
  position: absolute; z-index: 2; width: 17%; aspect-ratio: 1;
  border-radius: 48% 52% 47% 53% / 52% 47% 53% 48%;
  background: var(--die-pip-fill);
  border: max(1.5px, 1.15cqw) solid var(--die-pip-ring);
  box-shadow:
    inset 0 .6cqw .9cqw rgba(255, 244, 202, .17),
    inset 0 -.9cqw 1.2cqw rgba(0, 0, 0, .46),
    0 .65cqw .55cqw rgba(36, 20, 9, .34);
  transform: translateZ(.35cqw);
}
.die-pip.at-tl { left: 14%; top: 14%; }
.die-pip.at-tr { right: 14%; top: 14%; }
.die-pip.at-ml { left: 14%; top: 41.5%; }
.die-pip.at-cc { left: 41.5%; top: 41.5%; }
.die-pip.at-mr { right: 14%; top: 41.5%; }
.die-pip.at-bl { left: 14%; bottom: 14%; }
.die-pip.at-br { right: 14%; bottom: 14%; }

/* Forty lightweight skins, drawn from mineral, organic, fired, and precious surfaces.
   The large reference boards set each material's geological scale; their
   stencils and carved furniture are deliberately not copied onto the die. */
#dieCube[data-material="alabaster"] {
  --die-surface:
    linear-gradient(124deg,
      transparent 0 27%, rgba(239,212,156,.34) 28%, rgba(174,111,27,.22) 28.7%,
      rgba(255,247,222,.30) 29.5%, transparent 30.4% 72%,
      rgba(177,115,31,.17) 73%, transparent 74%),
    radial-gradient(ellipse at 22% 18%, rgba(255,253,239,.88) 0 12%, transparent 46%),
    radial-gradient(ellipse at 72% 69%, rgba(188,151,103,.15) 0 15%, transparent 49%),
    radial-gradient(ellipse at 42% 54%, rgba(255,247,222,.44) 0 18%, transparent 55%),
    linear-gradient(145deg,#fff9e8 0%,#ead9bd 58%,#aa8457 100%);
  --die-edge:#cdb38a; --die-pip-fill:#a66d19; --die-pip-ring:#dfb755;
}
#dieCube[data-material="marble"] {
  --die-surface:
    linear-gradient(121deg, transparent 0 29%, rgba(121,124,128,.10) 30%, rgba(255,255,255,.32) 30.7%, transparent 31.5% 68%, rgba(117,120,124,.14) 69%, transparent 70%),
    linear-gradient(38deg, transparent 0 45%, rgba(157,157,154,.12) 46%, transparent 47% 73%, rgba(255,255,255,.28) 74%, transparent 75%),
    radial-gradient(ellipse at 22% 18%, rgba(255,255,255,.66), transparent 48%),
    linear-gradient(145deg,#fffdf6,#ded9cf 58%,#98928b);
  --die-edge:#c9bdab; --die-pip-fill:#9c6a25; --die-pip-ring:#d8b04f;
}
#dieCube[data-material="lapis"] {
  --die-surface:
    radial-gradient(circle at 19% 24%, #d0a23b 0 .85cqw, transparent 1.15cqw),
    radial-gradient(circle at 73% 68%, rgba(78,119,186,.60) 0 4%, transparent 29%),
    radial-gradient(circle at 56% 37%, rgba(208,162,59,.78) 0 .55cqw, transparent .88cqw),
    radial-gradient(circle at 83% 19%, rgba(208,162,59,.58) 0 .35cqw, transparent .66cqw),
    linear-gradient(127deg, transparent 0 64%, rgba(208,162,59,.28) 65%, transparent 66%),
    linear-gradient(145deg,#386dc0,#153f91 55%,#07173f);
  --die-edge:#c89a36; --die-pip-fill:#d5a83e; --die-pip-ring:#f0ca69;
}
#dieCube[data-material="terracotta"] {
  --die-surface:
    radial-gradient(circle at 22% 27%, rgba(75,29,17,.28) 0 .75cqw, transparent 1.15cqw),
    radial-gradient(circle at 76% 69%, rgba(247,154,96,.19) 0 11%, transparent 37%),
    radial-gradient(circle at 61% 21%, rgba(74,28,16,.18) 0 .42cqw, transparent .78cqw),
    radial-gradient(circle at 34% 78%, rgba(74,28,16,.16) 0 .32cqw, transparent .66cqw),
    linear-gradient(145deg,#df8457,#b75431 58%,#672717);
  --die-edge:#793a23; --die-pip-fill:#321a12; --die-pip-ring:#6a321f;
}
#dieCube[data-material="gilded-bronze"] {
  --die-surface:
    linear-gradient(112deg, transparent 0 24%, rgba(255,224,154,.28) 28%, transparent 35% 72%, rgba(46,92,76,.15) 75%, transparent 81%),
    linear-gradient(145deg,#e2bd61,#9a6729 48%,#4c2e17);
  --die-edge:#efcb6c; --die-pip-fill:#24170f; --die-pip-ring:#d3a948;
}
#dieCube[data-material="porphyry"] {
  --die-surface:
    radial-gradient(circle at 20% 28%, rgba(208,172,162,.62) 0 1.05cqw, transparent 1.42cqw),
    radial-gradient(circle at 69% 72%, rgba(185,140,128,.56) 0 .83cqw, transparent 1.17cqw),
    radial-gradient(circle at 81% 23%, rgba(208,172,162,.44) 0 .58cqw, transparent .92cqw),
    radial-gradient(circle at 42% 52%, rgba(164,118,111,.42) 0 .45cqw, transparent .76cqw),
    radial-gradient(circle at 57% 13%, rgba(208,172,162,.38) 0 .34cqw, transparent .66cqw),
    linear-gradient(145deg,#8c4d61,#5d2942 56%,#290f20);
  --die-edge:#a77d39; --die-pip-fill:#d2a444; --die-pip-ring:#edc76c;
}
#dieCube[data-material="murano"] {
  --die-surface:
    radial-gradient(circle at 23% 20%, rgba(255,255,238,.52) 0 3%, transparent 17%),
    radial-gradient(circle at 73% 66%, rgba(201,236,225,.32) 0 3%, transparent 7%),
    radial-gradient(circle at 31% 76%, rgba(201,236,225,.24) 0 2%, transparent 5%),
    linear-gradient(145deg,#3c8990,#155563 55%,#082e3d);
  --die-edge:#aa8542; --die-pip-fill:#e0c98e; --die-pip-ring:#4c321b;
}
#dieCube[data-material="mother-of-pearl"] {
  --die-surface:
    radial-gradient(ellipse at 24% 18%, rgba(255,253,244,.82) 0 13%, transparent 46%),
    radial-gradient(ellipse at 27% 71%, rgba(116,198,201,.47) 0 18%, transparent 54%),
    radial-gradient(ellipse at 76% 30%, rgba(219,154,190,.46) 0 18%, transparent 53%),
    radial-gradient(ellipse at 72% 74%, rgba(244,219,151,.42) 0 17%, transparent 48%),
    conic-gradient(from 28deg at 55% 48%, rgba(244,219,151,.32), rgba(116,198,201,.39), rgba(161,177,227,.36), rgba(219,154,190,.40), rgba(244,219,151,.32)),
    #e9e4da;
  --die-edge:#c9b992; --die-pip-fill:#a77928; --die-pip-ring:#d9b553;
}
#dieCube[data-material="onyx"] {
  --die-surface:
    radial-gradient(ellipse 145% 92% at -18% 106%,
      transparent 0 45%, rgba(94,95,91,.25) 47%, rgba(224,222,213,.38) 49.2%,
      rgba(83,84,80,.22) 51.5%, transparent 55%),
    radial-gradient(ellipse 126% 86% at 113% -8%,
      transparent 0 53%, rgba(99,99,94,.19) 55%, rgba(194,193,185,.30) 57.2%,
      transparent 61%),
    radial-gradient(ellipse 155% 65% at 42% 105%,
      transparent 0 61%, rgba(122,122,116,.16) 62%, rgba(230,228,219,.24) 63%,
      transparent 65%),
    linear-gradient(128deg,
      transparent 0 26%, rgba(207,205,197,.17) 27%, rgba(83,84,80,.24) 29.4%,
      transparent 32% 68%, rgba(183,181,174,.15) 69%, transparent 71%),
    linear-gradient(145deg,#373735 0%,#111212 51%,#010202 100%);
  --die-edge:#343330; --die-pip-fill:#c89632; --die-pip-ring:#e2ba56;
}
#dieCube[data-material="jade-gilt"] {
  --die-surface:
    radial-gradient(ellipse at 20% 25%, rgba(145,170,130,.28) 0 12%, transparent 45%),
    radial-gradient(ellipse at 77% 68%, rgba(17,60,40,.28) 0 15%, transparent 48%),
    radial-gradient(ellipse at 59% 31%, rgba(177,191,155,.17) 0 11%, transparent 38%),
    linear-gradient(126deg, transparent 0 43%, rgba(177,191,155,.20) 44%, transparent 45%),
    linear-gradient(145deg,#78926e,#315b3e 56%,#132f22);
  --die-edge:#c49a3d; --die-pip-fill:#d2a448; --die-pip-ring:#edc96e;
}
#dieCube[data-material="rose-quartz"] {
  --die-surface:
    radial-gradient(ellipse at 22% 20%, rgba(255,240,235,.72) 0 13%, transparent 48%),
    radial-gradient(ellipse at 74% 68%, rgba(255,226,226,.42) 0 18%, transparent 52%),
    radial-gradient(ellipse at 61% 33%, rgba(185,126,135,.13) 0 16%, transparent 49%),
    linear-gradient(123deg, transparent 0 32%, rgba(255,245,241,.50) 33%, transparent 34% 69%, rgba(255,236,235,.36) 70%, transparent 71%),
    linear-gradient(145deg,#fff0eb,#d8a5a9 57%,#94656e);
  --die-edge:#d5b07a; --die-pip-fill:#a87727; --die-pip-ring:#e0b75a;
}
#dieCube[data-material="malachite-bronze"] {
  --die-surface:
    repeating-radial-gradient(ellipse 115% 84% at 20% 105%,
      rgba(83,183,126,.34) 0 1.1cqw, rgba(3,49,35,.22) 1.4cqw 2.4cqw,
      rgba(121,194,148,.24) 2.7cqw 3.2cqw, transparent 3.6cqw 5.1cqw),
    repeating-radial-gradient(ellipse 92% 68% at 104% -5%,
      rgba(5,54,39,.32) 0 1.2cqw, rgba(83,183,126,.28) 1.5cqw 2.1cqw,
      transparent 2.5cqw 4.4cqw),
    linear-gradient(145deg,#38a36c,#086441 56%,#022d23);
  --die-edge:#a87431; --die-pip-fill:#d1a044; --die-pip-ring:#e8bd5a;
}
#dieCube[data-material="ivory"] {
  --die-surface:
    radial-gradient(ellipse at 22% 18%, rgba(255,248,224,.78) 0 14%, transparent 49%),
    radial-gradient(ellipse at 73% 70%, rgba(189,149,90,.11) 0 17%, transparent 52%),
    repeating-linear-gradient(174deg, transparent 0 8cqw, rgba(198,155,88,.09) 8.2cqw 8.5cqw, transparent 8.8cqw 16cqw),
    linear-gradient(145deg,#fff6dd,#ead8aa 59%,#af874f);
  --die-edge:#d0aa65; --die-pip-fill:#9c6821; --die-pip-ring:#dcb553;
}
#dieCube[data-material="ebony-gold"] {
  --die-surface:
    repeating-radial-gradient(ellipse 130% 38% at 42% 53%,
      rgba(124,87,59,.24) 0 .7cqw, rgba(18,10,6,.16) 1cqw 2cqw,
      rgba(90,56,36,.18) 2.2cqw 2.8cqw, transparent 3.1cqw 5cqw),
    linear-gradient(145deg,#65452d,#21140d 52%,#060302);
  --die-edge:#b98a34; --die-pip-fill:#d0a13f; --die-pip-ring:#edc766;
}
#dieCube[data-material="blued-steel"] {
  --die-surface:
    radial-gradient(ellipse at 22% 25%, rgba(66,115,154,.34) 0 13%, transparent 48%),
    radial-gradient(ellipse at 71% 68%, rgba(126,67,82,.27) 0 17%, transparent 51%),
    radial-gradient(ellipse at 77% 22%, rgba(155,102,63,.22) 0 10%, transparent 38%),
    linear-gradient(128deg, transparent 0 44%, rgba(98,132,162,.23) 45%, transparent 46%),
    linear-gradient(145deg,#5c7891,#21364a 55%,#09121d);
  --die-edge:#b88b3a; --die-pip-fill:#d2a247; --die-pip-ring:#edc66c;
}
#dieCube[data-material="petrified-wood-gold"] {
  --die-surface:
    repeating-radial-gradient(ellipse 72% 46% at 31% 45%,
      rgba(240,208,160,.31) 0 .55cqw, rgba(51,28,16,.22) .8cqw 1.35cqw,
      rgba(152,98,49,.25) 1.6cqw 2.3cqw, transparent 2.6cqw 3.5cqw),
    linear-gradient(126deg, transparent 0 36%, rgba(240,208,160,.28) 37%, transparent 38%),
    linear-gradient(145deg,#c58a45,#74451f 57%,#2f1a0e);
  --die-edge:#bd8b3c; --die-pip-fill:#d6a649; --die-pip-ring:#efcb6c;
}
#dieCube[data-material="tigers-eye-bronze"] {
  --die-surface:
    repeating-linear-gradient(132deg,
      rgba(255,212,94,.58) 0 .55cqw, rgba(229,169,48,.35) .9cqw 1.7cqw,
      rgba(39,20,5,.52) 2.1cqw 3.1cqw, rgba(139,79,13,.30) 3.4cqw 4.5cqw),
    linear-gradient(145deg,#d79b2b,#6c3f0d 58%,#1d1106);
  --die-edge:#9e6c2d; --die-pip-fill:#d2a348; --die-pip-ring:#efc86a;
}
#dieCube[data-material="solid-gold"] {
  --die-surface:
    linear-gradient(118deg, transparent 0 28%, rgba(255,240,160,.42) 30%, transparent 34% 72%, rgba(111,62,4,.15) 75%, transparent 79%),
    repeating-linear-gradient(170deg, transparent 0 8cqw, rgba(255,228,119,.14) 8.2cqw 8.5cqw, transparent 8.8cqw 15cqw),
    linear-gradient(145deg,#ffe477,#c79018 58%,#6f3e04);
  --die-edge:#e9b938; --die-pip-fill:#3c2105; --die-pip-ring:#f8da65;
}
#dieCube[data-material="damascus-steel"] {
  --die-surface:
    repeating-radial-gradient(ellipse 132% 42% at 26% 56%,
      rgba(241,240,234,.54) 0 .45cqw, rgba(48,51,49,.38) .8cqw 1.35cqw,
      rgba(174,176,173,.42) 1.7cqw 2.1cqw, transparent 2.45cqw 3.35cqw),
    linear-gradient(145deg,#deded7,#777a78 55%,#242725);
  --die-edge:#8d7740; --die-pip-fill:#c99a38; --die-pip-ring:#e9c25f;
}
#dieCube[data-material="copper-verdigris"] {
  --die-surface:
    radial-gradient(ellipse at 18% 24%, rgba(30,125,107,.48) 0 12%, transparent 43%),
    radial-gradient(ellipse at 74% 66%, rgba(126,181,161,.37) 0 16%, transparent 49%),
    radial-gradient(ellipse at 71% 23%, rgba(38,92,80,.40) 0 9%, transparent 36%),
    linear-gradient(145deg,#d99462,#9b5635 57%,#3f241c);
  --die-edge:#a76c39; --die-pip-fill:#176b5b; --die-pip-ring:#d08d4a;
}
#dieCube[data-material="red-granite"] {
  --die-surface:
    radial-gradient(circle at 18% 25%, rgba(214,175,164,.65) 0 .9cqw, transparent 1.3cqw),
    radial-gradient(circle at 70% 68%, rgba(43,17,16,.72) 0 1.05cqw, transparent 1.45cqw),
    radial-gradient(circle at 79% 24%, rgba(215,137,111,.58) 0 .62cqw, transparent .96cqw),
    radial-gradient(circle at 42% 51%, rgba(53,20,22,.62) 0 .45cqw, transparent .82cqw),
    radial-gradient(circle at 58% 13%, rgba(214,175,164,.52) 0 .38cqw, transparent .70cqw),
    linear-gradient(145deg,#bd5a45,#7a2d22 57%,#2b1110);
  --die-edge:#a77d3a; --die-pip-fill:#d1a148; --die-pip-ring:#edc76a;
}
#dieCube[data-material="black-granite"] {
  --die-surface:
    radial-gradient(circle at 19% 24%, rgba(155,153,146,.54) 0 .76cqw, transparent 1.14cqw),
    radial-gradient(circle at 72% 69%, rgba(94,94,89,.58) 0 .92cqw, transparent 1.32cqw),
    radial-gradient(circle at 80% 21%, rgba(138,109,57,.48) 0 .34cqw, transparent .66cqw),
    radial-gradient(circle at 43% 50%, rgba(8,9,9,.74) 0 .58cqw, transparent .93cqw),
    linear-gradient(145deg,#5b5a56,#20201f 55%,#050606);
  --die-edge:#60563c; --die-pip-fill:#c99a36; --die-pip-ring:#e8bf5d;
}
#dieCube[data-material="gilded-silver"] {
  --die-surface:
    repeating-linear-gradient(14deg, transparent 0 5cqw, rgba(154,157,155,.13) 5.2cqw 5.5cqw, transparent 5.8cqw 9cqw),
    radial-gradient(ellipse at 22% 18%, rgba(255,255,255,.72), transparent 48%),
    radial-gradient(ellipse at 73% 70%, rgba(192,194,192,.18), transparent 49%),
    linear-gradient(145deg,#fffef8,#d4d3cf 58%,#777b79);
  --die-edge:#c7a552; --die-pip-fill:#aa7622; --die-pip-ring:#e2bd59;
}
#dieCube[data-material="hammered-bronze"] {
  --die-surface:
    radial-gradient(ellipse at 18% 24%, rgba(74,40,20,.42) 0 .75cqw, rgba(227,173,108,.18) .95cqw 1.2cqw, transparent 1.45cqw),
    radial-gradient(ellipse at 70% 69%, rgba(74,40,20,.38) 0 .9cqw, rgba(227,173,108,.16) 1.1cqw 1.35cqw, transparent 1.6cqw),
    radial-gradient(ellipse at 80% 22%, rgba(74,40,20,.36) 0 .5cqw, rgba(227,173,108,.14) .7cqw .95cqw, transparent 1.2cqw),
    radial-gradient(ellipse at 42% 49%, rgba(74,40,20,.34) 0 .62cqw, rgba(227,173,108,.14) .8cqw 1cqw, transparent 1.3cqw),
    linear-gradient(145deg,#d89752,#8a5127 56%,#351c0d);
  --die-edge:#b17b35; --die-pip-fill:#27150c; --die-pip-ring:#c98538;
}
#dieCube[data-material="meteorite-iron"] {
  --die-surface:
    repeating-linear-gradient(12deg, transparent 0 3.2cqw, rgba(216,218,215,.42) 3.4cqw 3.8cqw, transparent 4cqw 7cqw),
    repeating-linear-gradient(58deg, transparent 0 4.2cqw, rgba(133,139,136,.36) 4.4cqw 4.8cqw, transparent 5cqw 8cqw),
    repeating-linear-gradient(132deg, transparent 0 5cqw, rgba(37,40,39,.50) 5.2cqw 5.7cqw, transparent 5.9cqw 9cqw),
    linear-gradient(145deg,#a6aaa7,#4c4e4b 55%,#161817);
  --die-edge:#9c7b39; --die-pip-fill:#cea142; --die-pip-ring:#edc864;
}
#dieCube[data-material="lava-stone-brass"] {
  --die-surface:
    radial-gradient(ellipse at 18% 25%, rgba(5,6,6,.86) 0 .85cqw, rgba(119,115,107,.18) 1cqw 1.25cqw, transparent 1.5cqw),
    radial-gradient(ellipse at 73% 68%, rgba(5,6,6,.90) 0 1.05cqw, rgba(119,115,107,.16) 1.22cqw 1.48cqw, transparent 1.7cqw),
    radial-gradient(ellipse at 80% 22%, rgba(5,6,6,.84) 0 .62cqw, transparent 1cqw),
    radial-gradient(ellipse at 42% 50%, rgba(5,6,6,.87) 0 .5cqw, transparent .9cqw),
    linear-gradient(145deg,#68645d,#2a2926 55%,#070807);
  --die-edge:#9f7834; --die-pip-fill:#cea040; --die-pip-ring:#edc661;
}
#dieCube[data-material="peacock-ore"] {
  --die-surface:
    radial-gradient(ellipse at 18% 24%, rgba(22,201,203,.66) 0 12%, transparent 43%),
    radial-gradient(ellipse at 73% 68%, rgba(199,41,135,.58) 0 17%, transparent 48%),
    radial-gradient(ellipse at 78% 21%, rgba(109,46,160,.60) 0 13%, transparent 42%),
    radial-gradient(ellipse at 43% 51%, rgba(40,165,121,.54) 0 15%, transparent 45%),
    radial-gradient(ellipse at 57% 16%, rgba(201,138,34,.50) 0 8%, transparent 34%),
    linear-gradient(145deg,#388f97,#173e48 55%,#090f1a);
  --die-edge:#a37a37; --die-pip-fill:#d2a343; --die-pip-ring:#f0cc69;
}
#dieCube[data-material="mokume-gane"] {
  --die-surface:
    repeating-radial-gradient(ellipse 136% 42% at 24% 54%,
      rgba(239,208,154,.56) 0 .55cqw, rgba(185,109,68,.48) .85cqw 1.35cqw,
      rgba(199,193,181,.42) 1.65cqw 2.1cqw, rgba(70,48,43,.46) 2.35cqw 2.95cqw,
      transparent 3.2cqw 4.1cqw),
    linear-gradient(145deg,#d5a05f,#8d6039 56%,#34251f);
  --die-edge:#a37a3e; --die-pip-fill:#d0a045; --die-pip-ring:#edc668;
}
#dieCube[data-material="radium"] {
  --die-surface:
    linear-gradient(34deg, transparent 0 28%, rgba(246,255,235,.70) 29% 30%, transparent 31% 67%, rgba(144,208,137,.40) 68% 69%, transparent 70%),
    linear-gradient(128deg, transparent 0 42%, rgba(244,255,225,.62) 43% 44%, transparent 45%),
    radial-gradient(ellipse at 25% 24%, rgba(255,255,245,.74) 0 13%, transparent 46%),
    radial-gradient(ellipse at 72% 68%, rgba(119,190,112,.32) 0 18%, transparent 53%),
    linear-gradient(145deg,#f4ffe3,#b8dca9 57%,#477252);
  --die-edge:#c7cbb2; --die-pip-fill:#dfffb1; --die-pip-ring:#78bc65;
}
#dieCube[data-material="uraninite"] {
  --die-surface:
    linear-gradient(32deg, transparent 0 24%, rgba(181,255,21,.74) 25% 26%, rgba(76,109,6,.38) 27% 29%, transparent 30%),
    linear-gradient(121deg, transparent 0 46%, rgba(215,255,43,.72) 47% 48%, rgba(74,106,5,.40) 49% 51%, transparent 52%),
    radial-gradient(circle at 22% 24%, rgba(91,102,82,.34) 0 .8cqw, transparent 1.3cqw),
    radial-gradient(circle at 73% 68%, rgba(3,5,4,.82) 0 1.1cqw, transparent 1.7cqw),
    linear-gradient(145deg,#555e4d,#1d211c 56%,#030504);
  --die-edge:#947238; --die-pip-fill:#b9ff16; --die-pip-ring:#5d8e08;
}
#dieCube[data-material="plutonium"] {
  --die-surface:
    conic-gradient(from 28deg at 28% 33%, rgba(255,255,142,.58), transparent 13% 31%, rgba(141,151,0,.34) 33% 47%, transparent 49%),
    linear-gradient(37deg, transparent 0 41%, rgba(255,255,143,.66) 42% 43%, transparent 44%),
    linear-gradient(128deg, transparent 0 63%, rgba(101,108,0,.46) 64% 66%, transparent 67%),
    linear-gradient(145deg,#f8ff54,#c9d915 56%,#555900);
  --die-edge:#25281d; --die-pip-fill:#13150d; --die-pip-ring:#050605;
}
#dieCube[data-material="platinum-rubies"] {
  --die-surface:
    radial-gradient(circle at 5% 5%, transparent 0 13%, rgba(55,59,61,.70) 13.8% 16%, rgba(255,255,255,.72) 16.5% 17.4%, transparent 18.2%),
    radial-gradient(circle at 95% 5%, transparent 0 13%, rgba(55,59,61,.70) 13.8% 16%, rgba(255,255,255,.72) 16.5% 17.4%, transparent 18.2%),
    radial-gradient(circle at 5% 95%, transparent 0 13%, rgba(55,59,61,.70) 13.8% 16%, rgba(255,255,255,.72) 16.5% 17.4%, transparent 18.2%),
    radial-gradient(circle at 95% 95%, transparent 0 13%, rgba(55,59,61,.70) 13.8% 16%, rgba(255,255,255,.72) 16.5% 17.4%, transparent 18.2%),
    repeating-linear-gradient(99deg, transparent 0 7cqw, rgba(105,109,111,.11) 7.2cqw 7.45cqw, transparent 7.7cqw 13cqw),
    radial-gradient(ellipse at 24% 19%, rgba(255,255,255,.80), transparent 49%),
    linear-gradient(145deg,#ffffff,#d8d8d8 57%,#777a7c);
  --die-edge:#aab0b4;
  --die-pip-fill:conic-gradient(from 15deg,#49000f,#d52b4b,#ff7380,#9f102e,#3f000d,#c2143a,#49000f);
  --die-pip-ring:#e4e6e7;
}
#dieCube[data-material="sterling-sapphires"] {
  --die-surface:
    radial-gradient(ellipse at 5% 5%, transparent 0 12%, rgba(57,67,76,.68) 13% 15.6%, rgba(255,255,255,.70) 16.2% 17.2%, transparent 18%),
    radial-gradient(ellipse at 95% 5%, transparent 0 12%, rgba(57,67,76,.68) 13% 15.6%, rgba(255,255,255,.70) 16.2% 17.2%, transparent 18%),
    radial-gradient(ellipse at 5% 95%, transparent 0 12%, rgba(57,67,76,.68) 13% 15.6%, rgba(255,255,255,.70) 16.2% 17.2%, transparent 18%),
    radial-gradient(ellipse at 95% 95%, transparent 0 12%, rgba(57,67,76,.68) 13% 15.6%, rgba(255,255,255,.70) 16.2% 17.2%, transparent 18%),
    repeating-linear-gradient(8deg, transparent 0 4.1cqw, rgba(255,255,255,.35) 4.25cqw 4.48cqw, transparent 4.7cqw 8.4cqw),
    radial-gradient(ellipse at 24% 19%, rgba(255,255,255,.84), transparent 48%),
    linear-gradient(145deg,#ffffff,#d8d9dc 57%,#747b82);
  --die-edge:#bfc5c9;
  --die-pip-fill:conic-gradient(from 18deg,#08266f,#4d83ed,#102f91,#020d3d,#285dcc,#08266f);
  --die-pip-ring:#e6e9eb;
}
#dieCube[data-material="white-gold-emeralds"] {
  --die-surface:
    radial-gradient(ellipse at 4% 4%, transparent 0 12%, rgba(77,57,15,.66) 13% 15.3%, rgba(255,246,205,.73) 16% 17%, transparent 17.8%),
    radial-gradient(ellipse at 96% 4%, transparent 0 12%, rgba(77,57,15,.66) 13% 15.3%, rgba(255,246,205,.73) 16% 17%, transparent 17.8%),
    radial-gradient(ellipse at 4% 96%, transparent 0 12%, rgba(77,57,15,.66) 13% 15.3%, rgba(255,246,205,.73) 16% 17%, transparent 17.8%),
    radial-gradient(ellipse at 96% 96%, transparent 0 12%, rgba(77,57,15,.66) 13% 15.3%, rgba(255,246,205,.73) 16% 17%, transparent 17.8%),
    repeating-linear-gradient(9deg, transparent 0 4.4cqw, rgba(255,250,222,.37) 4.55cqw 4.78cqw, transparent 5cqw 8.8cqw),
    radial-gradient(ellipse at 23% 18%, rgba(255,255,249,.88), transparent 49%),
    linear-gradient(145deg,#fffef8,#e3e0d7 57%,#817d72);
  --die-edge:#d2b567;
  --die-pip-fill:conic-gradient(from 16deg,#014b29,#42d785,#087842,#012f1d,#18a95e,#014b29);
  --die-pip-ring:#e3c46d;
}
#dieCube[data-material="gold-diamonds"] {
  --die-surface:
    conic-gradient(from 8deg at 3% 3%, transparent 0 9deg, rgba(79,36,0,.67) 10deg 15deg, rgba(255,239,145,.70) 16deg 18deg, transparent 19deg 29deg, rgba(79,36,0,.60) 30deg 35deg, rgba(255,239,145,.64) 36deg 38deg, transparent 39deg),
    conic-gradient(from 278deg at 97% 3%, transparent 0 9deg, rgba(79,36,0,.67) 10deg 15deg, rgba(255,239,145,.70) 16deg 18deg, transparent 19deg 29deg, rgba(79,36,0,.60) 30deg 35deg, rgba(255,239,145,.64) 36deg 38deg, transparent 39deg),
    conic-gradient(from 98deg at 3% 97%, transparent 0 9deg, rgba(79,36,0,.67) 10deg 15deg, rgba(255,239,145,.70) 16deg 18deg, transparent 19deg 29deg, rgba(79,36,0,.60) 30deg 35deg, rgba(255,239,145,.64) 36deg 38deg, transparent 39deg),
    conic-gradient(from 188deg at 97% 97%, transparent 0 9deg, rgba(79,36,0,.67) 10deg 15deg, rgba(255,239,145,.70) 16deg 18deg, transparent 19deg 29deg, rgba(79,36,0,.60) 30deg 35deg, rgba(255,239,145,.64) 36deg 38deg, transparent 39deg),
    repeating-linear-gradient(8deg, transparent 0 4cqw, rgba(255,241,153,.38) 4.18cqw 4.42cqw, transparent 4.65cqw 8.1cqw),
    radial-gradient(ellipse at 24% 18%, rgba(255,246,173,.90), transparent 48%),
    linear-gradient(145deg,#ffe985,#c78d16 57%,#6f3c04);
  --die-edge:#e8ba43;
  --die-pip-fill:conic-gradient(from 12deg,#7890aa,#ffffff,#b9d9ee,#71859e,#f8ffff,#9ebdd3,#7890aa);
  --die-pip-ring:#f4d36b;
}
#dieCube[data-material="platinum-rubies"] .die-face::after,
#dieCube[data-material="sterling-sapphires"] .die-face::after,
#dieCube[data-material="white-gold-emeralds"] .die-face::after,
#dieCube[data-material="gold-diamonds"] .die-face::after {
  border-width: max(1.5px, 1.35cqw);
  opacity: .92;
  box-shadow:
    inset 0 1.15cqw 1.1cqw rgba(255, 255, 244, .72),
    inset 0 -1.35cqw 1.15cqw rgba(38, 24, 9, .52),
    0 .55cqw .65cqw rgba(36, 20, 9, .38);
}
#dieCube[data-material="platinum-rubies"] .die-pip,
#dieCube[data-material="sterling-sapphires"] .die-pip,
#dieCube[data-material="white-gold-emeralds"] .die-pip,
#dieCube[data-material="gold-diamonds"] .die-pip {
  overflow: hidden;
  box-shadow:
    inset .7cqw .7cqw 1.1cqw rgba(255,255,255,.72),
    inset -.9cqw -.9cqw 1.3cqw rgba(0,0,0,.42),
    0 .65cqw .7cqw rgba(36,20,9,.34);
}
#dieCube[data-material="platinum-rubies"] .die-pip::after,
#dieCube[data-material="sterling-sapphires"] .die-pip::after,
#dieCube[data-material="white-gold-emeralds"] .die-pip::after,
#dieCube[data-material="gold-diamonds"] .die-pip::after {
  content: "";
  position: absolute;
  inset: -25%;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(circle at 31% 28%, rgba(255,255,255,.90) 0 2.2%, transparent 3.2%),
    linear-gradient(117deg, transparent 34%, rgba(255,255,255,.06) 42%, rgba(255,255,255,.72) 49%, rgba(255,255,255,.05) 57%, transparent 65%);
  opacity: .78;
  transform: translateX(-56%);
  animation: preciousPipGlint var(--die-gem-sparkle-duration, 3.7s)
    cubic-bezier(.3,.1,.2,1) infinite;
}
#dieCube[data-material="sterling-sapphires"] .die-pip:nth-child(2n)::after,
#dieCube[data-material="white-gold-emeralds"] .die-pip:nth-child(2n)::after {
  animation-delay: -1.25s;
}
#dieCube[data-material="platinum-rubies"] .die-pip:nth-child(3n)::after,
#dieCube[data-material="gold-diamonds"] .die-pip:nth-child(3n)::after {
  animation-delay: -2.35s;
}
@keyframes preciousPipGlint {
  0%, 18% {
    opacity: calc(.06 + var(--die-gem-sparkle-strength, 1) * .07);
    transform: translateX(-58%) scale(.88);
  }
  37% {
    opacity: calc(.40 + var(--die-gem-sparkle-strength, 1) * .30);
    transform: translateX(5%) scale(calc(.91 + var(--die-gem-sparkle-strength, 1) * .05));
  }
  52%, 100% {
    opacity: calc(.04 + var(--die-gem-sparkle-strength, 1) * .04);
    transform: translateX(58%) scale(.92);
  }
}
#beastStage[data-die-gem-sparkle="0"] #dieCube[data-material="platinum-rubies"] .die-pip::after,
#beastStage[data-die-gem-sparkle="0"] #dieCube[data-material="sterling-sapphires"] .die-pip::after,
#beastStage[data-die-gem-sparkle="0"] #dieCube[data-material="white-gold-emeralds"] .die-pip::after,
#beastStage[data-die-gem-sparkle="0"] #dieCube[data-material="gold-diamonds"] .die-pip::after {
  display: none;
}
@media (prefers-reduced-motion: reduce) {
  body:not(.die-force-motion) #dieCube[data-material="platinum-rubies"] .die-pip::after,
  body:not(.die-force-motion) #dieCube[data-material="sterling-sapphires"] .die-pip::after,
  body:not(.die-force-motion) #dieCube[data-material="white-gold-emeralds"] .die-pip::after,
  body:not(.die-force-motion) #dieCube[data-material="gold-diamonds"] .die-pip::after {
    animation: none;
    opacity: .42;
    transform: translateX(-8%) scale(.94);
  }
}
.beast-rig-live.is-reduced-motion #dieCube[data-material="platinum-rubies"] .die-pip::after,
.beast-rig-live.is-reduced-motion #dieCube[data-material="sterling-sapphires"] .die-pip::after,
.beast-rig-live.is-reduced-motion #dieCube[data-material="white-gold-emeralds"] .die-pip::after,
.beast-rig-live.is-reduced-motion #dieCube[data-material="gold-diamonds"] .die-pip::after {
  animation: none;
  opacity: .42;
  transform: translateX(-8%) scale(.94);
}
#dieCube[data-material="actinium"] {
  --die-surface:
    conic-gradient(from 15deg at 66% 42%, rgba(218,246,255,.42), transparent 11% 27%, rgba(44,159,223,.38) 29% 45%, transparent 47%),
    linear-gradient(36deg, transparent 0 39%, rgba(198,242,255,.67) 40% 41%, transparent 42%),
    linear-gradient(126deg, transparent 0 59%, rgba(72,200,255,.50) 60% 62%, transparent 63%),
    radial-gradient(ellipse at 24% 21%, rgba(126,201,255,.58), transparent 49%),
    linear-gradient(145deg,#7ec9ff,#1e69a8 56%,#071f45);
  --die-edge:#364f70; --die-pip-fill:#5ee6ff; --die-pip-ring:#1576c8;
}
#dieCube[data-material="antique-amber"] {
  --die-surface:
    radial-gradient(ellipse at 18% 23%, rgba(58,24,6,.56) 0 .42cqw, transparent .72cqw),
    radial-gradient(circle at 72% 68%, rgba(255,225,130,.58) 0 .36cqw, rgba(126,55,6,.23) .55cqw .75cqw, transparent .95cqw),
    radial-gradient(ellipse at 78% 22%, rgba(81,31,4,.48) 0 .25cqw, transparent .52cqw),
    radial-gradient(ellipse at 42% 49%, rgba(255,214,106,.34) 0 15%, transparent 45%),
    linear-gradient(145deg,#ffb51a,#b75a06 56%,#5f2202);
  --die-edge:#bd8635; --die-pip-fill:#dca23c; --die-pip-ring:#f2c763;
}
#dieCube[data-material="sodalite"] {
  --die-surface:
    linear-gradient(29deg, transparent 0 29%, rgba(238,234,221,.68) 30% 33%, rgba(139,115,78,.36) 34% 35%, transparent 36%),
    linear-gradient(118deg, transparent 0 56%, rgba(218,216,208,.62) 57% 59%, transparent 60%),
    radial-gradient(ellipse at 23% 24%, rgba(88,109,164,.45) 0 15%, transparent 47%),
    radial-gradient(ellipse at 73% 69%, rgba(7,19,46,.45) 0 18%, transparent 51%),
    linear-gradient(145deg,#586da4,#1e3472 56%,#07132e);
  --die-edge:#9ca2a4; --die-pip-fill:#d8d8d0; --die-pip-ring:#868b8c;
}
#dieCube[data-material="jasper"] {
  --die-surface:
    conic-gradient(from 23deg at 25% 31%, rgba(210,160,134,.48), transparent 12% 28%, rgba(69,25,23,.38) 30% 47%, transparent 49%),
    conic-gradient(from 118deg at 71% 69%, rgba(194,174,150,.42), transparent 15% 34%, rgba(152,82,69,.40) 36% 54%, transparent 56%),
    linear-gradient(32deg, transparent 0 45%, rgba(214,179,152,.42) 46% 48%, transparent 49%),
    linear-gradient(145deg,#c35e43,#8a2b1f 56%,#35100d);
  --die-edge:#a9823c; --die-pip-fill:#d4a245; --die-pip-ring:#efc76b;
}
@supports (transform-style: preserve-3d) and (width: 1cqw) {
  body.die-living #dieCubeScene { display: block; }
  body.die-living #dieArt { display: none; }
  body.die-living #dieTurnly { animation: none; }
}
body.die-living.die-cube-invalid #dieCubeScene { display: none; }
body.die-living.die-cube-invalid #dieArt { display: block; }
.beast-rig-live.is-die-spin-paused #dieSpinner {
  animation: none;
  transform: rotate3d(1, 0, 0, 0turn);
}
@keyframes dieCubeSpinX {
  from { transform: rotate3d(1, 0, 0, 0turn); }
  to   { transform: rotate3d(1, 0, 0, 1turn); }
}
.die-aura {
  position: absolute; left: 50%; top: 50%;
  width: var(--die-aura); height: calc(var(--die-aura) - 10%);
  transform: translate(-50%, -50%);
  background: radial-gradient(closest-side,
              rgba(255, 214, 122, .50), rgba(var(--die-glow-hue), .20) 46%,
              rgba(var(--die-glow-hue), 0) 74%);
  opacity: var(--die-aura-strength);
  animation: dieAura var(--die-bob-beat) ease-in-out infinite alternate;
  pointer-events: none;
}

/* The glow rig is deliberately not part of the die renderer. It is a small
   score of light behind the clean canvas: two soft washes plus up to thirty-
   two individually positioned ink rays. Materials, pips and face contours
   remain completely untouched. */
.die-glow-rig {
  position: absolute; left: 50%; top: 50%; z-index: 0;
  width: 225%; aspect-ratio: 1;
  transform: translate(-50%, -50%) scale(var(--die-glow-radius-scale));
  transform-origin: 50% 50%; pointer-events: none; overflow: visible;
  opacity: var(--die-glow-opacity);
  filter: brightness(var(--die-glow-brightness));
  isolation: isolate;
}
.die-glow-rig[hidden] { display: none !important; }
.die-glow-halo,
.die-glow-core,
.die-glow-rays {
  position: absolute; inset: 0; display: block; pointer-events: none;
}
.die-glow-halo {
  border-radius: 50%;
  background:
    radial-gradient(circle,
      rgba(var(--die-glow-hot), .56) 0 11%,
      rgba(var(--die-glow-primary), .34) 24%,
      rgba(var(--die-glow-primary), .13) 48%,
      rgba(var(--die-glow-deep), 0) 73%);
  filter: blur(1.5px);
  animation: dieGlowHaloPulse var(--die-glow-pulse-duration) ease-in-out infinite alternate;
}
.die-glow-core {
  left: 50%; top: 50%; right: auto; bottom: auto;
  width: 58%; aspect-ratio: 1; border-radius: 50%;
  transform: translate(-50%, -50%) scale(var(--die-glow-core-scale));
  background:
    radial-gradient(circle,
      rgba(var(--die-glow-hot), .82) 0 12%,
      rgba(var(--die-glow-primary), .54) 34%,
      rgba(var(--die-glow-primary), .16) 61%,
      rgba(var(--die-glow-primary), 0) 78%);
  filter: blur(.72px) saturate(1.06);
  animation: dieGlowCorePulse var(--die-glow-pulse-duration) ease-in-out infinite alternate;
}
.die-glow-rays {
  transform-origin: 50% 50%;
  animation: dieGlowRayDrift var(--die-glow-drift-duration) linear infinite;
}
.die-glow-ray {
  position: absolute; left: 50%; top: 50%; display: block;
  width: var(--die-glow-ray-width); height: 48%;
  transform-origin: 50% 0;
  transform: translateX(-50%) rotate(var(--glow-ray-angle))
    scaleY(var(--die-glow-ray-length-scale));
  pointer-events: none;
}
.die-glow-ray[hidden] { display: none; }
.die-glow-ray::before {
  content: ""; position: absolute; inset: 0;
  transform-origin: 50% 0;
  transform: scaleY(var(--glow-ray-length-variation));
  opacity: var(--glow-ray-opacity);
  clip-path: polygon(44% 0, 56% 0, 76% 74%, 50% 100%, 24% 74%);
  background: linear-gradient(to bottom,
    rgba(var(--die-glow-hot), 0) 0 24%,
    rgba(var(--die-glow-hot), .86) 43%,
    rgba(var(--die-glow-primary), .67) 68%,
    rgba(var(--die-glow-primary), 0) 100%);
  filter: drop-shadow(0 0 .7px rgba(var(--die-glow-primary), .72));
  animation: dieGlowRayShimmer var(--die-glow-pulse-duration) ease-in-out infinite alternate;
  animation-delay: var(--glow-ray-delay);
}

.beast-rig-live[data-die-glow="golden"] #dieLev {
  --die-glow-hot: 255, 248, 205;
  --die-glow-primary: 241, 184, 50;
  --die-glow-deep: 137, 79, 14;
  --die-glow-hue: 255, 190, 74;
}
.beast-rig-live[data-die-glow="angelic"] #dieLev {
  --die-glow-hot: 246, 253, 255;
  --die-glow-primary: 117, 194, 255;
  --die-glow-deep: 50, 90, 184;
  --die-glow-hue: 117, 194, 255;
}
.beast-rig-live[data-die-glow="radioactive"] #dieLev {
  --die-glow-hot: 239, 255, 170;
  --die-glow-primary: 164, 245, 41;
  --die-glow-deep: 36, 145, 48;
  --die-glow-hue: 164, 245, 41;
}
.beast-rig-live[data-die-glow="ember"] #dieLev {
  --die-glow-hot: 255, 230, 164;
  --die-glow-primary: 244, 91, 38;
  --die-glow-deep: 126, 25, 18;
  --die-glow-hue: 244, 91, 38;
}
.beast-rig-live[data-die-glow="moon-silver"] #dieLev {
  --die-glow-hot: 255, 255, 247;
  --die-glow-primary: 190, 206, 220;
  --die-glow-deep: 75, 91, 121;
  --die-glow-hue: 190, 206, 220;
}

.beast-rig-live.is-glow-pulse-paused .die-glow-halo,
.beast-rig-live.is-glow-pulse-paused .die-glow-core,
.beast-rig-live.is-glow-pulse-paused .die-glow-ray::before {
  animation: none;
}
.beast-rig-live.is-glow-drift-paused .die-glow-rays { animation: none; }

@keyframes dieGlowHaloPulse {
  from { opacity: .58; transform: scale(.94); }
  to   { opacity: .94; transform: scale(1.035); }
}
/* THE CORE BREATHES WITHOUT A FILTER (2026-09-28, of a phone left on the
   landing: "It got hot"): it used to breathe its blur and saturation too,
   and a filter cannot be animated off the main thread -- that one small
   pulse restyled the page every frame (55 ms a second at phone speed, all
   but the whole of the idle landing's style work). The blur and saturation
   now hold at their middle (.die-glow-core, .72px and 1.06) and the core
   breathes in opacity alone -- a scale drawn from a variable in the
   keyframes kept it on the main thread just the same (measured). */
@keyframes dieGlowCorePulse {
  from { opacity: .64; }
  to   { opacity: .94; }
}
@keyframes dieGlowRayDrift {
  from { transform: rotate(0turn); }
  to   { transform: rotate(var(--die-glow-drift-turn)); }
}
@keyframes dieGlowRayShimmer {
  from { opacity: .42; }
  to   { opacity: .92; }
}

@keyframes dieBob {
  from { transform: translateY(calc(var(--die-bob) * -1 * var(--die-motion))); }
  to   { transform: translateY(calc(var(--die-bob) * var(--die-motion))); }
}
@keyframes dieTilt {
  from { transform: rotate(calc(var(--die-lean) * -1)); }
  to   { transform: rotate(var(--die-lean)); }
}
@keyframes dieAura {
  from { opacity: calc(var(--die-aura-strength) * .55); transform: translate(-50%, -50%) scale(.94); }
  to   { opacity: var(--die-aura-strength);             transform: translate(-50%, -50%) scale(1.06); }
}

/* A machine that asks for less motion gets less, not none. The
   preference exists to stop large, fast, parallax and spinning motion,
   and a three-pixel drift is none of those — so the rise stays, gentler
   and slower, the lean stands down because rotation is the part that
   unsettles people, and the aura keeps breathing, since a change of
   opacity is not movement at all. `?dials` can override this for
   previewing on a machine that asks for stillness. */
@media (prefers-reduced-motion: reduce) {
  body:not(.die-force-motion) #dieLev { --die-motion: 1; }
  body:not(.die-force-motion) #dieTilt { animation: none; }
}

/* ---- the dials panel, staging only ---------------------------------- */
#dieDials, #dieDials * { color: #f0dfae; }
#dieDials {
  position: fixed; left: 8px; right: 8px; bottom: 8px; z-index: 9999;
  background: rgba(28, 19, 7, .93); color: #f0dfae;
  border: 1px solid rgba(201, 162, 39, .5); border-radius: 8px;
  padding: 10px 12px 12px; font: 12px/1.35 var(--sans);
  max-height: 46vh; overflow-y: auto; overscroll-behavior: contain;
  box-shadow: 0 6px 26px rgba(0, 0, 0, .5);
}
#dieDials[hidden] { display: none; }
#dieDials h4 {
  font: 700 11px/1 var(--sans); letter-spacing: .16em;
  text-transform: uppercase; color: var(--gold); margin-bottom: 8px;
}
#dieDials label {
  display: grid; grid-template-columns: 84px 1fr 58px;
  align-items: center; gap: 8px; margin: 5px 0;
}
#dieDials input[type=range] { width: 100%; accent-color: #c9a227; }
#dieDials .v { text-align: right; font-variant-numeric: tabular-nums; opacity: .85; }
#dieDials .eye-dials {
  min-width: 0; margin: 12px 0 10px; padding: 9px 10px 7px;
  border: 1px solid rgba(201, 162, 39, .34); border-radius: 6px;
  background: rgba(240, 223, 174, .045);
}
#dieDials .eye-dials legend {
  padding: 0 6px;
  font: 700 11px/1 var(--sans); letter-spacing: .16em;
  text-transform: uppercase; color: var(--gold);
}
#dieDials .eye-dials-note {
  margin: 0 0 7px; color: rgba(240, 223, 174, .72);
}
#dieDials .bling-switch-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 7px;
}
#dieDials .eye-dials .bling-switch {
  display: grid;
  grid-template-columns: 38px minmax(0, 1fr) 26px;
  align-items: center;
  gap: 9px;
  min-height: 54px;
  margin: 0;
  padding: 7px 8px;
  border: 1px solid rgba(201, 162, 39, .26);
  border-radius: 6px;
  background: rgba(10, 7, 3, .2);
  cursor: pointer;
  transition: border-color .16s ease, background-color .16s ease;
}
#dieDials .eye-dials .bling-switch:hover {
  border-color: rgba(232, 195, 83, .56);
  background: rgba(240, 223, 174, .065);
}
#dieDials .bling-switch input[type="checkbox"] {
  appearance: none;
  position: relative;
  width: 36px;
  height: 20px;
  margin: 0;
  border: 1px solid rgba(240, 223, 174, .46);
  border-radius: 999px;
  background: rgba(7, 5, 2, .72);
  cursor: pointer;
  transition: border-color .16s ease, background-color .16s ease;
}
#dieDials .bling-switch input[type="checkbox"]::before {
  content: "";
  position: absolute;
  width: 14px;
  height: 14px;
  left: 2px;
  top: 2px;
  border-radius: 50%;
  background: #9e8b5a;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .55);
  transition: transform .16s ease, background-color .16s ease;
}
#dieDials .bling-switch input[type="checkbox"]:checked {
  border-color: rgba(242, 205, 92, .92);
  background: rgba(119, 80, 7, .78);
}
#dieDials .bling-switch input[type="checkbox"]:checked::before {
  transform: translateX(16px);
  background: #f0cb59;
}
#dieDials .bling-switch input[type="checkbox"]:focus-visible {
  outline: 2px solid rgba(240, 205, 108, .95);
  outline-offset: 3px;
}
#dieDials .bling-switch-copy {
  display: grid;
  gap: 2px;
  min-width: 0;
}
#dieDials .bling-switch-copy strong {
  color: #f0dfae;
  font: 700 11px/1.15 var(--sans);
  letter-spacing: .025em;
}
#dieDials .bling-switch-copy small {
  color: rgba(240, 223, 174, .62);
  font: 10px/1.25 var(--sans);
}
#dieDials .eye-dials .bling-switch output {
  color: rgba(240, 223, 174, .68);
  font-size: 10px;
  text-align: right;
}
#dieDials .eye-dials .bling-switch:has(input:checked) output {
  color: #f0cb59;
}
#dieDials .eye-dials label {
  grid-template-columns: minmax(92px, auto) minmax(120px, 1fr) 84px;
}
#dieDials .eye-dials output {
  text-align: right; color: #f0dfae;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
#dieDials .eye-dials input[type="range"] {
  min-width: 0; cursor: ew-resize;
}
#dieDials .bubble-color-picker {
  display: grid; grid-template-columns: minmax(92px, auto) minmax(120px, 1fr) 84px;
  align-items: center; gap: 8px; margin: 9px 0 7px;
}
#dieDials .bubble-color-title { color: #f0dfae; }
#dieDials .bubble-color-options {
  display: flex; flex-wrap: wrap; gap: 6px;
}
#dieDials .eye-dials .bubble-color-option {
  display: inline-flex; grid-template-columns: none; align-items: center; gap: 5px;
  width: auto; margin: 0; padding: 3px 7px 3px 5px;
  border: 1px solid rgba(201, 162, 39, .32); border-radius: 999px;
  background: rgba(240, 223, 174, .055); cursor: pointer;
}
#dieDials .bubble-color-option input {
  width: 14px; height: 14px; margin: 0; cursor: pointer;
}
#dieDials .bubble-color-option.is-gold input { accent-color: #d2a633; }
#dieDials .bubble-color-option.is-blue input { accent-color: #245ea8; }
#dieDials .bubble-color-option.is-red input { accent-color: #a63b2d; }
#dieDials .bubble-color-option.is-green input { accent-color: #3f7b42; }
#dieDials .bubble-color-picker output {
  text-align: right; color: #f0dfae; white-space: nowrap;
}
#dieDials .palm-conjure-only[hidden] {
  display: none !important;
}
#dieDials .eye-dials select {
  min-width: 0; width: 100%; height: 30px; padding: 0 7px;
  font: 12px/1.2 var(--sans); color: #f0dfae;
  background: #24190d; border: 1px solid rgba(201, 162, 39, .48);
  border-radius: 4px; cursor: pointer;
}
#dieDials .eye-dials select:focus-visible {
  outline: 2px solid rgba(240, 205, 108, .92);
  outline-offset: 3px;
}
#dieDials .eye-dials input[type="range"]:focus-visible {
  outline: 2px solid rgba(240, 205, 108, .92);
  outline-offset: 3px; border-radius: 999px;
}
#dieDials .eye-dials .visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
#dieDials .btns { display: flex; gap: 8px; margin-top: 9px; flex-wrap: wrap; }
#dieDials button {
  flex: 1; min-width: 88px; min-height: 30px; padding: 0 10px;
  font: 700 11px/1 var(--sans); letter-spacing: .1em;
  background: #3a2a11; color: #f0dfae; border: 1px solid rgba(201,162,39,.45);
  border-radius: 5px; box-shadow: none;
}
#dieDials .mo { display: flex; align-items: center; gap: 7px; margin-top: 8px; }
#dieDials textarea {
  width: 100%; height: 74px; margin-top: 8px; padding: 6px;
  font: 11px/1.4 ui-monospace, Menlo, Consolas, monospace;
  background: #17100a; color: #cdbb8c;
  border: 1px solid rgba(201,162,39,.3); border-radius: 5px;
}

/* Folded away, the dials leave a tab to call them back by. */
#dieDialsTab {
  position: fixed; left: 8px; bottom: 8px; z-index: 9999;
  min-height: 30px; padding: 0 14px;
  font: 700 11px/1 var(--sans); letter-spacing: .14em;
  background: rgba(28, 19, 7, .92); color: var(--gold);
  border: 1px solid rgba(201, 162, 39, .5); border-radius: 5px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .45);
}
#dieDialsTab[hidden] { display: none; }

@media (max-width: 520px) {
  #dieDials .eye-dials label {
    grid-template-columns: minmax(88px, auto) minmax(0, 1fr);
    gap: 5px 8px; margin: 8px 0;
  }
  #dieDials .eye-dials output {
    grid-column: 2; text-align: left;
  }
  #dieDials .bubble-color-picker {
    grid-template-columns: minmax(88px, auto) minmax(0, 1fr);
  }
  #dieDials .bubble-color-picker output { grid-column: 2; text-align: left; }
}

/* --------------------------------------------------------------------------
   THE MENU SHEET (founder, 2026-08-24). The splash keeps one door; the
   rest live behind the sandwich. Native <dialog> for the same reasons
   the rules sheet is one — the browser owns the focus trap, Esc and the
   inert page, and close() cannot hang on an animation. Sized in dvh, so
   it fits the viewport a phone actually shows.
   -------------------------------------------------------------------------- */
dialog.menu-dialog {
  width: min(88vw, 380px); max-height: 82dvh;
  padding: 0; border: none; background: transparent;
  margin: auto; overflow: visible;
  opacity: 0; transform: translateY(14px) scale(.97);
  transition: opacity .2s ease, transform .24s cubic-bezier(.2, 1.1, .4, 1);
}
dialog.menu-dialog[open] { opacity: 1; transform: none; }
@starting-style {
  dialog.menu-dialog[open] { opacity: 0; transform: translateY(14px) scale(.97); }
}
dialog.menu-dialog:not([open]) { display: none; }
dialog.menu-dialog::backdrop { background: rgba(24, 16, 6, .58); }

.menu-sheet {
  display: flex; flex-direction: column; gap: 12px;
  padding: 22px 20px 18px;
  background: #e7d1a5;
  background-image: -webkit-image-set(
    url("/static/art/dadi-parchment-neutral-seamless-web-512.webp") 1x,
    url("/static/art/dadi-parchment-neutral-seamless-web-1024.webp") 2x);
  background-image: image-set(
    url("/static/art/dadi-parchment-neutral-seamless-web-512.webp") 1x,
    url("/static/art/dadi-parchment-neutral-seamless-web-1024.webp") 2x);
  background-size: 512px 512px;
  border: 2px solid rgba(140, 104, 30, .55);
  box-shadow: 0 14px 40px rgba(0, 0, 0, .45);
}
.menu-door {
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: 52px; padding: 0 16px;
  font-family: var(--serif); font-size: 17px; letter-spacing: .16em;
  color: #2b2018; text-align: left; text-decoration: none;
  background: rgba(255, 252, 240, .5);
  border: 1px solid rgba(140, 104, 30, .5); border-radius: 4px;
  box-shadow: none;
}
.menu-door:active { background: rgba(255, 252, 240, .85); }
.menu-door .mk { color: var(--gold); font-size: 15px; }
.menu-foot {
  margin-top: 4px; text-align: center;
  font-family: var(--serif); font-size: 12px; letter-spacing: .1em;
  color: #6b5836; opacity: .8;
}

/* --------------------------------------------------------------------------
   THE CHEAPEST TURN THERE IS. A single painting cannot be rotated —
   rotation needs a second view of the object, and there is only one.
   What CAN be done, and what hand-drawn animation has always done, is
   NARROW it: squeeze the die across its width and let it swell back, and
   the eye reads a thing turning in place. It shows no new face, so it
   never lies about which numbers are up, and it costs not one byte.
   Zero on the dial switches it off entirely.
   -------------------------------------------------------------------------- */
#dieTurnly { animation: dieTurnly var(--die-turn-beat) ease-in-out infinite; }
@keyframes dieTurnly {
  0%, 100% { transform: scaleX(1); }
  50%      { transform: scaleX(calc(1 - var(--die-turn) * .82)); }
}

/* A machine asking for less motion keeps the die, and keeps it still. */

/* --------------------------------------------------------------------------
   THE FOOTER BAR (founder, 2026-08-24). Four doors and a shield, fixed
   to the glass so it stays under the thumb while the room behind it
   scrolls. It serves the menu and the table only: the splash keeps its
   single door, and the antechamber is a waiting room with one way out.

   The shield is an inline path rather than a plate — crisp at any size,
   no request, and no icon kit, which this project has never shipped.
   -------------------------------------------------------------------------- */
#footBar {
  --fb-h: 62px;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  display: none; align-items: flex-end; justify-content: space-around;
  height: calc(var(--fb-h) + env(safe-area-inset-bottom, 0px));
  padding: 0 4px env(safe-area-inset-bottom, 0px);
  background: linear-gradient(180deg, #2a1d0b 0%, #1d1408 100%);
  border-top: 2px solid rgba(201, 162, 39, .55);
  box-shadow: 0 -8px 22px rgba(0, 0, 0, .38);
}
body:has(#s1.on) #footBar, body:has(#s2.on) #footBar { display: flex; }

/* the room makes way, so nothing hides under the bar */
body:has(#s1.on) #s1, body:has(#s2.on) #s2 {
  padding-bottom: calc(var(--fb-h, 62px) + 14px + env(safe-area-inset-bottom, 0px));
}

.fb {
  flex: 1; min-width: 0; height: var(--fb-h);
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 3px;
  background: none; border: none; box-shadow: none; border-radius: 0;
  color: #e6d2a0; padding: 0 2px;
}
.fb .gl { font-size: 17px; line-height: 1; color: var(--gold); opacity: .9; }
.fb .lb {
  font-family: var(--sans); font-size: 9.5px; letter-spacing: .13em;
  white-space: nowrap;
}
.fb:active .gl, .fb:active .lb { color: #fff3cf; }
.fb.on .gl, .fb.on .lb { color: #ffe9a8; }

/* the shield stands proud of the bar, the way a boss stands off a rim */
.fb-shield {
  /* proud of the rim, not perched on a pole */
  flex: 0 0 auto; width: 62px; height: calc(var(--fb-h) + 7px);
  margin-bottom: 1px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none;
}
.fb-shield svg { width: 46px; height: 51px; overflow: visible; }
.sh-body { fill: #7d1f18; }
.sh-rim {
  fill: none; stroke: url(#none); stroke: #c9a227; stroke-width: 2.6;
  stroke-linejoin: round;
}
.sh-mark { fill: #f0d68a; }
.fb-shield:active .sh-body { fill: #96261d; }
.fb-shield:active .sh-mark { fill: #fff3cf; }

/* the dev tab steps over the footer bar rather than onto its first door */
body:has(#s1.on) #dieDialsTab, body:has(#s2.on) #dieDialsTab {
  bottom: calc(62px + 12px + env(safe-area-inset-bottom, 0px));
}
body:has(#s1.on) #dieDials, body:has(#s2.on) #dieDials {
  bottom: calc(62px + 12px + env(safe-area-inset-bottom, 0px));
}

/* --------------------------------------------------------------------------
   THE STAKE ROW, AND WHAT SITS OVER WHAT (2026-08-24; on the menu since
   2026-10-02, the third row of the setup, above the meter).

   The presets ran off both edges of the phone with their words spilling
   out of them. `.row button` carries `flex: 1`, which is a ZERO basis —
   so the browser believes all seven fit on one line and never wraps,
   then `min-width: auto` pushes the text out past the box. Sized to
   their own content they wrap like any other words. On the menu they
   wear the queue's own stake presets' size (13px, tight), so the seven
   sit on one line on a phone and wrap only where one is too narrow.

   A sheet that is up owns the screen, so the footer bar stands down while
   one is open.
   -------------------------------------------------------------------------- */
#wagerPick { flex-wrap: wrap; gap: 5px; }
#wagerPick button {
  flex: 1 1 auto; max-width: none; min-width: 0;
  padding: 9px 5px; font-size: 13px; letter-spacing: .04em;
}
/* the pick is the fill, as on the mode tabs: no seal to widen the row */
#wagerPick button.picked::before { content: ""; }
/* a narrower phone (375: 301px of row against 325 needed) takes the seven
   a size down and tighter, rather than sending CUSTOM to a line of its own */
@media (max-width: 400px) {
  #wagerPick { gap: 4px; }
  #wagerPick button { padding: 9px 3px; font-size: 12px; letter-spacing: 0; }
}
#wagerCustomRow { margin-top: 6px; }

body:has(#s8.on) #footBar,
body:has(#s3.on) #footBar { display: none; }

/* --------------------------------------------------------------------------
   THE BOARDS: BIGGER, SQUARER, AND CUT FROM STONE (founder, 2026-08-24).

   The wells were a flat brown tint with a 12px radius on a 50px cell —
   rounded enough to read as buttons rather than sockets. They are marble
   now, from a seamless tile whose every term is periodic so it meets
   itself at all four edges, and the corners are barely broken. Each well
   takes the tile at a different offset, so no two are twins and the
   board reads as one slab that has been drilled, not a grid of coasters.
   -------------------------------------------------------------------------- */
.cell {
  border-radius: 5px;
  background-color: #ded5c2;
  background-image:
    linear-gradient(180deg, rgba(60, 44, 18, .17), rgba(60, 44, 18, .05) 42%,
                    rgba(255, 250, 236, .16)),
    url("/static/art/marble_well.webp");
  background-size: auto, 132px 132px;
  box-shadow:
    inset 0 5px 8px rgba(48, 33, 12, .46),      /* the near rim's shadow */
    inset 0 -4px 6px rgba(255, 250, 236, .40),  /* light pooling at the far rim */
    inset 0 0 0 1px rgba(70, 54, 26, .30),      /* the drilled edge */
    0 1px 0 rgba(255, 252, 242, .40);           /* the worn lip around it */
}
/* no two wells cut from the same inch of stone */
.cell:nth-child(6n+1) { background-position: 0 0, 0 0; }
.cell:nth-child(6n+2) { background-position: 0 0, -47px -23px; }
.cell:nth-child(6n+3) { background-position: 0 0, -91px -68px; }
.cell:nth-child(6n+4) { background-position: 0 0, -19px -101px; }
.cell:nth-child(6n+5) { background-position: 0 0, -74px -44px; }
.cell:nth-child(6n)   { background-position: 0 0, -112px -7px; }

.col:nth-child(2n) .cell:nth-child(6n+1) { background-position: 0 0, -33px -85px; }
.col:nth-child(3n) .cell:nth-child(6n+2) { background-position: 0 0, -8px -57px; }

/* room to breathe between bigger wells */
.board { gap: 8px; }
.col { gap: 7px; }

/* --------------------------------------------------------------------------
   THE FOOTER BAR STANDS DOWN (founder, 2026-08-24: "hide the bottom menu
   for now"). Kept whole — markup, wiring and dress — behind one gate, so
   it comes back by adding `bar` to the body and nothing has to be rebuilt.
   -------------------------------------------------------------------------- */
body:has(#s1.on) #footBar, body:has(#s2.on) #footBar { display: none; }
body.bar:has(#s1.on) #footBar, body.bar:has(#s2.on) #footBar { display: flex; }
body:has(#s8.on) #footBar,
body:has(#s3.on) #footBar { display: none; }
/* and the room takes its floor back */
body:has(#s1.on) #s1, body:has(#s2.on) #s2 { padding-bottom: 14px; }
body.bar:has(#s1.on) #s1, body.bar:has(#s2.on) #s2 {
  padding-bottom: calc(62px + 14px + env(safe-area-inset-bottom, 0px));
}
body:has(#s1.on) #dieDialsTab, body:has(#s2.on) #dieDialsTab,
body:has(#s1.on) #dieDials, body:has(#s2.on) #dieDials { bottom: 8px; }

/* --------------------------------------------------------------------------
   THE CHESSBOARD (founder, 2026-08-24). Two stones alternating, dark on
   the odd squares — a column's index plus a cell's index deciding which,
   which is exactly how a chessboard is coloured. The pale wells keep the
   dark-veined stone and the dark wells the pale-veined one, so a die
   reads against either.
   -------------------------------------------------------------------------- */
.col:nth-child(odd)  .cell:nth-child(even),
.col:nth-child(even) .cell:nth-child(odd) {
  background-color: #4a3d2e;
  background-image:
    linear-gradient(180deg, rgba(12, 8, 3, .34), rgba(12, 8, 3, .10) 42%,
                    rgba(255, 244, 214, .12)),
    url("/static/art/marble_well_dark.webp");
  box-shadow:
    inset 0 5px 9px rgba(8, 5, 2, .55),
    inset 0 -4px 7px rgba(255, 246, 218, .20),
    inset 0 0 0 1px rgba(20, 14, 6, .45),
    0 1px 0 rgba(255, 252, 242, .30);
}

/* --------------------------------------------------------------------------
   THE SLAB'S EDGE (founder, 2026-08-24: those rounded outer edges look
   weak). A 16px radius on a stone slab reads as a soft-cornered card.
   Marble is CUT — the corner is nearly a right angle, the arris broken
   just enough to catch light, and the edge is a chamfer of its own
   colour rather than a fade into the vellum. So: the radius comes almost
   all the way off, and the rim is stated with rings of shadow and light
   that go all the way round, which is what makes a cut edge read heavy.
   -------------------------------------------------------------------------- */
.board-frame {
  padding: 10px;
  border-radius: 3px;
  box-shadow:
    inset 0 0 0 1px rgba(255, 250, 235, .50),   /* the arris, catching light */
    inset 0 0 0 4px rgba(96, 72, 32, .40),      /* the chamfer's shade */
    inset 0 3px 5px rgba(255, 252, 242, .38),   /* the lit top face */
    inset 0 -4px 7px rgba(44, 30, 10, .34),     /* the underside, in shadow */
    0 3px 0 rgba(120, 92, 44, .55),             /* the slab's own thickness */
    0 10px 20px rgba(40, 26, 8, .46);           /* and what it casts */
}
.board-frame:has(.theirs) {
  border-radius: 3px;
  box-shadow:
    inset 0 0 0 1px rgba(255, 250, 235, .44),
    inset 0 0 0 4px rgba(96, 72, 32, .44),
    inset 0 3px 5px rgba(255, 252, 242, .30),
    inset 0 -4px 7px rgba(44, 30, 10, .40),
    0 3px 0 rgba(120, 92, 44, .50),
    0 10px 20px rgba(40, 26, 8, .42);
}
/* the columns stop rounding themselves inside a squared slab */
.col { border-radius: 3px; }
.cell { border-radius: 3px; }

/* --------------------------------------------------------------------------
   THE MIDDLE IS FOR THE DICE (founder, 2026-08-24). The band between the
   slabs held two cartouches, a guide, a banner, a fuse, a timer and the
   drand round. Everything that is not the landing ground has left: the
   names and scores to the backs of the boards, the round number to the
   receipt. What is left is only as tall as a die needs to fall.
   -------------------------------------------------------------------------- */
#strip {
  padding: 0; border: none; background: none;
  justify-content: center; gap: 0;
}
/* NOT flex:1 — that made the lane swallow every pixel the cartouches
   gave up, and the boards ended further apart than they began. The lane
   is worth one die plus the line that speaks over it, and no more. */
#strip { flex: 0 0 auto; }
#offerStage {
  flex: 0 0 auto;
  min-height: calc(var(--cell, 50px) + 34px);
  display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 2px;
}
#guide { margin: 0; }
#banner { margin: 0; }
#roundLine { display: none; }

/* the cartouches, now a thin line at each board's back rather than a box */
#s2 > .total {
  min-width: 0; width: min(300px, 92%); align-self: center;
  flex-direction: row; align-items: center; justify-content: center;
  gap: 9px; padding: 2px 10px 3px; margin: 0;
  border: none; border-radius: 0; box-shadow: none;
  background: none;
  border-bottom: 1px solid rgba(120, 95, 55, .38);
}
#s2 > #t1 { border-bottom: none; border-top: 1px solid rgba(120, 95, 55, .38); }
#s2 > .total .mark { width: 20px; height: 20px; }
#s2 > .total .who { font-size: 10px; letter-spacing: .18em; opacity: .8; }
#s2 > .total .n {
  font-family: var(--serif); font-size: 21px; line-height: 1;
  margin-left: auto;
}

/* the slabs draw closer: the gap is what a die needs, not a room */
#s2 .board-frame { margin: 0; }
#s2 > #t0 { margin-bottom: 3px; }
#s2 > #t1 { margin-top: 3px; }

/* --------------------------------------------------------------------------
   THE FRAME STANDS BACK AT THE TABLE (founder, 2026-08-24). Corner
   sprays sized for a splash crowd a playing surface: they reach in over
   the slabs and steal the width a board wants. On the table only, the
   corners come down and the wire draws in, so the marble has the page.
   -------------------------------------------------------------------------- */
body:has(#s2.on) { --fw: min(26vw, 24vh, 150px); --fi: clamp(4px, 1vw, 9px); }
/* THE BEZEL IS DROPPED (founder, 2026-09-13: "it is time we drop the arena
   bezel, since we are pressed for space anyway"): no leather margin round
   the column and no sheet padding on the table, so the boards have the
   whole width; the sheet's inset rings go with it below. */
body:has(#s2.on) #wrap { padding: 0; }
body:has(#s2.on) #page { padding: 0; }

/* The lane is now only what falls into it: a banner over a die. */
#offerStage { min-height: calc(var(--cell, 50px) + 18px); gap: 0; }
#s2 > #guide {
  text-align: center; margin: 2px 0 0; padding: 0 8px;
  font-size: 11px; letter-spacing: .1em; opacity: .75;
}
#s2 > #talkStrip { margin: 2px 0 0; }

/* --------------------------------------------------------------------------
   THE GAME'S BAR GOES TO THE GLASS (founder, 2026-08-24). #bar stops
   flowing at the end of the screen and pins itself to the bottom of the
   viewport. Nothing about the buttons changes — same elements, same ids,
   same order — so everything the game does to them still works: CAST
   coming and going each turn, RETRY taking its place, BRIBE lighting
   during a beat.

   CAST takes the middle and stands proud of the rim, because it is the
   button a player reaches for every turn. The others sit flat either
   side of it. The board makes room by the bar's own height, so nothing
   is ever hidden underneath it.
   -------------------------------------------------------------------------- */
#s2 #bar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
  margin: 0; min-height: 0; flex-wrap: nowrap;
  align-items: center; justify-content: space-around; gap: 2px;
  height: calc(66px + env(safe-area-inset-bottom, 0px));
  padding: 0 4px env(safe-area-inset-bottom, 0px);
  background: linear-gradient(180deg, #2a1d0b 0%, #1d1408 100%);
  border-top: 2px solid rgba(201, 162, 39, .55);
  box-shadow: 0 -8px 22px rgba(0, 0, 0, .38);
}
/* the room gives the bar its floor back */
body:has(#s2.on) #s2 {
  padding-bottom: calc(66px + 10px + env(safe-area-inset-bottom, 0px));
}

/* every hand in the bar reads the same: a word over an oath, in gold */
#s2 #bar > button, #s2 #bar .stackWrap > button, #s2 #bar .stack > button {
  flex: 1 1 0; min-width: 0; max-width: none; height: 52px;
  padding: 0 4px; border-radius: 5px;
  display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 1px;
  background: rgba(255, 250, 235, .06);
  border: 1px solid rgba(201, 162, 39, .30);
  box-shadow: none; color: #e6d2a0;
}
#s2 #bar > button .word, #s2 #bar .stackWrap > button .word, #s2 #bar .stack > button .word {
  font-family: var(--sans); font-size: 11px; letter-spacing: .12em;
  line-height: 1; white-space: nowrap;
}
#s2 #bar > button .oath, #s2 #bar .stackWrap > button .oath, #s2 #bar .stack > button .oath {
  font-size: 8.5px; letter-spacing: .06em; opacity: .62;
  line-height: 1.1; white-space: nowrap;
}
#s2 #bar > button:active, #s2 #bar .stackWrap > button:active, #s2 #bar .stack > button:active { background: rgba(255, 250, 235, .16); }
#s2 #bar > button[hidden], #s2 #bar .stack > button[hidden] { display: none; }
/* THE STACKS (founder, 2026-09-14): OPTIONS at the right and POWERUPS at
   the left each open a column of hands upward from their door, over the
   table, one at a time; the door lights while its column stands */
#s2 #bar > .stackWrap { position: relative; flex: 1 1 0; min-width: 0; display: flex; }
#s2 #bar .stackWrap > button.open { background: rgba(201, 162, 39, .22); border-color: rgba(201, 162, 39, .6); }
#s2 #bar .stack {
  position: absolute; bottom: calc(100% + 8px); width: 140px; z-index: 5;
  display: flex; flex-direction: column; gap: 4px; padding: 6px;
  background: linear-gradient(180deg, #2a1d0b 0%, #1d1408 100%);
  border: 1px solid rgba(201, 162, 39, .5); border-radius: 8px;
  box-shadow: 0 -8px 22px rgba(0, 0, 0, .45);
}
#s2 #bar .stack[hidden] { display: none; }
#s2 #bar #powerWrap .stack { left: 0; }
#s2 #bar #optWrap .stack { right: 0; }
#s2 #bar .stack > button { width: 100%; flex: none; height: 50px; }
#s2 #bar .stack > button .oath { white-space: normal; text-align: center; }
#s2 #bar > #powerWrap { order: -3; }
#s2 #bar > #focusTab { order: 1; }
#s2 #bar > #optWrap { order: 2; }
#s2 #bar #focusTab.on, #s2 #bar #focusTab.half { color: #2a1d06; background: var(--leaf); border-color: #6b5512; }
#s2 #bar #focusTab.half { background: linear-gradient(180deg, rgba(201, 162, 39, .55), rgba(150, 118, 30, .55)); }

/* CAST in the middle, proud of the rim — the hand reached for every turn */
#s2 #bar > #cast, #s2 #bar > #retry {
  order: 0; flex: 0 0 auto; width: 92px; height: 62px;
  margin-bottom: 4px;
  background: linear-gradient(180deg, #8d2019 0%, #6d1710 100%);
  border: 2px solid #c9a227; border-radius: 7px 7px 16px 16px;
  color: #ffeec2;
  box-shadow: 0 3px 0 rgba(80, 16, 10, .8), 0 6px 14px rgba(0, 0, 0, .42);
}
#s2 #bar > #cast .word, #s2 #bar > #retry .word {
  font-family: var(--serif); font-size: 16px; letter-spacing: .18em;
}
#s2 #bar > #cast:active, #s2 #bar > #retry:active {
  background: linear-gradient(180deg, #a52a20 0%, #7d1c14 100%);
}
/* two hands to the left of the shield, two to its right */
#s2 #bar > #bribeBtn { order: -1; }

/* The purse, now a quiet line under the score. It was painted for a dark
   bar — gold with a glow behind it — and reads as a ghost on parchment,
   so it is re-inked for the page it actually sits on. */
#s2 > #barPurse {
  align-self: center; width: min(300px, 92%);
  flex-direction: row; align-items: baseline; justify-content: center;
  flex-wrap: wrap; gap: 4px 12px;
  margin: 0 0 1px; padding: 0 10px;
  font-family: var(--sans); font-size: 11px; letter-spacing: .09em;
  color: #6b5836; text-shadow: none; background: none;
  opacity: .85;
}
#s2 > #barPurse .riding { font-size: 11px; opacity: .75; }
#s2 > #barPurse a, #s2 > #barPurse .receipt {
  color: #8a6a1e; font-size: 10px; letter-spacing: .14em;
}

/* --------------------------------------------------------------------------
   EACH PLAYER STANDS BESIDE THEIR OWN BOARD (founder, 2026-08-24). A
   three-wide slab leaves dead vellum either side of it on a phone, and
   that is where a shield and a score belong — beside the board they
   belong to, not stacked above and below where they cost height the
   marble wants. Opposite sides, so the two never read as one column.
   -------------------------------------------------------------------------- */
#s2 .board-frame { position: relative; overflow: visible; }
#s2 .board-frame > .total {
  position: absolute; top: 50%; transform: translateY(-50%);
  width: clamp(58px, 20vw, 84px);
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 0; margin: 0; padding: 5px 2px 6px;
  border: 1px solid rgba(140, 104, 30, .38); border-radius: 3px;
  background: linear-gradient(180deg, rgba(255, 252, 240, .72),
                                      rgba(214, 196, 158, .40));
  box-shadow: inset 0 1px 0 rgba(255, 250, 235, .7), 0 1px 3px rgba(0, 0, 0, .28);
}
/* the far board's owner on the right, the near board's on the left */
/* A portrait wants to be seen, and the margin beside a slab is not wide
   enough for one at any size worth having. So the medallion STRADDLES
   the slab's outer edge — mostly out in the vellum, its inner sliver
   over the frame's own border, which is stone rather than playing
   surface. That buys three times the face for twenty pixels of edge. */
#s2 .board-frame:has(.theirs) > .total { left: calc(100% - 20px); }
#s2 .board-frame:has(.mine)   > .total { right: calc(100% - 20px); }

#s2 .board-frame > .total .mark {
  width: clamp(46px, 16vw, 68px); height: clamp(46px, 16vw, 68px);
  margin: 0; border-radius: 50%;
  border: 2px solid rgba(201, 162, 39, .75);
  background: rgba(255, 252, 240, .6);
  box-shadow: 0 2px 6px rgba(40, 26, 8, .40);
}
#s2 .board-frame > .total .who {
  font-family: var(--serif); font-size: 8px; letter-spacing: .12em;
  opacity: .8; text-align: center; line-height: 1.1;
  overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
#s2 .board-frame > .total .n {
  font-family: var(--serif); font-size: 22px; line-height: 1; margin: 0;
}

/* The sandwich has no business at the table (founder, 2026-08-24): the
   match has its own bar, and a second way out over the board is noise. */
body:has(#s2.on) .burger { display: none; }

/* --------------------------------------------------------------------------
   A BEZEL AT THE TABLE, NOT A GARDEN (founder, 2026-08-24). The corner
   sprays belong to a title page. Over a match they crowd the slabs and
   the top-right one grows straight through the opponent's portrait. So
   at the table the whole flowered frame stands down and the page keeps
   only its own gold bezel — which was under the flowers all along.
   -------------------------------------------------------------------------- */
body:has(#s2.on) .bframe { display: none; }
body:has(#s2.on) #page {
  border-width: 0;                  /* the bezel is dropped (founder, 2026-09-13) */
  box-shadow: none;
}

/* --------------------------------------------------------------------------
   THREE THINGS THE CATCH-UP SWEEP CAUGHT (2026-08-24). Each had a cause
   in the file rather than a guess: a stale rule written for furniture
   that has since stood down, a fixed height arriving from the vendored
   library, and a dialog child that never claimed its width.
   -------------------------------------------------------------------------- */

/* The dev tab was sitting ON the CAST plaque. An earlier block lifted it
   clear of the footer bar; a later block — same selectors, same weight —
   reset it to bottom:8px once that bar stood down. But the MATCH keeps
   its own action bar at the foot of the table, so at s2 the tab covered
   the lower two thirds of CAST and only the top strip could be tapped.
   The lift belongs to the table, not to the retired bar. */
body:has(#s2.on) #dieDialsTab,
body:has(#s2.on) #dieDials {
  bottom: calc(66px + 12px + env(safe-area-inset-bottom, 0px));
}

/* "No writing ever escapes its plaque" (founder, 2026-08-22) had come
   undone on five buttons — four in the gear sheet, one on the verdict.
   The cause is vendored: Web Awesome's native.css puts a FIXED height on
   every bare <button>, so a label that wraps to three lines paints
   outside a box that never grew. This file already cures it twice, for
   .glyphed and for the roster champs; the plain rows were simply never
   given the same treatment. min-height still holds the floor at 44px. */
.row button, #claimGo { height: auto; }

/* The menu sheet shrank away from its own dialog: the library styles a
   bare <dialog> as a flex column with align-items:start, so a child
   without a width wraps its content and leaves the round × floating over
   the page. The rules sheet already answers this with width:100%. */
.menu-sheet { width: 100%; }

/* --------------------------------------------------------------------------
   THE SPLASH BECOMES ONE BLOCK (founder, 2026-08-25: "the logo, beast,
   writing, button should form a cohesive block", and on desktop the
   page's lower edge fell off the screen with a gulf under the beast).

   All three complaints had one cause between them. `.pitch-art` carried
   `margin-top: auto`, which does not add space — it eats ALL the spare
   space and flings the writing and the door to the section's foot. That
   is the gulf under the beast, and it is why the block never read as a
   block. It is gone; the four pieces now sit on one even gap and the
   group is centred as a whole.

   Two more things were making the page taller than the window. The
   section demanded `min-height: 100dvh` while sitting inside 48px of
   ancestor padding, so it could never fit by construction; and every
   piece was sized by WIDTH alone, so a short window kept full-size art
   and pushed the only door off the bottom. Each piece is now bounded by
   the height it actually has as well, and the whole block shrinks
   together rather than spilling.

   Measured after: 1440x900 overflow 194 -> 0, 1280x800 286 -> 0,
   390x844 48 -> 0, 768x1024 70 -> 0, 320x568 137 -> 15 with the door
   fully on screen at every one of them.
   -------------------------------------------------------------------------- */
#s0 {
  justify-content: center;       /* the block sits as one group */
  min-height: 0;                 /* stop asking for a height it cannot have */
  padding-top: min(calc(var(--fw) * 0.5), 92px, 11vh);
  padding-bottom: min(calc(var(--fw) * 0.3), 56px, 7vh);
  gap: calc(var(--col) * 0.022);
  --beastW: min(460px, calc(100vw - 30px), 42vh);
  --door: min(295px, 74vw, 26vh);
}
#s0 .pitch-art {
  margin-top: 0;                 /* NOT auto — auto ate the spare space */
  width: min(330px, 82%, 30vh);
}
#s0 .incipit img.logo-full { width: min(360px, 88%, 34vh); }

/* the owl's own composition keeps its narrower proportions, and takes
   the same height bound so it cannot spill either */
body.rim-owl #s0 {
  --beastW: min(365px, calc(100vw - 96px), 38vh);
  --door: min(235px, 60vw, 24vh);
}
body.rim-owl #s0 .pitch-art { width: min(248px, 64%, 26vh); }

/* A short window — a landscape phone, or the oldest small one — cannot
   hold the block at its full size no matter how the padding is trimmed.
   Rather than shrink the art on every phone to satisfy the shortest, the
   caps tighten only where the height is genuinely scarce. Measured at
   851x393: the door came fully onto the screen and the page's spill fell
   from 53px to 11. Portrait phones and desktops never see this block. */
@media (max-height: 600px) {
  #s0 {
    padding-top: min(calc(var(--fw) * 0.5), 92px, 9vh);
    padding-bottom: min(calc(var(--fw) * 0.3), 56px, 5vh);
    --beastW: min(460px, calc(100vw - 30px), 38vh);
    --door: min(295px, 74vw, 23vh);
  }
  #s0 .pitch-art { width: min(330px, 82%, 27vh); }
  #s0 .incipit img.logo-full { width: min(360px, 88%, 31vh); }
}

/* --------------------------------------------------------------------------
   THE FLOWERED FRAME'S CORNERS AGREE ABOUT THE WIRE (founder, 2026-08-25:
   "the lower left corner is too big, look at the coupling differences").

   Both complaints were the same fault. The four plates are drawn at four
   different zooms — the bottom-left carries a 33px wire where the
   top-left carries 19 — so showing them at one scale gave the contour
   four thicknesses, a step at every joint, and a bottom-left that looked
   oversized because a thicker wire means a bigger everything. My earlier
   patches made it worse rather than better: the top-left was grown 7%
   and the bottom-right 20% by eye, and the left rail was set to a
   24-native "compromise" between a 19 and a 33.

   Each plate is now scaled by ITS OWN wire so all four draw the same
   line, and positioned by where that wire sits rather than by its box —
   the wires hide at four different insets (the top-right 33px inside its
   canvas, the bottom-right flush). The rails are cropped to their wire,
   so each rail div IS the line and simply runs between the boxes.
   -------------------------------------------------------------------------- */
.bf-tl { width: calc(1182.6 * var(--fk)); height: calc(1191.8 * var(--fk)); top: calc(var(--fi) - 3.1 * var(--fk)); left: calc(var(--fi) - 2.1 * var(--fk)); }
.bf-tr { width: calc(1042.4 * var(--fk)); height: calc(965.6 * var(--fk)); top: calc(var(--fi) - 18.4 * var(--fk)); right: calc(var(--fi) - 26.4 * var(--fk)); }
.bf-bl { width: calc(698.4 * var(--fk)); height: calc(738.4 * var(--fk)); bottom: calc(var(--fi) - 3.8 * var(--fk)); left: calc(var(--fi) - 0.0 * var(--fk)); }
.bf-br { width: calc(1166.5 * var(--fk)); height: calc(1038.9 * var(--fk)); bottom: calc(var(--fi) - 0.0 * var(--fk)); right: calc(var(--fi) - 0.0 * var(--fk)); }

.bf-t, .bf-b { height: calc(20.0 * var(--fk)); }
.bf-l, .bf-r { width: calc(20.0 * var(--fk)); }
.bf-t { top: var(--fi); left: calc(var(--fi) + 1179.5 * var(--fk)); right: calc(var(--fi) + 1015.0 * var(--fk)); }
.bf-b { bottom: var(--fi); left: calc(var(--fi) + 697.4 * var(--fk)); right: calc(var(--fi) + 1165.5 * var(--fk)); }
.bf-l { left: var(--fi); top: calc(var(--fi) + 1187.7 * var(--fk)); bottom: calc(var(--fi) + 733.6 * var(--fk)); }
.bf-r { right: var(--fi); top: calc(var(--fi) + 946.2 * var(--fk)); bottom: calc(var(--fi) + 1037.9 * var(--fk)); }

/* The bottom-left plate, redrawn by the founder to the spec the
   measurements asked for — a 19px wire in a 1080px canvas — so it now
   belongs to the same family as the other three and takes the same
   arithmetic they do rather than any fudge. Its upright arm renders
   20.0 and its foot 20.0 against a 20 rail, both within a twentieth.
   In wire-units the four corners now read 58, 52, 57 and 57: one kit. */
.bf-bl {
  width: calc(1136.8 * var(--fk)); height: calc(1136.8 * var(--fk));
  bottom: calc(var(--fi) - 0.0 * var(--fk));
  left: calc(var(--fi) - 0.0 * var(--fk));
}
.bf-b { left: calc(var(--fi) + 1135.8 * var(--fk)); }
.bf-l { bottom: calc(var(--fi) + 1135.8 * var(--fk)); }

/* --------------------------------------------------------------------------
   THREE CHARACTERS OF FLOAT (founder, 2026-08-25), and a placement block
   that grows one line per beast.

   The die's shells already read every number from a custom property, so a
   character costs no new machinery at all — only a different set of
   values. Each beast names one in the cast and wears it as a body class.

     drift  the scholar's: a long slow rise and a wide, unhurried lean
     skip   the jester's:  quick and near, never quite still
     throb  the conjurer's: barely rises, but the light in it pulses

   PLACEMENT is per beast and lives at the foot of this block, one rule
   each, written by tools/beast_plate.py — which lifts the painted die off
   the plate and measures where it sat as fractions of the beast's own box.
   Fractions, so the die stays in the hand from a 320px phone to a desktop.
   A legacy beast whose die cannot be separated gets no rule and keeps its
   paint. Living plates use registered body, die, eye and magic layers.
   -------------------------------------------------------------------------- */
body.die-drift #dieLev {
  --die-bob: 34%;    --die-bob-beat: 2.6s;
  --die-lean: 3.2deg; --die-lean-beat: 9.4s;
  --die-aura-strength: .82;
}
body.die-skip #dieLev {
  --die-bob: 60%;    --die-bob-beat: .85s;
  --die-lean: 4.5deg; --die-lean-beat: 7.3s;
  --die-aura-strength: 1;
}
body.die-throb #dieLev {
  --die-bob: 22%;    --die-bob-beat: 3.4s;
  --die-lean: 1.8deg; --die-lean-beat: 11.5s;
  --die-aura: 320%;  --die-glow-bloom: 12px;
}
/* the conjurer's die swells with its own light, on a beat of its own so
   the pulse never lands with the rise */
body.die-throb .die-aura { animation-duration: 2.1s; }
body.die-throb #dieArt { animation: dieThrob 2.1s ease-in-out infinite alternate; }
@keyframes dieThrob {
  from { transform: scale(.965); }
  to   { transform: scale(1.035); }
}
/* a machine asking for stillness keeps the light but loses the swell */
@media (prefers-reduced-motion: reduce) {
  body:not(.die-force-motion).die-throb #dieArt { animation: none; }
}

/* ---- the placement block: one line per beast, written by the tool ---- */
body.beast-baboon #dieLev { --die-x: 24.7%; --die-y: 21.9%; --die-w: 15.98%; display: block; }

/* --------------------------------------------------------------------------
   THE LIVING PLATE — REGISTERED CHARACTER RIGS (2026-08-25).

   The painting remains a painting. Life is supplied only by aperture-clipped
   blink paint and continuously translated irises, the separated die and drawn
   stars, one palm-to-die pencil spring, and a compact radiating aura. The
   sockets, brows, muzzle, costume, hands and ground never move. CSS owns
   continuous motion; beast-rig.js serializes blink, gaze and magic cues.
   -------------------------------------------------------------------------- */
body.beast-royal_baboon #dieLev {
  --die-x: 16.89%; --die-y: 21.2%; --die-w: 12.12%;
  /* THE BIGGER DIE (founder, 2026-09-28: "make the die bigger as standard",
     then "The die should be even bigger", then "maybe make the die 5%
     smaller"): drawn 1.425 times its first size, 48 -> 68 px at 430. Everything the approved entrance aims at his palm is
     measured in the die's own size -- where it rises from and how small it
     starts (dieConjureRise), the swirl's field (#dieMagicField), the float --
     so each is divided by the same --die-grow and stays where it was. */
  --die-grow: 1.425;
  /* 14.55% on the 10%-larger die preserved the approved screen-space rise;
     on the die 1.425 times as large, 14.55 / 1.425 keeps it. */
  --die-bob: 10.21%; --die-bob-beat: 3.2s;
  --die-lean: 2.4deg; --die-lean-beat: 8.8s;
  --die-aura: 185%; --die-aura-strength: .68;
  --die-glow-core: 1.5px; --die-glow-bloom: 7px;
  --die-glow-hue: 237, 176, 46;
  display: block;
}
body.beast-owl #dieLev {
  /* Runtime manifest: 1200x972 plate, die box 159.5,174 to 359.5,398. */
  --die-x: 21.625%; --die-y: 29.424%; --die-w: 16.667%;
  --die-bob: 12%; --die-bob-beat: 3.45s;
  --die-lean: 2.1deg; --die-lean-beat: 9.6s;
  --die-aura: 172%; --die-aura-strength: .62;
  --die-glow-core: 1.25px; --die-glow-bloom: 6px;
  --die-glow-hue: 232, 174, 49;
  display: block;
}
body.beast-kneeling_owl #dieLev {
  /* 1200x1180 registered plate; the foreground talons remain above the cube. */
  --die-x: 11.7942%; --die-y: 22.7824%; --die-w: 15.3074%;
  --die-bob: 5.5%; --die-bob-beat: 3.8s;
  --die-lean: 1.7deg; --die-lean-beat: 10.4s;
  --die-aura: 155%; --die-aura-strength: .52;
  --die-glow-core: 1.15px; --die-glow-bloom: 5.5px;
  --die-glow-hue: 235, 178, 51;
  display: block;
}
body.beast-courtly_raven #dieLev {
  /* A slower courtly float above one feathered finger. */
  --die-x: 19.597%; --die-y: 18.456%; --die-w: 17.86%;
  --die-bob: 12%; --die-bob-beat: 3.65s;
  --die-lean: 2.3deg; --die-lean-beat: 9.9s;
  --die-aura: 172%; --die-aura-strength: .63;
  --die-glow-core: 1.25px; --die-glow-bloom: 6px;
  --die-glow-hue: 214, 168, 49;
  display: block;
}
body.beast-cloud_sage_monkey #dieLev {
  /* The sage barely disturbs the die: a contemplative, shallow hover. */
  --die-x: 22.721%; --die-y: 27.508%; --die-w: 17.253%;
  --die-bob: 7%; --die-bob-beat: 4.15s;
  --die-lean: 1.8deg; --die-lean-beat: 11.2s;
  --die-aura: 165%; --die-aura-strength: .58;
  --die-glow-core: 1.2px; --die-glow-bloom: 5.8px;
  --die-glow-hue: 241, 185, 65;
  display: block;
}
body.beast-robin_hood_fox #dieLev {
  /* A lively high-right flourish, with the fox's original palm below it. */
  --die-x: 87.402%; --die-y: 25.446%; --die-w: 20.214%;
  --die-bob: 11%; --die-bob-beat: 3.25s;
  --die-lean: 2.8deg; --die-lean-beat: 8.7s;
  --die-aura: 176%; --die-aura-strength: .66;
  --die-glow-core: 1.25px; --die-glow-bloom: 6.2px;
  --die-glow-hue: 226, 173, 48;
  display: block;
}
.beast-rig-live {
  cursor: pointer; touch-action: manipulation;
  outline: none;
}
.beast-rig-live:focus-visible {
  outline: none;
}
.beast-rig-live:focus-visible #dieLev {
  filter: drop-shadow(0 0 2px rgba(255, 241, 174, .96))
          drop-shadow(0 0 6px rgba(201, 146, 30, .72));
}
.beast-rig-live .beast-face-layer {
  z-index: 5;
  transform: none;
  transition: none;
  will-change: opacity;
}
.beast-gaze-backdrop,
.beast-gaze-aperture,
.beast-gaze-actor,
.beast-gaze-occluder,
.beast-die-occluder {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.beast-gaze-backdrop,
.beast-gaze-actor,
.beast-gaze-occluder,
.beast-die-occluder {
  object-fit: contain;
}
.beast-gaze-backdrop {
  z-index: 2;
}
.beast-gaze-aperture {
  z-index: 3;
  display: block;
  overflow: hidden;
}
.beast-gaze-occluder {
  z-index: 4;
}
.beast-die-occluder {
  z-index: 5;
}
/* The gaze stack appears only as one complete registered unit. If the fixed
   original-paint socket rim is missing, the untouched base eyes remain rather
   than exposing reconstructed sclera over a lid. */
.beast-gaze-backdrop,
.beast-gaze-aperture,
.beast-gaze-occluder {
  visibility: hidden;
}
.beast-rig-live.is-gaze-ready .beast-gaze-backdrop,
.beast-rig-live.is-gaze-ready .beast-gaze-aperture,
.beast-rig-live.is-gaze-ready .beast-gaze-occluder {
  visibility: visible;
}
/* These openings follow the painted inner wet-line. The original registered
   lid/socket pixels sit above them, so reconstructed whites physically cannot
   cover the inked rims even while an iris is at full travel. */
body.beast-royal_baboon .beast-gaze-aperture.is-viewer-left {
  clip-path: polygon(
    40.75% 18.994%, 41% 18.239%, 41.625% 17.61%, 42.5% 17.233%,
    43.5% 17.358%, 44.25% 17.736%, 44.875% 18.365%, 45.25% 19.119%,
    45.125% 19.748%, 44.625% 20.377%, 43.875% 20.881%, 42.875% 21.132%,
    42% 20.881%, 41.25% 20.377%, 40.875% 19.748%
  );
}
body.beast-royal_baboon .beast-gaze-aperture.is-viewer-right {
  clip-path: polygon(
    48% 20.503%, 48.375% 19.748%, 49.125% 19.245%, 50.125% 18.868%,
    51.25% 18.868%, 52.375% 19.245%, 53.25% 19.748%, 53.875% 20.377%,
    54.125% 21.006%, 53.625% 21.635%, 52.625% 22.264%, 51.5% 22.642%,
    50.375% 22.516%, 49.375% 22.138%, 48.625% 21.635%, 48.125% 21.132%
  );
}
body.beast-owl .beast-gaze-aperture.is-viewer-left {
  clip-path: polygon(
    42.75% 27.778%, 43.833% 26.183%, 46% 25.206%,
    48.375% 25.72%, 50.333% 27.675%, 51.292% 30.195%,
    50.958% 33.025%, 49.583% 35.237%, 47.083% 36.06%,
    44.583% 34.979%, 42.917% 32.922%, 42.167% 30.35%
  );
}
body.beast-owl .beast-gaze-aperture.is-viewer-right {
  clip-path: polygon(
    56.875% 26.749%, 58.75% 25.566%, 61.042% 25.617%,
    63.125% 26.749%, 64.583% 28.807%, 65% 31.379%,
    64.375% 33.693%, 62.917% 35.494%, 60.625% 36.008%,
    58.542% 34.979%, 57.292% 33.179%, 56.667% 30.607%
  );
}
.beast-gaze-actor {
  z-index: 1;
  transform: translate3d(0, 0, 0);
  will-change: transform;
  backface-visibility: hidden;
}
.beast-gaze-backdrop[hidden],
.beast-gaze-actor[hidden],
.beast-gaze-occluder[hidden],
.beast-die-occluder[hidden] {
  display: none;
}
.beast-rig-live[data-face-state="blink10"] #beastFaceBlink10,
.beast-rig-live[data-face-state="blink25"] #beastFaceBlink25,
.beast-rig-live[data-face-state="blink50"] #beastFaceBlink50,
.beast-rig-live[data-face-state="blink75"] #beastFaceBlink75,
.beast-rig-live[data-face-state="blinkClosed"] #beastFaceBlinkClosed { opacity: 1; }
body.die-living #dieBob {
  animation-name: dieLivingBob;
  animation-duration: var(--die-bob-beat);
  animation-timing-function: cubic-bezier(.37, .02, .58, 1);
  animation-iteration-count: infinite;
}
body.die-living #dieTilt {
  animation-name: dieLivingLean;
  animation-duration: var(--die-lean-beat);
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
}
body.die-living #dieArt {
  animation: dieLivingLight 2.83s ease-in-out infinite;
}
body.die-living .die-aura {
  animation-duration: 2.85s;
  background:
    radial-gradient(circle at 38% 35%, rgba(var(--die-glow-hot), .84), transparent 13%),
    radial-gradient(closest-side, rgba(var(--die-glow-primary), .70),
      rgba(var(--die-glow-hue), .38) 51%, rgba(var(--die-glow-hue), 0) 78%);
}

#dieBob, #dieTurnly { position: relative; }
#dieMagicField,
#dieStarUnderlay,
#dieStarField,
#dieSmokeField,
#dieSparkField {
  position: absolute; left: 50%; top: 50%; width: 270%; aspect-ratio: 1;
  z-index: 0; pointer-events: none; overflow: visible;
  transform: translate(-50%, -50%);
}
#dieStarUnderlay,
#dieStarField,
#dieSmokeField,
#dieSparkField { display: block; }
body.beast-royal_baboon #dieStarUnderlay { z-index: 1; }
/* the swirl from his palm keeps its size and place whatever the die's size */
body.beast-royal_baboon #dieMagicField { width: calc(270% / var(--die-grow, 1)); }
body.beast-royal_baboon #dieStarField { z-index: 3; }
/* Atmosphere is a sibling of the die, never a layer painted onto its faces.
   Both channels sit behind #dieTilt so pips and material remain immaculate. */
body.beast-royal_baboon #dieSmokeField { z-index: 0; }
body.beast-royal_baboon #dieSparkField { z-index: 1; }
#dieMagicField::after {
  content: ""; position: absolute; left: 13%; top: 67%; width: 49%; height: 27%;
  border-radius: 50%; opacity: .12; transform: rotate(-14deg);
  background: radial-gradient(ellipse,
    rgba(255, 226, 126, .58), rgba(237, 176, 46, .20) 46%, transparent 76%);
  filter: blur(2.5px); mix-blend-mode: screen;
}
#dieMagicRays {
  position: absolute; left: 50%; top: 50%; width: 72%; aspect-ratio: 1;
  transform: translate(-50%, -50%);
  opacity: .62; border-radius: 50%;
  background: radial-gradient(ellipse at center,
    rgba(255, 247, 202, .54) 0 14%,
    rgba(241, 184, 50, .22) 31%,
    rgba(241, 184, 50, 0) 70%);
  filter: drop-shadow(0 0 2px rgba(255, 218, 112, .82));
  animation: magicRaysBreathe 4.15s ease-in-out infinite alternate;
}
#dieMagicRays::before,
#dieMagicRays::after {
  /* The old crossed gradients made an eight-spoke wheel that read as a 3D
     debug/ray-tracing artifact whenever the clean canvas die rotated over it.
     The radial wash, individual painted stars and pencil spring carry the
     magic without placing mechanical lines behind the die. */
  content: none;
}
.die-magic-piece {
  position: absolute; height: auto; max-width: none;
  opacity: 0; pointer-events: none; object-fit: contain;
  scale: var(--magic-sign-scale, 1); transform-origin: center;
  filter: drop-shadow(0 0 1px rgba(58, 34, 6, .78))
          drop-shadow(0 0 3px rgba(255, 205, 72, .66));
}
#magicStarGoldLarge { left: 7%; top: 4%; width: 25%; }
#magicStarBlue { right: 7%; top: 17%; width: 17%; }
#magicStarRedSmall { left: 16%; bottom: 12%; width: 12%; }
#magicStarGoldEcho { right: 18%; bottom: 7%; width: 10%; }
#magicStarBlueEcho { left: 38%; top: -1%; width: 9%; }
/* The owl's three signs return to the precise places from which they were
   lifted. Echo actors add two neighbouring notes without baking the signs
   into one flashing plate. */
body.beast-owl #magicStarGoldLarge { left: 34.84%; top: 65.49%; width: 7.55%; }
body.beast-owl #magicStarBlue { left: 45.34%; top: 71.36%; width: 5.98%; }
body.beast-owl #magicStarRedSmall { left: 55.72%; top: 66.33%; width: 8.82%; }
body.beast-owl #magicStarGoldEcho { left: 41.2%; top: 60.2%; width: 7.1%; }
body.beast-owl #magicStarBlueEcho { left: 52.4%; top: 76.3%; width: 5.9%; }

/* Separate, bounded actor pools: hand-inked vapour and painted sparks.  These
   deliberately use flat manuscript pigment, pencil contours and small
   highlights instead of photographic fog, canvas particles or neon dots. */
.die-smoke-actor,
.die-spark-actor {
  position: absolute; pointer-events: none; opacity: 0;
  transform-origin: center; will-change: left, top, transform, opacity;
}
.die-smoke-actor {
  left: var(--smoke-origin-x, 50%); top: var(--smoke-origin-y, 31%);
  width: var(--smoke-size, 9%); aspect-ratio: 1.18;
  border: 1.35px solid rgba(var(--smoke-ink-rgb, 74, 51, 27), .88);
  border-radius: 58% 42% 61% 39% / 45% 55% 44% 56%;
  background:
    radial-gradient(circle at 33% 29%, rgba(255, 241, 199, .84) 0 9%, transparent 25%),
    radial-gradient(ellipse at 53% 58%,
      rgba(var(--smoke-rgb, 111, 88, 50), .88) 0 27%,
      rgba(var(--smoke-rgb, 111, 88, 50), .62) 52%,
      rgba(242, 222, 171, .74) 76%, rgba(242, 222, 171, .20) 100%);
  box-shadow:
    inset 1px 1px 0 rgba(255, 239, 194, .72),
    inset -1px -1px 0 rgba(var(--smoke-ink-rgb, 74, 51, 27), .26),
    0 0 0 .55px rgba(236, 190, 78, .38);
  filter: drop-shadow(.45px .65px 0 rgba(38, 26, 17, .38));
}
.die-smoke-actor::before,
.die-smoke-actor::after {
  content: ""; position: absolute;
  border: 1.15px solid rgba(var(--smoke-ink-rgb, 74, 51, 27), .82);
  background: rgba(var(--smoke-rgb, 111, 88, 50), .74);
  box-shadow: inset .7px .7px 0 rgba(255, 239, 194, .58);
}
.die-smoke-actor::before {
  left: -19%; top: 39%; width: 49%; height: 46%;
  border-radius: 64% 36% 55% 45% / 51% 58% 42% 49%;
  transform: rotate(-23deg);
}
.die-smoke-actor::after {
  right: -13%; top: 12%; width: 43%; height: 39%;
  border-radius: 43% 57% 61% 39% / 55% 41% 59% 45%;
  transform: rotate(21deg);
}
.die-smoke-actor.is-active.is-smoke {
  animation: dieSmokeBillow var(--effect-duration, 3200ms)
    cubic-bezier(.18, .58, .24, 1) both;
}
.die-smoke-actor[data-smoke-model="wisps"] {
  aspect-ratio: .42;
  border-width: 1px 1.45px;
  border-radius: 48% 52% 44% 56% / 76% 69% 31% 24%;
  background:
    linear-gradient(104deg, transparent 0 21%, rgba(255, 242, 207, .72) 28% 35%, transparent 43%),
    linear-gradient(to top,
      rgba(var(--smoke-rgb, 111, 88, 50), .82),
      rgba(var(--smoke-rgb, 111, 88, 50), .48) 58%,
      rgba(246, 227, 181, .16));
  box-shadow: inset 1px 0 rgba(255, 241, 204, .62);
}
.die-smoke-actor[data-smoke-model="wisps"]::before {
  left: -23%; top: 44%; width: 54%; height: 38%;
  border-width: 1px 0 0 1px; border-radius: 70% 0 0 38%;
  background: rgba(var(--smoke-rgb, 111, 88, 50), .38);
  transform: rotate(-19deg) skewY(-12deg);
}
.die-smoke-actor[data-smoke-model="wisps"]::after {
  right: -28%; top: 18%; width: 57%; height: 32%;
  border-width: 1px 1px 0 0; border-radius: 0 72% 36% 0;
  background: rgba(var(--smoke-rgb, 111, 88, 50), .28);
  transform: rotate(17deg) skewY(14deg);
}
.die-smoke-actor[data-smoke-model="wisps"].is-active.is-smoke {
  animation: dieSmokeWisp var(--effect-duration, 3200ms)
    cubic-bezier(.16, .54, .24, 1) both;
}
.die-smoke-actor[data-smoke-model="ribbon"] {
  aspect-ratio: .48;
  border-width: 1.15px;
  border-radius: 62% 38% 65% 35% / 23% 67% 33% 77%;
  background:
    linear-gradient(151deg,
      transparent 0 13%, rgba(var(--smoke-rgb, 111, 88, 50), .72) 18% 34%,
      rgba(255, 240, 202, .68) 37% 43%,
      rgba(var(--smoke-rgb, 111, 88, 50), .66) 47% 64%, transparent 71%),
    linear-gradient(to top, rgba(var(--smoke-rgb, 111, 88, 50), .34), transparent 76%);
  box-shadow: inset 1px .5px rgba(255, 241, 205, .56);
}
.die-smoke-actor[data-smoke-model="ribbon"]::before,
.die-smoke-actor[data-smoke-model="ribbon"]::after {
  width: 72%; height: 25%; background: transparent;
  border-width: 1.2px 0 1.2px 1.2px;
  border-radius: 72% 0 0 72%;
}
.die-smoke-actor[data-smoke-model="ribbon"]::before {
  left: -28%; top: 31%; transform: rotate(-30deg);
}
.die-smoke-actor[data-smoke-model="ribbon"]::after {
  right: -28%; top: 59%; transform: rotate(150deg);
}
.die-smoke-actor[data-smoke-model="ribbon"].is-active.is-smoke {
  animation: dieSmokeRibbon var(--effect-duration, 3200ms)
    cubic-bezier(.21, .50, .23, 1) both;
}
.die-smoke-actor[data-smoke-model="corkscrew"] {
  aspect-ratio: 1;
  border: 2px solid rgba(var(--smoke-ink-rgb, 74, 51, 27), .82);
  border-left-color: transparent;
  border-bottom-color: rgba(var(--smoke-rgb, 111, 88, 50), .48);
  border-radius: 50%; background: transparent; box-shadow: none;
  filter: drop-shadow(.45px .65px 0 rgba(38, 26, 17, .36));
}
.die-smoke-actor[data-smoke-model="corkscrew"]::before,
.die-smoke-actor[data-smoke-model="corkscrew"]::after {
  background: transparent; border-radius: 50%;
  border: 1.45px solid rgba(var(--smoke-rgb, 111, 88, 50), .84);
  border-left-color: transparent; box-shadow: none;
}
.die-smoke-actor[data-smoke-model="corkscrew"]::before {
  left: 17%; top: 17%; width: 62%; height: 62%; transform: rotate(31deg);
}
.die-smoke-actor[data-smoke-model="corkscrew"]::after {
  left: 35%; top: 35%; width: 27%; height: 27%; transform: rotate(-41deg);
}
.die-smoke-actor[data-smoke-model="corkscrew"].is-active.is-smoke {
  animation: dieSmokeCorkscrew var(--effect-duration, 3200ms)
    cubic-bezier(.17, .49, .25, 1) both;
}
.die-smoke-actor[data-smoke-model="cauldron"] {
  aspect-ratio: 1.78;
  border-width: 1.25px;
  border-radius: 52% 48% 46% 54% / 59% 63% 37% 41%;
  background:
    radial-gradient(circle at 18% 61%, rgba(var(--smoke-rgb, 111, 88, 50), .82) 0 21%, transparent 23%),
    radial-gradient(circle at 47% 36%, rgba(255, 240, 199, .60) 0 13%, rgba(var(--smoke-rgb, 111, 88, 50), .78) 15% 29%, transparent 31%),
    radial-gradient(circle at 78% 58%, rgba(var(--smoke-rgb, 111, 88, 50), .78) 0 23%, transparent 25%),
    linear-gradient(to top, rgba(var(--smoke-rgb, 111, 88, 50), .72), transparent 72%);
}
.die-smoke-actor[data-smoke-model="cauldron"]::before {
  left: -12%; top: 36%; width: 38%; height: 53%; transform: rotate(-11deg);
}
.die-smoke-actor[data-smoke-model="cauldron"]::after {
  right: -11%; top: 27%; width: 40%; height: 56%; transform: rotate(12deg);
}
.die-smoke-actor[data-smoke-model="cauldron"].is-active.is-smoke {
  animation: dieSmokeCauldron var(--effect-duration, 3200ms)
    cubic-bezier(.14, .55, .22, 1) both;
}
.die-spark-actor {
  left: var(--spark-origin-x, 50%); top: var(--spark-origin-y, 50%);
  width: var(--spark-size, 2.2%); aspect-ratio: .34;
  border-radius: 58% 42% 62% 38% / 68% 61% 39% 32%;
  background: linear-gradient(to bottom,
    rgba(var(--spark-hot-rgb, 255, 235, 151), .98) 0 18%,
    rgba(var(--spark-rgb, 222, 170, 48), .98) 38% 68%,
    rgba(var(--spark-rgb, 222, 170, 48), 0) 100%);
  border-left: .8px solid rgba(43, 29, 18, .84);
  border-right: .55px solid rgba(43, 29, 18, .56);
  box-shadow: 0 0 2px rgba(var(--spark-hot-rgb, 255, 235, 151), .90);
  filter: drop-shadow(.35px .45px 0 rgba(36, 24, 14, .60));
}
.die-spark-actor::before {
  content: ""; position: absolute; left: 45%; top: -34%; width: 78%; aspect-ratio: 1;
  border-radius: 50%; transform: translateX(-50%);
  background: rgba(var(--spark-hot-rgb, 255, 235, 151), .96);
  border: .7px solid rgba(45, 29, 15, .72);
}
.die-spark-actor.is-active.is-spark {
  animation: dieSparkFlight var(--effect-duration, 850ms)
    cubic-bezier(.12, .62, .28, 1) both;
}
#diePencilTraces {
  position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1;
  overflow: visible; pointer-events: none;
}
.magic-pencil-trace {
  opacity: 0; fill: none; stroke: #241b14; stroke-width: 2.15;
  stroke-linecap: round; stroke-linejoin: round; mix-blend-mode: multiply;
  filter: drop-shadow(.25px .25px 0 rgba(73, 49, 28, .34));
}
.magic-pencil-trace path {
  stroke-dasharray: 100; stroke-dashoffset: 100;
}
#magicTraceSpring .magic-spring-strand.is-secondary path {
  opacity: .72; stroke: #e5b64a; stroke-width: 1.35;
  filter: drop-shadow(0 0 2px rgba(229, 182, 74, .58));
}
.magic-pencil-trace.is-active { opacity: 1; }
.magic-spring-strand {
  /* Each pencil strand scales around its die-side endpoint. Width remains
     centred; height moves only the palm-side source up/down. */
  transform-box: view-box;
  transform: scale(1, var(--magic-arc-source-scale, 1));
}
body.beast-royal_baboon .magic-spring-strand.is-primary {
  transform-origin: 120px 134px;
  transform: scale(var(--magic-arc-scale, .55), var(--magic-arc-source-scale, 1));
}
body.beast-royal_baboon .magic-spring-strand.is-secondary {
  transform-origin: 122px 137px;
  transform: scale(var(--magic-arc-scale, .55), var(--magic-arc-source-scale, 1));
}
#magicTraceSpring.is-active path {
  animation: springTraceDraw var(--magic-duration, 1650ms) ease-out both;
}
#magicTraceSpring.is-active .magic-spring-strand.is-secondary path { animation-delay: 75ms; }
#dieTilt { position: relative; z-index: 2; }
#dieArt { position: relative; z-index: 2; }
#magicStarGoldLarge.is-active { animation: magicGoldLarge var(--magic-duration, 2400ms) ease-out both; }
#magicStarBlue.is-active { animation: magicBlueStar var(--magic-duration, 2000ms) ease-out both; }
#magicStarRedSmall.is-active { animation: magicRedSmall var(--magic-duration, 1600ms) ease-out both; }
#magicStarGoldEcho.is-active { animation: magicGoldEcho var(--magic-duration, 1600ms) ease-out both; }
#magicStarBlueEcho.is-active { animation: magicBlueEcho var(--magic-duration, 1450ms) ease-out both; }
.beast-rig-live.aura-crest #dieMagicField::after {
  animation: magicPalmResponse var(--magic-crest-duration, 3450ms) ease-out both;
}
.beast-rig-live.is-engaged .die-aura {
  filter: saturate(1.25) brightness(1.18);
}
.beast-rig-live.is-engaged .die-glow-rig {
  filter: brightness(var(--die-glow-brightness)) saturate(1.14);
}

/* The baboon's entrance is one continuous bottom-to-top sentence. The palm
   mark lives outside #dieTurnly, so it can draw while the die itself is still
   absent. Bob, lean and rotation begin only after the forming die settles. */
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"] #dieBob,
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"] #dieTilt {
  animation: none;
  transform: none;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"] #dieSpinner {
  animation-play-state: paused;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"] #dieTurnly {
  opacity: 0;
  transform: translateY(55%) scale(.03);
  transform-origin: 50% 80%;
  animation: none;
  will-change: transform, opacity;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-emerging #dieTurnly {
  animation: dieConjureIn var(--conjure-emerge-duration, 960ms)
    cubic-bezier(.18, .82, .25, 1) both;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-settling #dieTurnly {
  opacity: 1;
  animation: dieConjureSettle var(--conjure-settle-duration, 420ms)
    cubic-bezier(.22, .8, .28, 1) both;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"] #dieMagicRays {
  opacity: 0;
  animation: none;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-emerging #dieMagicRays {
  animation: conjureAuraIn var(--conjure-emerge-duration, 960ms) ease-out both;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-settling #dieMagicRays {
  opacity: .62;
  animation: magicRaysBreathe 4.15s ease-in-out infinite alternate;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="idle"] #dieTurnly,
body.beast-royal_baboon .beast-rig-live[data-magic-phase="reduced"] #dieTurnly {
  opacity: 1;
  transform: none;
}

body.beast-royal_baboon #dieStarField .die-magic-piece.is-active.is-bubble {
  left: calc(50% + var(--bubble-origin-x, 0%));
  top: var(--bubble-origin-y, 58%);
  width: var(--bubble-size, 5.5%);
  animation: magicStarBubble var(--magic-duration, 2200ms)
    cubic-bezier(.18, .72, .3, 1) both;
}
body.beast-royal_baboon .baboon-large-star,
body.beast-royal_baboon .baboon-bubble-star {
  scale: 1;
}
body.beast-royal_baboon .die-star-field .baboon-large-star.is-active.is-scatter {
  left: var(--scatter-start-x, 50%);
  top: var(--scatter-start-y, 50%);
  width: var(--scatter-size, 12%);
  animation: baboonPaintedStarScatter var(--magic-duration, 2300ms)
    cubic-bezier(.18, .76, .28, 1) both;
}
body.beast-royal_baboon .baboon-large-star.is-active.is-classic {
  left: var(--classic-left, 0%);
  top: var(--classic-top, 0%);
  width: var(--classic-size, 10%);
  animation-name: var(--classic-animation, magicGoldLarge), baboonClassicStarOrigin;
  animation-duration: var(--magic-duration, 2200ms), var(--magic-duration, 2200ms);
  animation-timing-function: ease-out, cubic-bezier(.18, .76, .28, 1);
  animation-fill-mode: both, both;
}
body.beast-royal_baboon #dieStarField .baboon-bubble-star.is-active.is-bubble {
  left: calc(50% + var(--bubble-origin-x, 0%));
  top: var(--bubble-origin-y, 65%);
  width: var(--bubble-size, 4%);
  animation: magicStarBubble var(--magic-duration, 2200ms)
    cubic-bezier(.18, .72, .3, 1) both;
}
body.beast-royal_baboon #dieStarField .baboon-bubble-star.is-active.is-bubble.is-stream {
  animation-iteration-count: infinite;
}
body.beast-royal_baboon #dieStarField .die-magic-piece.is-active.is-ignition {
  left: calc(50% + var(--bubble-origin-x, 0%));
  top: var(--bubble-origin-y, 50%);
  width: var(--bubble-size, 6.2%);
  animation: magicIgnitionStar var(--magic-duration, 1150ms)
    cubic-bezier(.2, .78, .3, 1) both;
}

@keyframes baboonPaintedStarScatter {
  0%, 10% {
    left: var(--scatter-start-x, 50%);
    top: var(--scatter-start-y, 50%);
    opacity: 0;
    transform: translate(-50%, -50%)
      rotate(var(--scatter-rotation-start, -10deg)) scale(.28);
  }
  31% {
    left: var(--scatter-end-x, 50%);
    top: var(--scatter-end-y, 50%);
    opacity: var(--scatter-opacity, .96);
    transform: translate(-50%, -50%) rotate(0deg) scale(1.06);
  }
  73% {
    left: var(--scatter-end-x, 50%);
    top: var(--scatter-end-y, 50%);
    opacity: .9;
    transform: translate(-50%, -50%) rotate(3deg) scale(1);
  }
  100% {
    left: calc(var(--scatter-end-x, 50%) + var(--scatter-drift-x, 0%));
    top: calc(var(--scatter-end-y, 50%) + var(--scatter-drift-y, 0%));
    opacity: 0;
    transform: translate(-50%, -50%)
      rotate(var(--scatter-rotation-end, 12deg)) scale(.92);
  }
}

@keyframes baboonClassicStarOrigin {
  0%, 10% {
    left: var(--classic-start-left, var(--classic-left, 0%));
    top: var(--classic-start-top, var(--classic-top, 0%));
  }
  31%, 100% {
    left: var(--classic-left, 0%);
    top: var(--classic-top, 0%);
  }
}

@keyframes dieConjureIn {
  0%, 8% { opacity: 0; transform: translateY(55%) scale(.03); }
  24% { opacity: .25; }
  66% { opacity: .92; transform: translateY(7%) scale(.84); }
  100% { opacity: 1; transform: translateY(0) scale(1.03); }
}
@keyframes dieConjureSettle {
  from { opacity: 1; transform: translateY(0) scale(1.03); }
  to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes conjureAuraIn {
  0%, 55% { opacity: 0; transform: translate(-50%, -50%) scale(.48); }
  78% { opacity: .38; }
  100% { opacity: .62; transform: translate(-50%, -50%) scale(1); }
}
@keyframes magicIgnitionStar {
  0% { opacity: 0; transform: translate(-50%, 20%) rotate(-12deg) scale(.18); }
  24% { opacity: .96; transform: translate(-50%, -4%) rotate(6deg) scale(.82); }
  58% { opacity: .84; transform: translate(-50%, -12%) rotate(-5deg) scale(1.04); }
  100% { opacity: 0; transform: translate(-50%, -34%) rotate(15deg) scale(.72); }
}
@keyframes magicStarBubble {
  0% {
    opacity: 0;
    left: calc(50% + var(--bubble-origin-x, 0%));
    top: var(--bubble-origin-y, 58%);
    transform: translate(-50%, 12%) rotate(var(--bubble-rotation-start, -8deg)) scale(.35);
  }
  15% {
    opacity: .88;
    left: calc(50% + var(--bubble-origin-x, 0%));
    transform: translate(-50%, 0) rotate(0deg) scale(.72);
  }
  58% {
    opacity: .72;
    left: calc(50% + var(--bubble-origin-x, 0%) + var(--bubble-drift-half, 0%));
    transform: translate(-50%, 0) rotate(8deg) scale(.88);
  }
  78% {
    opacity: .62;
    left: calc(50% + var(--bubble-origin-x, 0%) + var(--bubble-drift-field, 0%));
    transform: translate(-50%, 0) rotate(13deg) scale(.96);
  }
  100% {
    opacity: 0;
    left: calc(50% + var(--bubble-origin-x, 0%) + var(--bubble-drift-field, 0%));
    top: calc(var(--bubble-origin-y, 58%) - var(--bubble-rise-field, 39%));
    transform: translate(-50%, 0) rotate(var(--bubble-rotation-end, 18deg)) scale(1);
  }
}
@keyframes dieLivingBob {
  0%   { transform: translate3d(-1.36%, calc(var(--die-bob) * -.28 * var(--die-motion)), 0); }
  19%  { transform: translate3d(.73%,  calc(var(--die-bob) * -.63 * var(--die-motion)), 0); }
  47%  { transform: translate3d(1.64%,  calc(var(--die-bob) *  .18 * var(--die-motion)), 0); }
  69%  { transform: translate3d(-.36%, calc(var(--die-bob) *  .42 * var(--die-motion)), 0); }
  100% { transform: translate3d(-1.36%, calc(var(--die-bob) * -.28 * var(--die-motion)), 0); }
}
@keyframes dieLivingLean {
  0%   { transform: rotate(calc(var(--die-lean) * -.52)); }
  31%  { transform: rotate(calc(var(--die-lean) *  .23)); }
  58%  { transform: rotate(var(--die-lean)); }
  83%  { transform: rotate(calc(var(--die-lean) * -.17)); }
  100% { transform: rotate(calc(var(--die-lean) * -.52)); }
}
@keyframes dieLivingLight {
  0%, 100% { transform: scale(.986); filter: drop-shadow(0 0 2px rgba(255,226,150,.94)) drop-shadow(0 0 8px rgba(237,176,46,.62)); }
  44%      { transform: scale(1.016); filter: drop-shadow(0 0 3px rgba(255,241,188,1)) drop-shadow(0 0 14px rgba(237,176,46,.86)); }
  67%      { transform: scale(1.002); }
}
@keyframes magicRaysBreathe {
  from { opacity: .55; transform: translate(-50%, -50%) rotate(-1.5deg) scale(.94); }
  to   { opacity: .88; transform: translate(-50%, -50%) rotate(1.8deg) scale(1.06); }
}
@keyframes magicPalmResponse {
  0%, 100% { opacity: .12; transform: rotate(-14deg) scale(.82); }
  19%, 61% { opacity: .54; transform: rotate(-14deg) scale(1.08); }
}
@keyframes magicGoldLarge {
  0% { opacity: 0; transform: translate(8%, 10%) rotate(-10deg) scale(.48); }
  14%, 72% { opacity: 1; }
  100% { opacity: 0; transform: translate(-7%, -9%) rotate(8deg) scale(1.08); }
}
@keyframes magicBlueStar {
  0% { opacity: 0; transform: translate(-12%, 8%) rotate(18deg) scale(.42); }
  18%, 68% { opacity: .98; }
  100% { opacity: 0; transform: translate(10%, -13%) rotate(78deg) scale(1.03); }
}
@keyframes magicRedSmall {
  0% { opacity: 0; transform: translate(9%, -12%) rotate(-20deg) scale(.34); }
  20%, 70% { opacity: .96; }
  100% { opacity: 0; transform: translate(-10%, 12%) rotate(32deg) scale(1.12); }
}
@keyframes magicGoldEcho {
  0% { opacity: 0; transform: translate(-12%, 8%) rotate(16deg) scale(.28); }
  22%, 68% { opacity: .90; }
  100% { opacity: 0; transform: translate(12%, -11%) rotate(52deg) scale(1.05); }
}
@keyframes magicBlueEcho {
  0% { opacity: 0; transform: translate(5%, 14%) rotate(-22deg) scale(.30); }
  20%, 65% { opacity: .88; }
  100% { opacity: 0; transform: translate(-8%, -16%) rotate(44deg) scale(1.02); }
}
@keyframes dieSmokeBillow {
  0% {
    opacity: 0;
    left: var(--smoke-origin-x, 50%); top: var(--smoke-origin-y, 31%);
    transform: translate(-50%, -50%) rotate(-8deg) scale(.20);
  }
  14% {
    opacity: var(--smoke-alpha-high, .75);
    transform: translate(-50%, -50%) rotate(-3deg) scale(.68);
  }
  48% {
    opacity: var(--smoke-alpha-mid, .66);
    left: var(--smoke-mid-x, 50%); top: var(--smoke-mid-y, 15%);
    transform: translate(-50%, -50%) rotate(5deg) scale(1.02);
  }
  78% {
    opacity: var(--smoke-alpha-late, .42);
    left: var(--smoke-late-x, 50%); top: var(--smoke-late-y, 4%);
    transform: translate(-50%, -50%) rotate(10deg) scale(1.24);
  }
  100% {
    opacity: 0;
    left: var(--smoke-end-x, 50%); top: var(--smoke-end-y, -9%);
    transform: translate(-50%, -50%) rotate(var(--smoke-turn, 0deg)) scale(1.42);
  }
}
@keyframes dieSmokeWisp {
  0% {
    opacity: 0; left: var(--smoke-origin-x, 50%); top: var(--smoke-origin-y, 31%);
    transform: translate(-50%, -50%) rotate(-16deg) scale(.18, .28);
  }
  17% { opacity: var(--smoke-alpha-high, .75); transform: translate(-50%, -50%) rotate(-8deg) scale(.54, .78); }
  49% {
    opacity: var(--smoke-alpha-mid, .66); left: var(--smoke-mid-x, 50%); top: var(--smoke-mid-y, 15%);
    transform: translate(-50%, -50%) rotate(7deg) scale(.82, 1.18);
  }
  78% {
    opacity: var(--smoke-alpha-late, .42); left: var(--smoke-late-x, 50%); top: var(--smoke-late-y, 4%);
    transform: translate(-50%, -50%) rotate(-5deg) scale(.98, 1.52);
  }
  100% {
    opacity: 0; left: var(--smoke-end-x, 50%); top: var(--smoke-end-y, -9%);
    transform: translate(-50%, -50%) rotate(var(--smoke-turn, 0deg)) scale(1.04, 1.82);
  }
}
@keyframes dieSmokeRibbon {
  0% {
    opacity: 0; left: var(--smoke-origin-x, 50%); top: var(--smoke-origin-y, 31%);
    transform: translate(-50%, -50%) rotate(-24deg) scale(.16, .24) skewY(-8deg);
  }
  16% { opacity: var(--smoke-alpha-high, .75); transform: translate(-50%, -50%) rotate(13deg) scale(.56, .68) skewY(9deg); }
  44% {
    opacity: var(--smoke-alpha-mid, .66); left: var(--smoke-mid-x, 50%); top: var(--smoke-mid-y, 15%);
    transform: translate(-50%, -50%) rotate(-11deg) scale(.86, 1.04) skewY(-6deg);
  }
  76% {
    opacity: var(--smoke-alpha-late, .42); left: var(--smoke-late-x, 50%); top: var(--smoke-late-y, 4%);
    transform: translate(-50%, -50%) rotate(17deg) scale(1.02, 1.32) skewY(8deg);
  }
  100% {
    opacity: 0; left: var(--smoke-end-x, 50%); top: var(--smoke-end-y, -9%);
    transform: translate(-50%, -50%) rotate(var(--smoke-turn, 0deg)) scale(1.08, 1.58) skewY(-4deg);
  }
}
@keyframes dieSmokeCorkscrew {
  0% {
    opacity: 0; left: var(--smoke-origin-x, 50%); top: var(--smoke-origin-y, 31%);
    transform: translate(-50%, -50%) rotate(-95deg) scale(.18);
  }
  15% { opacity: var(--smoke-alpha-high, .75); transform: translate(-50%, -50%) rotate(-28deg) scale(.58); }
  48% {
    opacity: var(--smoke-alpha-mid, .66); left: var(--smoke-mid-x, 50%); top: var(--smoke-mid-y, 15%);
    transform: translate(-50%, -50%) rotate(72deg) scale(.94);
  }
  79% {
    opacity: var(--smoke-alpha-late, .42); left: var(--smoke-late-x, 50%); top: var(--smoke-late-y, 4%);
    transform: translate(-50%, -50%) rotate(181deg) scale(1.20);
  }
  100% {
    opacity: 0; left: var(--smoke-end-x, 50%); top: var(--smoke-end-y, -9%);
    transform: translate(-50%, -50%) rotate(var(--smoke-turn, 270deg)) scale(1.46);
  }
}
@keyframes dieSmokeCauldron {
  0% {
    opacity: 0; left: var(--smoke-origin-x, 50%); top: var(--smoke-origin-y, 31%);
    transform: translate(-50%, -50%) rotate(-4deg) scale(.22, .16);
  }
  14% { opacity: var(--smoke-alpha-high, .75); transform: translate(-50%, -50%) rotate(2deg) scale(.72, .48); }
  48% {
    opacity: var(--smoke-alpha-mid, .66); left: var(--smoke-mid-x, 50%); top: var(--smoke-mid-y, 15%);
    transform: translate(-50%, -50%) rotate(-3deg) scale(1.10, .82);
  }
  77% {
    opacity: var(--smoke-alpha-late, .42); left: var(--smoke-late-x, 50%); top: var(--smoke-late-y, 4%);
    transform: translate(-50%, -50%) rotate(4deg) scale(1.34, 1.03);
  }
  100% {
    opacity: 0; left: var(--smoke-end-x, 50%); top: var(--smoke-end-y, -9%);
    transform: translate(-50%, -50%) rotate(var(--smoke-turn, 0deg)) scale(1.54, 1.18);
  }
}
@keyframes dieSparkFlight {
  0% {
    opacity: 0;
    left: var(--spark-origin-x, 50%); top: var(--spark-origin-y, 50%);
    transform: translate(-50%, -50%) rotate(var(--spark-turn, 0deg)) scale(.18);
  }
  12% {
    opacity: var(--spark-alpha-high, .98);
    transform: translate(-50%, -50%) rotate(var(--spark-turn, 0deg)) scale(.74);
  }
  54% {
    opacity: var(--spark-alpha-mid, .90);
    left: var(--spark-mid-x, 50%); top: var(--spark-mid-y, 35%);
    transform: translate(-50%, -50%) rotate(calc(var(--spark-turn, 0deg) + 16deg)) scale(1);
  }
  100% {
    opacity: 0;
    left: var(--spark-end-x, 50%); top: var(--spark-end-y, 20%);
    transform: translate(-50%, -50%) rotate(calc(var(--spark-turn, 0deg) + 34deg)) scale(.34);
  }
}
@keyframes springTraceDraw {
  0% { opacity: 0; stroke-dashoffset: 100; transform: translateY(-2px); }
  16% { opacity: .64; }
  58% { opacity: .82; stroke-dashoffset: 0; transform: translateY(0); }
  82% { opacity: .68; stroke-dashoffset: 0; transform: translateY(.5px); }
  100% { opacity: 0; stroke-dashoffset: -4; transform: translateY(1px); }
}

@media (prefers-reduced-motion: reduce) {
  body:not(.die-force-motion).die-living #dieBob,
  body:not(.die-force-motion).die-living #dieTilt,
  body:not(.die-force-motion).die-living #dieSpinner,
  body:not(.die-force-motion).die-living #dieArt,
  body:not(.die-force-motion).die-living .die-aura,
  body:not(.die-force-motion) .beast-rig-live .magic-actor.is-active,
  body:not(.die-force-motion) .beast-rig-live #dieMagicRays,
  body:not(.die-force-motion) .beast-rig-live #dieMagicRays::after,
  body:not(.die-force-motion) .beast-rig-live #dieMagicField::after,
  body:not(.die-force-motion) .beast-rig-live .die-glow-halo,
  body:not(.die-force-motion) .beast-rig-live .die-glow-core,
  body:not(.die-force-motion) .beast-rig-live .die-glow-rays,
  body:not(.die-force-motion) .beast-rig-live .die-glow-ray::before {
    animation: none !important;
  }
  body:not(.die-force-motion) .beast-rig-live .beast-face-layer,
  body:not(.die-force-motion) .beast-rig-live .beast-gaze-actor {
    transition: none; transform: none;
  }
}

/* `?reduced` makes the same state inspectable without changing the OS setting. */
body.die-living .beast-rig-live.is-reduced-motion #dieBob,
body.die-living .beast-rig-live.is-reduced-motion #dieTilt,
body.die-living .beast-rig-live.is-reduced-motion #dieSpinner,
body.die-living .beast-rig-live.is-reduced-motion #dieArt,
body.die-living .beast-rig-live.is-reduced-motion .die-aura,
.beast-rig-live.is-reduced-motion .magic-actor.is-active,
.beast-rig-live.is-reduced-motion #dieMagicRays,
.beast-rig-live.is-reduced-motion #dieMagicRays::after,
.beast-rig-live.is-reduced-motion #dieMagicField::after,
.beast-rig-live.is-reduced-motion .die-glow-halo,
.beast-rig-live.is-reduced-motion .die-glow-core,
.beast-rig-live.is-reduced-motion .die-glow-rays,
.beast-rig-live.is-reduced-motion .die-glow-ray::before {
  animation: none !important;
}
.beast-rig-live.is-reduced-motion .beast-face-layer,
.beast-rig-live.is-reduced-motion .beast-gaze-actor {
  transition: none; transform: none;
}

/* --------------------------------------------------------------------------
   THE LEATHER BOARDS (founder, 2026-09-03: "put these on the 2 boards,
   exactly as they are, with thin bezels between the tiles").

   The two board plates from the founder's Photoshop mock — red leather for
   the far board, blue for the near — worn as a nine-slice border, so every
   board size (3x3, 4x3, 4x4) wears the same frame: the corners stay the
   painted corners and the rails repeat their grain. Measured on the plates
   themselves: a frame half a cell wide on the sides and 0.55 of a cell top
   and bottom, round cells of ~95px. The cells are the plate's own
   parchment; the bezel between them is the painted grid line's colour,
   drawn thinner than the mock's 9px (0.055 of a cell, never under 3px), as
   asked. The marble slab, its wells, the chessboard and the rings stand
   down. The column sums, which lived in the slab's margin, now sit on the
   frame's band below (mine) or above (theirs) their column.
   -------------------------------------------------------------------------- */
#s2 .board-frame {
  /* the plate's own proportion is 0.50 / 0.55 of a cell; laid on 30%
     thinner (founder, 2026-09-03: "it looks clumsy this thick") */
  --bd-side: calc(var(--cell) * 0.35);
  --bd-top:  calc(var(--cell) * 0.385);
  --bezel:   max(3px, calc(var(--cell) * 0.105));   /* the red stroke's own width */
  padding: 0; background: none; box-shadow: none; border-radius: 0;
  border: solid; border-width: var(--bd-top) var(--bd-side);
  border-image: url("/static/art/board_red.webp") 52 47 52 46
                / var(--bd-top) var(--bd-side) round;
}
#s2 .board-frame:has(.mine) {
  border-image: url("/static/art/board_blue.webp") 47 50 48 50
                / var(--bd-top) var(--bd-side) round;
}
#s2 .board-frame .board { gap: var(--bezel); padding: 0; margin: 0; }
#s2 .board-frame .col { gap: 0; border-radius: 0; position: relative; }
#s2 .board-frame .cells { gap: var(--bezel); }
#s2 .board-frame .cell {
  border-radius: 0; box-shadow: none;
  background: url("/static/art/cell_red.webp") center / cover;
}
#s2 .board-frame:has(.mine) .cell {
  background-image: url("/static/art/cell_blue.webp");
}
/* the sums leave the flow and stand on the leather band */
#s2 .board-frame .colsum {
  position: absolute; left: 0; right: 0; margin: 0; padding: 0;
  bottom: calc(-1 * var(--bd-top)); height: var(--bd-top);
  line-height: var(--bd-top); min-height: 0;
  font-size: calc(var(--bd-top) * 0.5);
  color: #f2dfae; text-shadow: 0 1px 1px rgba(0, 0, 0, .6);
}
#s2 .board-frame:has(.theirs) .colsum {
  bottom: auto; top: calc(-1 * var(--bd-top));
}
#s2 .board-frame .col.cemented .colsum { color: #fff3cf; }

/* The plaques straddle the LEATHER, not the cells. Their anchor is the
   frame's padding box, which the marble slab padded by 10px and the
   nine-slice border pads by nothing — so "20px in from the edge" landed
   20px over the first column. Measured from the frame's outer edge
   instead: 20px onto the leather, and never onto a cell at any size. */
#s2 .board-frame {
  /* how far onto the leather the plaque reaches: 20px on a thin frame,
     and most of the band on a thick one, so a 74px cell's 37px frame
     does not push the plaques off a 430px screen */
  --straddle: max(20px, calc(var(--bd-side) * 0.85));
}
#s2 .board-frame:has(.mine) > .total {
  right: calc(100% + var(--bd-side) - var(--straddle));
}
#s2 .board-frame:has(.theirs) > .total {
  left: calc(100% + var(--bd-side) - var(--straddle));
}

/* THE MANUSCRIPT RULES (founder, 2026-09-03: "take the exact lines I used
   for the drawings"). The grid lines are the plates' own painted strokes,
   cut whole — a single brown stroke on the red plate, a gold-cored double
   rule on the blue — and laid into the gaps. Each column carries the
   vertical stroke down its right-hand gap for its full height; each cell
   carries the horizontal stroke across the gap beneath it. The horizontal
   strokes stop at the column's edge, so where they meet the vertical one
   the crossing is the vertical stroke running through, as in the
   painting. Tiled at their painted aspect rather than stretched, so the
   stroke keeps its grain at any cell size. The gap is the stroke's own
   width on its plate, never under 3px. */
#s2 .board-frame:has(.mine) {
  --bezel: max(3px, calc(var(--cell) * 0.124));   /* the double rule is wider */
}
#s2 .board-frame .col::after, #s2 .board-frame .cell::after {
  content: ""; position: absolute; pointer-events: none;
}
#s2 .board-frame .col::after {
  top: 0; bottom: 0; left: 100%; width: var(--bezel);
  background: url("/static/art/line_red_v.webp") center top / 100% auto repeat-y;
}
#s2 .board-frame .cell::after {
  left: 0; right: 0; top: 100%; height: var(--bezel);
  background: url("/static/art/line_red_h.webp") left center / auto 100% repeat-x;
}
#s2 .board-frame:has(.mine) .col::after {
  background-image: url("/static/art/line_blue_v.webp");
}
#s2 .board-frame:has(.mine) .cell::after {
  background-image: url("/static/art/line_blue_h.webp");
}
/* the frame's inner edge already carries its own rule */
#s2 .board-frame .col:last-child::after,
#s2 .board-frame .cell:last-child::after { display: none; }

/* --------------------------------------------------------------------------
   THE BOCCA CONFIGURATION (founder's second mock, 2026-09-03).

   The boards stand off-centre to the right, leaving a column on the left
   for the two PROFILE CONSTRUCTS: a medallion holding the portrait with the
   level tag at its foot, the name on a scroll, the score in a shield --
   stacked for the far player, and mirrored for the near one so the shield
   (an arch, point up) comes first and the medallion last. Between the
   boards, bridging them, the BOCCA DELLA VERITA: the dice are born tiny in
   its mouth and roll down its tongue, growing, to land where the offer has
   always sat. Every measure is the mock's own, in fractions of the profile
   column's width (--pfw, the scroll's width, 26.8% of the mock) and of the
   bocca's width (--bocca, 25.1%).
   -------------------------------------------------------------------------- */
#s2 {
  --pfw:   min(27vw, 118px);
  --bocca: min(25vw, 110px);
}
body:has(#s2.on) #s2 { padding-left: calc(var(--pfw) + 10px); }
/* the lines under the near board keep the whole width: they are not the
   boards' business, and a narrowed purse wraps into two */
#s2 > #guide, #s2 > #talkStrip, #s2 > #barPurse {
  margin-left: calc(-1 * (var(--pfw) + 10px));
}
#s2 > #barPurse { width: auto; align-self: stretch; }

/* THE CONSTRUCTS -- the old plaque card stands down entirely */
#s2 .board-frame > .total {
  top: auto; left: auto; right: calc(100% + 4px); transform: none;
  width: var(--pfw); height: calc(var(--pfw) * 1.9);
  display: block; padding: 0; margin: 0; border: 0;
  background: none; box-shadow: none; min-width: 0;
}
/* both :has() rules restate left/right/top: the straddle rules above carry
   the same :has() weight and would otherwise keep their say. Offsets are
   from the leather's OUTER edge -- the containing block is the padding box,
   which now begins where the cells do, so the border is added back. */
#s2 .board-frame:has(.theirs) > .total,
#s2 .board-frame:has(.mine)   > .total {
  left: auto; right: calc(100% + var(--bd-side) + 4px);
}
#s2 .board-frame:has(.theirs) > .total { top: calc(3% - var(--bd-top)); }
#s2 .board-frame:has(.mine)   > .total { top: calc(-7% - var(--bd-top)); }
#s2 .total .pf-medal, #s2 .total .pf-level, #s2 .total .pf-scroll, #s2 .total .pf-shield {
  position: absolute; left: 50%; transform: translateX(-50%);
  background: center / 100% 100% no-repeat;
}
#s2 .total .pf-medal  { width: 87%;  height: calc(var(--pfw) * 1.01); z-index: 1;
                        background-image: url("/static/art/pf_medallion.webp"); }
#s2 .total .pf-level  { width: 29%;  height: calc(var(--pfw) * .27);  z-index: 4;
                        background-image: url("/static/art/pf_level.webp"); }
#s2 .total .pf-scroll { width: 100%; height: calc(var(--pfw) * .41);  z-index: 3;
                        background-image: url("/static/art/pf_scroll.webp"); }
#s2 .total .pf-shield { width: 62%;  height: calc(var(--pfw) * .755); z-index: 1;
                        background-image: url("/static/art/pf_shield.webp"); }
/* the far player's stack, top to bottom: medallion, tag at its foot, scroll, shield */
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-medal  { top: 0; }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-level  { top: calc(var(--pfw) * .76); }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { top: calc(var(--pfw) * .84); }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-shield { top: calc(var(--pfw) * 1.11); }
/* the near player's, mirrored: arch, scroll, medallion, tag at its foot */
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield { top: 0; background-image: url("/static/art/pf_arch.webp"); }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: calc(var(--pfw) * .625); }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-medal  { top: calc(var(--pfw) * .865); }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-level  { top: calc(var(--pfw) * 1.625); }

/* what is written on them: the portrait in the oval, the name on the
   scroll, the score in the shield, the level in the tag */
#s2 .total .pf-medal .mark {
  position: absolute; inset: 8% 10% 10% 10%; width: auto; height: auto;
  margin: 0; border: 0; border-radius: 50%; object-fit: cover;
  background: none; box-shadow: none;
}
#s2 .total .pf-scroll .who {
  position: absolute; inset: 0; padding: 0 13%;
  display: flex; align-items: center; justify-content: center;
  font: 700 calc(var(--pfw) * .10) / 1 var(--serif); letter-spacing: .02em;
  color: #1c1208; opacity: 1; max-width: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#s2 .total .pf-shield .n {
  position: absolute; inset: 0 0 16% 0;
  display: flex; align-items: center; justify-content: center;
  font: calc(var(--pfw) * .30) / 1 var(--serif); margin: 0;
  color: #f3e6c2; text-shadow: 0 1px 2px rgba(0, 0, 0, .75);
}
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield .n { inset: 16% 0 0 0; }
#s2 .total .pf-level .lv {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font: 700 calc(var(--pfw) * .12) / 1 var(--serif); color: #e9c45a;
  text-shadow: 0 1px 1px rgba(0, 0, 0, .7);
}
#s2 .total .pf-level .lv:empty::before { content: "\2013"; opacity: .55; }

/* THE BOCCA, bridging the boards */
#strip { position: relative; z-index: 3; }
#offerStage {
  position: relative; min-height: calc(var(--bocca) * .83);
  display: block; overflow: visible; width: 100%;
}
#offerStage::before {
  content: ""; position: absolute; pointer-events: none; z-index: 2;
  left: 50%; transform: translateX(-50%);
  top: calc(var(--bocca) * -.14);
  width: var(--bocca); height: calc(var(--bocca) * 1.079);
  background: url("/static/art/bocca.webp") center / contain no-repeat;
}
/* the offer lands on the tongue -- its upper third, just under the lips
   (founder: "settle a bit higher") */
#offerArea {
  position: absolute; z-index: 4; min-height: 0;
  left: 50%; top: calc(var(--bocca) * .66);
  transform: translate(-50%, -50%);
}
/* the slot halves are buttons and wore the page's button shadow: that was
   the pale box behind the dice */
#slotPicker .slotHalf { box-shadow: none; background: none; }
/* the line that speaks over the dice stands to the left of the mask */
#banner {
  position: absolute; z-index: 4; margin: 0;
  right: calc(50% + var(--bocca) * .56); top: 50%; transform: translateY(-50%);
  text-align: right; white-space: nowrap; max-width: none;
}
#simonyFuse { z-index: 5; }
#bribeTimer { z-index: 5; }

/* the roll-out: born small and high in the mouth, down the tongue, full
   size on landing; the tumble underneath is untouched */
#offerArea .die3d { transform-origin: 50% 50%; }
#offerArea .die3d.in-mouth {
  transform: translateY(calc(var(--bocca) * -.13)) scale(.12);
}
#offerArea .die3d.spat {
  animation: bocca-spit 1700ms linear both;   /* the easing lives per movement */
}
/* three movements: out through the lips, slowly; down the length of the
   tongue, growing all the way; and the small hop up to land. Each frame
   names the easing that carries it to the next, so the whole reads as one
   motion rather than three. */
/* one even motion (founder, 2026-09-03: "smooth, moderate speed"): out of
   the dark of the mouth, over the lip, the length of the tongue, the drop
   off its end and the rise to rest -- each frame handing to the next with
   matched slopes so nothing kicks */
@keyframes bocca-spit {
  from { transform: translateY(calc(var(--bocca) * -.13)) scale(.12); opacity: .0;
         animation-timing-function: cubic-bezier(.4, 0, .7, .5); }
  8%   { opacity: 1; }
  28%  { transform: translateY(calc(var(--bocca) * -.06)) scale(.34);
         animation-timing-function: cubic-bezier(.35, .1, .65, .9); }
  62%  { transform: translateY(calc(var(--bocca) * .14)) scale(.86);
         animation-timing-function: cubic-bezier(.33, .2, .6, 1); }
  78%  { transform: translateY(calc(var(--bocca) * .21)) scale(.98);
         animation-timing-function: cubic-bezier(.3, .4, .4, 1); }
  to   { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  #offerArea .die3d.in-mouth { transform: none; }
  #offerArea .die3d.spat { animation: none; }
}

/* --------------------------------------------------------------------------
   THE CHAMPION SPEAKS FROM HIS MEDALLION (founder, 2026-09-03). A bubble
   hung on the far portrait, dressed as the rest of the table: parchment,
   a brown-gold rule, a tail toward the face. It is out of the flow, so
   the boards never move for it; it pops in, and it goes on a tap or in
   its own time. The band that used to carry the line stands down at a
   champion's table.
   -------------------------------------------------------------------------- */
.mask-mode #talkStrip { display: none; }
#s2 .pf-medal .speech {
  position: absolute; z-index: 20;
  left: 94%; top: 8%;
  width: max-content; max-width: min(58vw, 230px);
  margin: 0; padding: 7px 10px 8px;
  font: italic 12.5px / 1.3 var(--serif); color: #2a1b0c;
  text-align: left; white-space: normal; letter-spacing: 0;
  background: #f5ecd6; border: 1.5px solid #8a6a2c; border-radius: 7px;
  box-shadow: 0 2px 6px rgba(40, 26, 8, .32), inset 0 0 0 1px rgba(255, 250, 235, .7);
  transform-origin: 0 18px;
  animation: speech-pop 200ms ease-out both;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* the tail: a square turned on its corner, wearing the same two edges */
#s2 .pf-medal .speech::before {
  content: ""; position: absolute; left: -7px; top: 12px;
  width: 11px; height: 11px; background: #f5ecd6;
  border-left: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(45deg);
}
#s2 .pf-medal .speech.gone { animation: speech-fade 240ms ease-in both; pointer-events: none; }
@keyframes speech-pop  { from { opacity: 0; transform: scale(.72); } to { opacity: 1; transform: none; } }
@keyframes speech-fade { to { opacity: 0; transform: translateY(-4px); } }
@media (prefers-reduced-motion: reduce) {
  #s2 .pf-medal .speech { animation: none; }
  #s2 .pf-medal .speech.gone { animation: none; opacity: 0; }
}

/* --------------------------------------------------------------------------
   THE SKINS MENU (founder, 2026-09-03). In the dials' own dress -- dark
   leather, a gold rule, gold lettering -- a tab at the table's foot on the
   right, and a panel of swatches: each the material itself with its pips,
   named. Two rows of choice, board face and dice face. The chosen board
   face lies under the leather and the painted rules on both boards.
   -------------------------------------------------------------------------- */
#skinTab {
  position: fixed; right: 8px; bottom: 8px; z-index: 9990;
  min-height: 30px; padding: 0 14px;
  font: 700 11px/1 var(--sans); letter-spacing: .14em;
  background: rgba(28, 19, 7, .92); color: var(--gold);
  border: 1px solid rgba(201, 162, 39, .5); border-radius: 5px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, .45);
}
#skinTab[hidden] { display: none; }
body:not(:has(#s2.on)) #skinTab { display: none; }        /* the table's, only */
body:has(#s2.on) #skinTab, body:has(#s2.on) #skins {
  bottom: calc(66px + 12px + env(safe-area-inset-bottom, 0px));
}
#skins {
  position: fixed; left: 8px; right: 8px; bottom: 8px; z-index: 9991;
  background: rgba(28, 19, 7, .95); color: #f0dfae;
  border: 1px solid rgba(201, 162, 39, .5); border-radius: 8px;
  padding: 10px 12px 12px; font: 12px/1.35 var(--sans);
  max-height: 62vh; overflow-y: auto; overscroll-behavior: contain;
  box-shadow: 0 6px 26px rgba(0, 0, 0, .5);
}
#skins[hidden] { display: none; }
#skins h4 {
  font: 700 11px/1 var(--sans); letter-spacing: .16em;
  text-transform: uppercase; color: var(--gold); margin: 10px 0 8px;
}
#skins .skins-head { display: flex; align-items: center; justify-content: space-between; }
#skins .skins-head h4 { margin: 0; }
#skinsClose {
  min-height: 26px; padding: 0 10px; font: 700 10px/1 var(--sans); letter-spacing: .14em;
  color: var(--gold); background: none; border: 1px solid rgba(201, 162, 39, .5);
  border-radius: 4px; box-shadow: none;
}
#skins .sw-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(66px, 1fr));
  gap: 8px 6px;
}
#skins .sw {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 0; margin: 0; min-height: 0; background: none; border: 0; box-shadow: none;
  color: #f0dfae; cursor: pointer; -webkit-tap-highlight-color: transparent;
}
/* the material, with the five of a die drilled in its own pip colour */
#skins .sw .face {
  width: 56px; height: 56px; border-radius: 6px;
  border: 2px solid rgba(201, 162, 39, .28);
  background:
    radial-gradient(circle at 26% 26%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 74% 26%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 50% 50%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 26% 74%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 74% 74%, var(--pip) 0 4.5px, transparent 5px),
    var(--face) center / cover, #cdbf9f;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .25), 0 2px 5px rgba(0, 0, 0, .45);
}
#skins .sw[data-slug=""] .face {
  background:
    radial-gradient(circle at 26% 26%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 74% 26%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 50% 50%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 26% 74%, var(--pip) 0 4.5px, transparent 5px),
    radial-gradient(circle at 74% 74%, var(--pip) 0 4.5px, transparent 5px),
    url("/static/art/cell_blue.webp") center / cover;
}
#skins .sw .name {
  font: 9px/1.1 var(--sans); letter-spacing: .04em; text-align: center;
  opacity: .8; max-width: 66px;
}
#skins .sw.chosen .face { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(201, 162, 39, .55), 0 2px 5px rgba(0, 0, 0, .45); }
#skins .sw.chosen .name { opacity: 1; color: var(--gold); }

/* the chosen board face lies in every cell of both boards */
#s2.skinned .board-frame .cell,
#s2.skinned .board-frame:has(.mine) .cell { background-image: var(--cell-face); }

/* THE TABS RIDE IN THE BAR (founder, 2026-09-03). In the bar they are bar
   buttons: the gear's size, the bar's dress, after the gear. The floating
   rules they carry elsewhere are outranked here on purpose. */
/* SKINS lives in the OPTIONS column now (2026-09-14): the floating tab's
   dress is outranked here, and it takes the column's */
body:has(#s2.on) #s2 #bar #skinTab {
  position: static; left: auto; right: auto; bottom: auto; z-index: auto;
  width: 100%; height: 50px; min-height: 0; padding: 0 4px; margin: 0;
  color: #e6d2a0; background: rgba(255, 250, 235, .06);
  border: 1px solid rgba(201, 162, 39, .30); border-radius: 5px;
  box-shadow: none;
}
body:has(#s2.on) #s2 #bar #skinTab[hidden] { display: none; }
/* FOCUS stands beside OPTIONS on the bar (2026-09-14), in the bar's dress */
body:has(#s2.on) #s2 #bar > #focusTab {
  position: static; z-index: auto; order: 1; flex: 1 1 0; width: auto; height: 52px; min-height: 0;
  padding: 0 4px; margin: 0;
  color: #e6d2a0; background: rgba(255, 250, 235, .06);
  border: 1px solid rgba(201, 162, 39, .30); border-radius: 5px;
  box-shadow: none;
}
body:has(#s2.on) #s2 #bar > #focusTab.on {
  color: #2a1d06; background: var(--leaf); border-color: #6b5512;
  box-shadow: 0 0 10px rgba(201, 162, 39, .30), inset 0 1px 0 rgba(255, 250, 220, .7);
}
body:has(#s2.on) #s2 #bar > #focusTab.half {
  color: #2a1d06; background: linear-gradient(180deg, rgba(201, 162, 39, .55), rgba(150, 118, 30, .55)); border-color: #6b5512;
}
body:not(:has(#s2.on)) #focusTab { display: none; }        /* the table's, only */
body:not(:has(#s2.on)) #skinTab { display: none; }

/* ONE SYSTEM LINE (founder, 2026-09-03). The single line the player reads,
   beside the mask, in the banner's own voice; the guide under the near
   board and the banner itself stand down, so the boards gain the row. */
#sysLine {
  position: absolute; z-index: 4; margin: 0;
  top: 50%; transform: translateY(-50%);
  /* centred in the whole space left of the mask (founder, 2026-09-03): the
     box runs from the page's edge -- back across the profile column's
     padding -- to the mask's lip, and the words sit in its middle */
  left: calc(-1 * (var(--pfw) + 10px)); right: calc(50% + var(--bocca) * .52);
  padding: 0 6px; text-align: center; white-space: normal;
  font-family: var(--serif); font-size: 11.5px; line-height: 1.25; letter-spacing: .14em;
  color: var(--vermilion); text-transform: uppercase;
  text-shadow: 0 1px 0 rgba(255, 250, 235, .7);
}
#sysLine:empty { display: none; }
#sysLine.shimmer { animation: shimmer 1.1s infinite; }
/* ===========================================================================
   THE LINE FADES (founder, 2026-09-15: "just do fade out fade in, no
   typewriter. We are in illuminated manuscript territory"). An ink wipe
   stood here for an hour and read as a hand typing; on a leaf like this
   one the words are either written or they are not. So: the old words go,
   the new words come, and nothing crosses the parchment.
   =========================================================================== */
#sysLine .ink { display: inline-block; max-width: 100%; }
#sysLine .ink.showing { animation: ink-in 300ms ease-out both; }
#sysLine .ink.fading  { animation: ink-out 160ms ease-in both; }
@keyframes ink-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes ink-out { from { opacity: 1; } to { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  #sysLine .ink.showing, #sysLine .ink.fading { animation: none; }
}
#s2 #banner, #s2 > #guide { display: none; }

/* THE RIG'S DIALS BELONG TO THE FRONT PAGE AND NOWHERE ELSE (founder,
   2026-09-03: "we dont need those settings on the game boards page"). */
body:not(:has(#s0.on)) #dieDialsTab, body:not(:has(#s0.on)) #dieDials { display: none; }

/* THE PROFILE, REFINED (founder, 2026-09-03: "clunky and ugly"). The name
   sits on the scroll's FACE -- measured on the clean plate, the face runs
   from 29% to 91% of the height and 7% to 98% of the width, the curls
   taking the rest -- in a blackletter when one is served, Title Case, a
   size that reads on a phone. For the far player the scroll lies over the
   level tag, not under it. */
@font-face {
  font-family: "DADI Scroll";
  src: url("/static/fonts/scroll.woff2") format("woff2");
  font-display: swap;
}
#s2 .total .pf-scroll .who {
  inset: 29% 3% 9% 8%; padding: 0 4%;
  font: 600 calc(var(--pfw) * .135) / 1 "DADI Scroll", "Old English Text MT",
        "UnifrakturMaguntia", var(--serif);
  letter-spacing: 0; text-transform: none; color: #1e1408;
}
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-level { z-index: 2; }   /* under the scroll */

/* THE FAR BOARD IS NOT DIMMED (founder, 2026-09-03: "the opponents dice on
   the board are weirdly discolored"). The inert rule faded the whole far
   board to 72% and drained its colour -- leather, rules and dice alike --
   which on the marble slab read as "not yours" and on leather reads as
   rot. The leather already says whose board it is, and the page refuses
   taps on it regardless; the dice keep their true stone. */
#s2 .board.inert { opacity: 1; filter: none; }

/* the painted stones' swatches carry the real painted face */
#skins .sw-orig .face { background: none; padding: 0; overflow: hidden; }
#skins .sw-orig .face .die {
  width: 100%; height: 100%; border-radius: 4px; box-shadow: none; margin: 0;
}

/* --------------------------------------------------------------------------
   THE SKINS MENU, SECOND FORM (founder, 2026-09-03): four drop-downs --
   far board, near board, far dice, near dice -- each a control showing its
   current tile and name, opening its own list. Every tile is the real
   thing: a die face painted from the material with the die's own bevel,
   or the cell face itself. The cells of each board wear their own choice.
   -------------------------------------------------------------------------- */
#skins .pick, #settingsPop .pick { margin: 8px 0 0; }
#skins .pick-label, #settingsPop .pick-label {
  display: block; font: 700 9.5px/1 var(--sans); letter-spacing: .16em;
  text-transform: uppercase; color: var(--gold); opacity: .85; margin: 0 0 4px 2px;
}
#skins .pick-head, #settingsPop .pick-head {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 0; padding: 5px 8px 5px 5px; margin: 0;
  font: 12.5px/1 var(--serif); color: #f0dfae; text-align: left;
  background: rgba(255, 250, 235, .05); border: 1px solid rgba(201, 162, 39, .38);
  border-radius: 6px; box-shadow: inset 0 1px 0 rgba(255, 250, 235, .06);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
#skins .pick.open .pick-head, #settingsPop .pick.open .pick-head { border-color: rgba(232, 195, 83, .7); }
#skins .pick-name, #settingsPop .pick-name { flex: 1; text-transform: capitalize; }
#skins .pick-caret, #settingsPop .pick-caret { color: var(--gold); font-size: 12px; opacity: .8; }
#skins .pick-list, #settingsPop .pick-list {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
  gap: 8px 6px; margin: 6px 0 2px; padding: 8px 6px;
  max-height: 38vh; overflow-y: auto; overscroll-behavior: contain;
  background: rgba(0, 0, 0, .25); border: 1px solid rgba(201, 162, 39, .22);
  border-radius: 6px;
}
#skins .pick-list[hidden], #settingsPop .pick-list[hidden] { display: none; }
#skins .opt, #settingsPop .opt {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 3px 0; margin: 0; min-height: 0; background: none; border: 0;
  border-radius: 6px; box-shadow: none; color: #f0dfae; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#skins .opt .name, #settingsPop .opt .name {
  font: 9px/1.1 var(--sans); letter-spacing: .03em; text-align: center;
  opacity: .8; max-width: 64px; text-transform: capitalize;
}
#skins .opt.chosen .name, #settingsPop .opt.chosen .name { opacity: 1; color: var(--gold); }
#skins .opt.chosen .tile, #settingsPop .opt.chosen .tile { outline: 2px solid var(--gold); outline-offset: 2px; }
/* the tiles: a die face with the die's own bevel, or a cell face */
#skins .tile, #settingsPop .tile {
  display: block; width: 56px; height: 56px; flex: 0 0 auto;
  border-radius: 6px; background: rgba(0, 0, 0, .35);
}
#skins .pick-head .tile, #settingsPop .pick-head .tile { width: 34px; height: 34px; border-radius: 5px; }
#skins .tile .die, #settingsPop .tile .die {
  width: 100%; height: 100%; margin: 0; border-radius: inherit;
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, .22),
    inset 0 -3px 5px rgba(0, 0, 0, .30),
    inset 0 0 0 1px rgba(50, 34, 16, .55),
    0 2px 4px rgba(0, 0, 0, .55);
}
#skins .cell-tile, #settingsPop .cell-tile {
  background-size: cover; background-position: center;
  box-shadow: inset 0 0 0 1px rgba(60, 40, 15, .5), inset 0 1px 0 rgba(255, 250, 235, .18),
              0 2px 4px rgba(0, 0, 0, .55);
}
/* each board wears its own face */
#s2 .board-frame:has(.theirs) .cell {
  background-image: var(--cell-face-far, url("/static/art/cell_red.webp"));
}
#s2 .board-frame:has(.mine) .cell {
  background-image: var(--cell-face-near, url("/static/art/cell_blue.webp"));
}
/* the heart of the board wears the motif; when a board is unskinned the
   motif variable is empty and the heart stays plain parchment */
#s2 .board-frame:has(.theirs) .cell.motif {
  background-image: var(--cell-motif-far, var(--cell-face-far, url("/static/art/cell_red.webp")));
}
#s2 .board-frame:has(.mine) .cell.motif {
  background-image: var(--cell-motif-near, var(--cell-face-near, url("/static/art/cell_blue.webp")));
}
/* EACH POSTER RULES ITS BOARD IN ITS OWN METAL (founder, 2026-09-03). The
   strokes between the cells and their width follow the board's face; a
   board with no face, or a face whose strokes are not cut yet, keeps the
   leather's painted rules. */
#s2 .board-frame:has(.theirs) {
  --bezel: max(3px, calc(var(--cell) * var(--rule-w-far, 0.105)));
}
#s2 .board-frame:has(.mine) {
  --bezel: max(3px, calc(var(--cell) * var(--rule-w-near, 0.124)));
}
#s2 .board-frame:has(.theirs) .col::after {
  background-image: var(--rule-v-far, url("/static/art/line_red_v.webp"));
}
#s2 .board-frame:has(.theirs) .cell::after {
  background-image: var(--rule-h-far, url("/static/art/line_red_h.webp"));
}
#s2 .board-frame:has(.mine) .col::after {
  background-image: var(--rule-v-near, url("/static/art/line_blue_v.webp"));
}
#s2 .board-frame:has(.mine) .cell::after {
  background-image: var(--rule-h-near, url("/static/art/line_blue_h.webp"));
}
/* the lists' names on one line, and room between the rows */
#skins .pick-list, #settingsPop .pick-list { gap: 12px 6px; }
#skins .opt .name, #settingsPop .opt .name {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 66px;
}
#skins .opt, #settingsPop .opt { min-height: 78px; justify-content: flex-start; }
/* the strokes are tiled to the CELL PITCH, one cell and one gap per tile,
   so the bosses and finials at each strip's ends land on the crossings
   as they do in the posters, at every cell size */
#s2 .board-frame .col::after {
  background-size: 100% calc(var(--cell) + var(--bezel));
  background-position: center top; background-repeat: repeat-y;
}
#s2 .board-frame .cell::after {
  background-size: calc(var(--cell) + var(--bezel)) 100%;
  background-position: left center; background-repeat: repeat-x;
}

/* SIX BOARDS, RIM AND ALL (founder, 2026-09-03). A chosen board wears its
   poster's plate as its rim -- the same nine-slice the leather wears, the
   slices read from the manifest in the plate's own pixels -- with the
   material's plain variants in its cells, the motif at the heart, and its
   own grid strokes. With no choice, the leather. */
#s2 .board-frame:has(.theirs) {
  border-image-source: var(--board-img-far, url("/static/art/board_red.webp"));
  border-image-slice: var(--board-slice-far, 52 47 52 46);
}
#s2 .board-frame:has(.mine) {
  border-image-source: var(--board-img-near, url("/static/art/board_blue.webp"));
  border-image-slice: var(--board-slice-near, 47 50 48 50);
}
/* the board option's tile is the plate itself, whole */
#skins .plate-tile { background-size: contain; background-repeat: no-repeat; background-color: rgba(0, 0, 0, .35); }
/* THE MOTIF, FINE AND DELICATE, ON A CHECKERBOARD (founder, 2026-09-03).
   No longer the bold stamp at the heart: on alternating cells the poster's
   motif is laid over the cell's own stone as a faint engraving, blended so
   the material still shows through it. The earlier heart rules are
   overridden here; a board with no material has no motif to lay. */
#s2 .board-frame:has(.theirs) .cell.motif {
  background-image: var(--cell-face-far, url("/static/art/cell_red.webp"));
}
#s2 .board-frame:has(.mine) .cell.motif {
  background-image: var(--cell-face-near, url("/static/art/cell_blue.webp"));
}
#s2 .board-frame .cell.motif::before {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: var(--cell-motif-none, none) center / cover no-repeat;
  opacity: .8; mix-blend-mode: overlay;
}
#s2 .board-frame:has(.theirs) .cell.motif::before { background-image: var(--cell-motif-far, none); }
#s2 .board-frame:has(.mine) .cell.motif::before { background-image: var(--cell-motif-near, none); }
/* the ornament layer blends plainly once it is a true transparent cut;
   the baked centre cut keeps the overlay trick until then */
#s2 .board-frame:has(.theirs) .cell.motif::before {
  mix-blend-mode: var(--motif-blend-far, overlay); opacity: var(--motif-alpha-far, .8);
}
#s2 .board-frame:has(.mine) .cell.motif::before {
  mix-blend-mode: var(--motif-blend-near, overlay); opacity: var(--motif-alpha-near, .8);
}
/* EACH SIDE OF THE RIM AS THE PLATE DRAWS IT (founder's leather mats,
   2026-09-03): the top keeps the leather's 0.385 of a cell, and the other
   three sides follow their own slice's proportion to it -- the rolled
   left edge of a mat comes out at its full girth. The plaques anchor past
   whatever the left rim is. */
#s2 .board-frame:has(.theirs) {
  --bd-left: calc(var(--bd-top) * var(--rim-l-far, .909));
  border-width: var(--bd-top) calc(var(--bd-top) * var(--rim-r-far, .909))
                calc(var(--bd-top) * var(--rim-b-far, 1)) var(--bd-left);
  border-image-width: 1;
}
#s2 .board-frame:has(.mine) {
  --bd-left: calc(var(--bd-top) * var(--rim-l-near, .909));
  border-width: var(--bd-top) calc(var(--bd-top) * var(--rim-r-near, .909))
                calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
  border-image-width: 1;
}
#s2 .board-frame:has(.theirs) > .total,
#s2 .board-frame:has(.mine)   > .total { right: calc(100% + var(--bd-left) + 4px); }
/* RAILS THAT RUN, AND ROSETTES AT THE CROSSINGS (founder, 2026-09-03: the
   separators appeared broken). The strips are the rails' straight middle
   and tile at their own aspect, so a rail runs unbroken from rim to rim;
   the crossing rosette is its own piece, set over the rails on every
   inner intersection. And the roll: the rim's sides STRETCH rather than
   repeat, so the leather never restarts mid-roll. */
#s2 .board-frame { border-image-repeat: round stretch; }
#s2 .board-frame .col::after  { background-size: 100% auto; background-position: center top; background-repeat: repeat-y; }
#s2 .board-frame .cell::after { background-size: auto 100%; background-position: left center; background-repeat: repeat-x; }
#s2 .board-frame .cell .cross {
  position: absolute; z-index: 2; pointer-events: none;
  left: calc(100% + var(--bezel) / 2); top: calc(100% + var(--bezel) / 2);
  width: calc(var(--cell) * var(--cross-w, 0)); height: calc(var(--cell) * var(--cross-w, 0));
  transform: translate(-50%, -50%);
  background: var(--cross-img, none) center / 100% 100% no-repeat;
}
#s2 .board-frame:has(.theirs) .cell .cross { --cross-img: var(--cross-far, none); --cross-w: var(--cross-w-far, 0); }
#s2 .board-frame:has(.mine)   .cell .cross { --cross-img: var(--cross-near, none); --cross-w: var(--cross-w-near, 0); }
/* WHOLE PIXELS AT THE CROSSINGS, AND THE NEAR ROLL WHOLE (founder,
   2026-09-03). The gap between cells rounds to an even number of pixels,
   so the rail's centre sits on a pixel; the rosette is sized from the
   gap's own scale and rounded the same way, so its rail core matches the
   strip's to the pixel and its centre lands on the same one. And both
   frames say their repeat mode themselves: rails round, sides stretch --
   the near plate's shorthand had kept "round" for its roll. */
#s2 .board-frame:has(.theirs) {
  --bezel: round(nearest, max(3px, calc(var(--cell) * var(--rule-w-far, 0.105))), 2px);
  border-image-repeat: round stretch;
}
#s2 .board-frame:has(.mine) {
  --bezel: round(nearest, max(3px, calc(var(--cell) * var(--rule-w-near, 0.124))), 2px);
  border-image-repeat: round stretch;
}
#s2 .board-frame .cell .cross {
  width: round(nearest, calc(var(--bezel) * var(--cross-ratio, 3)), 2px);
  height: round(nearest, calc(var(--bezel) * var(--cross-ratio, 3)), 2px);
}
#s2 .board-frame:has(.theirs) .cell .cross { --cross-ratio: var(--cross-ratio-far, 3); }
#s2 .board-frame:has(.mine)   .cell .cross { --cross-ratio: var(--cross-ratio-near, 3); }

/* --------------------------------------------------------------------------
   THE MOUTH AND ITS BEZEL (founder, 2026-09-03). The stage is one box of
   width --bocca. A bezel is drawn across it whole; the mouth is drawn into
   the ring's hole -- its width, its centre -- or, with no bezel, across
   the whole box. The dice are born at the mouth's own point and land a
   little below it, both read from the mouth's record, so any mask works.
   -------------------------------------------------------------------------- */
#offerStage { min-height: calc(var(--bocca) * (var(--stage-aspect, 1.079) - .25)); }
#offerStage::before {
  height: calc(var(--bocca) * var(--stage-aspect, 1.079));
  /* the ring is the upper layer; the mouth lies under it */
  background:
    var(--bezel-img, none) center / 100% auto no-repeat,
    var(--mouth-img, url("/static/art/bocca.webp"))
      calc(var(--bocca) * (var(--mouth-cx, .5) - var(--mouth-w, 1) / 2))
      calc(var(--bocca) * (var(--mouth-cy, .5395) - var(--mouth-w, 1) * var(--mouth-aspect, 1.079) / 2))
      / calc(var(--bocca) * var(--mouth-w, 1)) auto no-repeat;
}
/* the offer lands below the mouth; the birth is the mouth itself */
#offerArea { top: calc(var(--bocca) * (var(--land-y, .80) - .14)); }
#offerArea .die3d.in-mouth {
  transform: translateY(calc(var(--bocca) * (var(--birth-y, .57) - var(--land-y, .80)))) scale(.12);
}
@keyframes bocca-spit {
  from { transform: translateY(calc(var(--bocca) * (var(--birth-y, .57) - var(--land-y, .80)))) scale(.12); opacity: .0;
         animation-timing-function: cubic-bezier(.4, 0, .7, .5); }
  8%   { opacity: 1; }
  28%  { transform: translateY(calc(var(--bocca) * (var(--birth-y, .57) - var(--land-y, .80)) * .45)) scale(.34);
         animation-timing-function: cubic-bezier(.35, .1, .65, .9); }
  62%  { transform: translateY(calc(var(--bocca) * .14)) scale(.86);
         animation-timing-function: cubic-bezier(.33, .2, .6, 1); }
  78%  { transform: translateY(calc(var(--bocca) * .21)) scale(.98);
         animation-timing-function: cubic-bezier(.3, .4, .4, 1); }
  to   { transform: none; }
}
/* THE RIM IS THE 3x3's RIM, WHATEVER THE BOARD (founder, 2026-09-03: keep
   the leather frame exactly the same on the 4x4; make the tiles smaller,
   not the dice; the gaps may close). The rim's widths follow --rim-cell,
   the cell a 3x3 gets on this screen, not the board's own cell. On a
   4-wide board the gaps close to seven-tenths and the die keeps four
   pixels more of its cell, so it is never smaller than it was. */
#s2 .board-frame {
  --bd-side: calc(var(--rim-cell, var(--cell)) * 0.35);
  --bd-top:  calc(var(--rim-cell, var(--cell)) * 0.385);
}
/* the rails are their own thing (founder, 2026-09-03): their width follows
   the 3x3's cell on this screen, on every board alike */
#s2 .board-frame:has(.theirs) {
  --bezel: round(nearest, max(3px, calc(var(--rim-cell, var(--cell)) * var(--rule-w-far, 0.105))), 2px);
}
#s2 .board-frame:has(.mine) {
  --bezel: round(nearest, max(3px, calc(var(--rim-cell, var(--cell)) * var(--rule-w-near, 0.124))), 2px);
}
#s2[data-cols="4"] .cell .die { width: calc(var(--cell) - 4px); height: calc(var(--cell) - 4px); }
/* THE DIE-SIZE DIALS (founder, 2026-09-03). Placed dice are a fraction of
   their cell, --die-k, the same on every board; rolling dice are the
   stage scaled by --hero-scale. Both live in the skins menu. */
#s2 .cell .die, #s2[data-cols="4"] .cell .die {
  width: calc(var(--cell) * var(--die-k, .94)); height: calc(var(--cell) * var(--die-k, .94));
}
#offerArea { transform: translate(-50%, -50%) scale(var(--hero-scale, 1)); }
#skins .dial-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin: 10px 0 2px; }
#skins .dial { display: grid; gap: 4px; }
#skins .dial input[type=range] { width: 100%; accent-color: #c9a227; margin: 0; }
#skins .dial output { font: 11px/1 var(--sans); color: #f0dfae; opacity: .85; justify-self: end; }
/* --------------------------------------------------------------------------
   THE FLIGHTS (founder, 2026-09-03): eight ways out of the mouth. The
   landing is where #offerArea sits -- shifted by --land-dx for the flights
   that settle beside the mask -- and every path is drawn back from there:
   --mx and --my are the mouth's offset from the landing, in the stage's
   own scale, so a flight reads the same at any rolling-dice size; --bu is
   the mask's width in that scale, the unit the bounces are measured in.
   The sequence dial is the duration of whichever flight is chosen.
   -------------------------------------------------------------------------- */
#offerArea {
  left: calc(50% + var(--land-dx, 0px));
  top: calc(var(--bocca) * (var(--land-y, .80) - .14) + var(--land-dy, 0px));
}
#offerArea .die3d {
  --mx: calc(-1 * var(--land-dx, 0px) / var(--hero-scale, 1));
  --my: calc((var(--bocca) * (var(--birth-y, .57) - var(--land-y, .80)) - var(--land-dy, 0px)) / var(--hero-scale, 1));
  --bu: calc(var(--bocca) / var(--hero-scale, 1));
}
#offerArea .die3d.in-mouth { transform: translate(var(--mx), var(--my)) scale(.12); }
#offerArea .die3d.spat { animation-duration: var(--spit-ms, 1700ms); }
#offerArea[data-flight="left"] .die3d.spat,
#offerArea[data-flight="right"] .die3d.spat { animation-name: flight-arc; }
#offerArea[data-flight="hop"] .die3d.spat { animation-name: flight-hop; }
#offerArea[data-flight="leap"] .die3d.spat { animation-name: flight-leap; }
#offerArea[data-flight="zoom"] .die3d.spat { animation-name: flight-zoom; }
#offerArea[data-flight="skip-left"] .die3d.spat,
#offerArea[data-flight="skip-right"] .die3d.spat { animation-name: flight-skip; }

/* down the tongue, as it was, now drawn from the landing */
@keyframes bocca-spit {
  from { transform: translate(var(--mx), var(--my)) scale(.12); opacity: 0;
         animation-timing-function: cubic-bezier(.4, 0, .7, .5); }
  8%   { opacity: 1; }
  28%  { transform: translate(var(--mx), calc(var(--my) * .45)) scale(.34);
         animation-timing-function: cubic-bezier(.35, .1, .65, .9); }
  62%  { transform: translate(var(--mx), calc(var(--bu) * .14)) scale(.86);
         animation-timing-function: cubic-bezier(.33, .2, .6, 1); }
  78%  { transform: translate(var(--mx), calc(var(--bu) * .21)) scale(.98);
         animation-timing-function: cubic-bezier(.3, .4, .4, 1); }
  to   { transform: none; }
}
/* out and up over the lip, across to one side, one bounce, and rest */
@keyframes flight-arc {
  from { transform: translate(var(--mx), var(--my)) scale(.12); opacity: 0;
         animation-timing-function: cubic-bezier(.4, 0, .6, .6); }
  8%   { opacity: 1; }
  30%  { transform: translate(calc(var(--mx) * .72), calc(var(--my) - var(--bu) * .12)) scale(.46);
         animation-timing-function: cubic-bezier(.3, .2, .7, .8); }
  60%  { transform: translate(calc(var(--mx) * .26), calc(var(--bu) * .05)) scale(.92);
         animation-timing-function: cubic-bezier(.2, .4, .5, 1); }
  74%  { transform: translate(calc(var(--mx) * .11), calc(var(--bu) * -.08)) scale(1);
         animation-timing-function: cubic-bezier(.5, 0, .8, .6); }
  88%  { transform: translate(calc(var(--mx) * .02), calc(var(--bu) * .015)) scale(1.03, .96);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  to   { transform: none; }
}
/* down the tongue and two hops on landing */
@keyframes flight-hop {
  from { transform: translate(var(--mx), var(--my)) scale(.12); opacity: 0;
         animation-timing-function: cubic-bezier(.4, 0, .7, .5); }
  8%   { opacity: 1; }
  28%  { transform: translate(var(--mx), calc(var(--my) * .45)) scale(.34);
         animation-timing-function: cubic-bezier(.35, .1, .65, .9); }
  54%  { transform: translate(var(--mx), calc(var(--bu) * .03)) scale(.94, .86);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  66%  { transform: translate(var(--mx), calc(var(--bu) * -.13)) scale(.96);
         animation-timing-function: cubic-bezier(.5, 0, .8, .5); }
  78%  { transform: translate(var(--mx), calc(var(--bu) * .02)) scale(1.03, .93);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  87%  { transform: translate(var(--mx), calc(var(--bu) * -.05)) scale(1);
         animation-timing-function: cubic-bezier(.5, 0, .8, .5); }
  to   { transform: none; }
}
/* thrown high out of the mouth, a hard landing, one settle */
@keyframes flight-leap {
  from { transform: translate(var(--mx), var(--my)) scale(.12); opacity: 0;
         animation-timing-function: cubic-bezier(.3, .1, .5, 1); }
  6%   { opacity: 1; }
  32%  { transform: translate(calc(var(--mx) * .6), calc(var(--my) - var(--bu) * .40)) scale(.56);
         animation-timing-function: cubic-bezier(.5, 0, .85, .55); }
  62%  { transform: translate(calc(var(--mx) * .15), calc(var(--bu) * .04)) scale(1.06, .88);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  74%  { transform: translate(0, calc(var(--bu) * -.07)) scale(.98, 1.02);
         animation-timing-function: cubic-bezier(.5, 0, .8, .6); }
  86%  { transform: translate(0, calc(var(--bu) * .01)) scale(1.01, .98);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  to   { transform: none; }
}
/* straight at the viewer: grows past full size and settles back */
@keyframes flight-zoom {
  from { transform: translate(var(--mx), var(--my)) scale(.05); opacity: 0;
         animation-timing-function: cubic-bezier(.5, 0, .8, .4); }
  10%  { transform: translate(var(--mx), calc(var(--my) * .95)) scale(.10); opacity: 1;
         animation-timing-function: cubic-bezier(.4, 0, .6, 1); }
  58%  { transform: translate(calc(var(--mx) * .2), calc(var(--my) * .15)) scale(1.24);
         animation-timing-function: cubic-bezier(.3, .3, .5, 1); }
  80%  { transform: translate(0, 0) scale(.96);
         animation-timing-function: cubic-bezier(.3, .3, .5, 1); }
  to   { transform: none; }
}
/* three diminishing skips across to one side */
@keyframes flight-skip {
  from { transform: translate(var(--mx), var(--my)) scale(.12); opacity: 0;
         animation-timing-function: cubic-bezier(.4, 0, .7, .6); }
  6%   { opacity: 1; }
  22%  { transform: translate(calc(var(--mx) * .78), calc(var(--bu) * -.02)) scale(.5);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  34%  { transform: translate(calc(var(--mx) * .62), calc(var(--bu) * -.17)) scale(.62);
         animation-timing-function: cubic-bezier(.5, 0, .8, .5); }
  48%  { transform: translate(calc(var(--mx) * .45), 0) scale(.78, .74);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  60%  { transform: translate(calc(var(--mx) * .32), calc(var(--bu) * -.10)) scale(.88);
         animation-timing-function: cubic-bezier(.5, 0, .8, .5); }
  72%  { transform: translate(calc(var(--mx) * .18), 0) scale(.97, .92);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  82%  { transform: translate(calc(var(--mx) * .08), calc(var(--bu) * -.05)) scale(1);
         animation-timing-function: cubic-bezier(.5, 0, .8, .5); }
  92%  { transform: translate(0, calc(var(--bu) * .01)) scale(1.02, .97);
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  to   { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  #offerArea .die3d.in-mouth { transform: none; }
  #offerArea[data-flight] .die3d.spat { animation: none; }
}
/* the pace dials sit three across; the flight is a plain select in the same dress */
#skins .dial-row.three { grid-template-columns: 1fr 1fr 1fr; margin-top: 6px; }
#skins .dial.flight { margin: 8px 0 2px; }
#skins .dial.flight select {
  width: 100%; font: 13px/1.2 var(--sans); color: #f0dfae; background: rgba(0,0,0,.35);
  border: 1px solid rgba(201,162,39,.5); border-radius: 6px; padding: 6px 8px;
}
/* --------------------------------------------------------------------------
   THE BOARDS CENTRED, THE ROLLS ON OPPOSITE SIDES (founder, 2026-09-03).
   The far roll stays on the left, the enemy's; the near roll goes to the
   right, ours. A plate is painted with its roll on the left, so the near
   plate is drawn mirrored on a pseudo-element behind the cells: the frame
   keeps a border for layout, its side widths swapped and its colour
   transparent, and the pseudo-element wears the plate with the original
   widths and a horizontal flip, so roll meets roll to the pixel. The near
   construct moves to the roll's side.
   -------------------------------------------------------------------------- */
body:has(#s2.on) #s2 { padding-left: 0; padding-right: 0; }
#s2 > #guide, #s2 > #talkStrip, #s2 > #barPurse { margin-left: 0; }
#s2 .board-frame { align-self: center; }
#s2 .board-frame:has(.mine) {
  --bd-left:  calc(var(--bd-top) * var(--rim-r-near, .909));   /* the thin edge, now on the left */
  --bd-right: calc(var(--bd-top) * var(--rim-l-near, .909));   /* the roll, now on the right */
  border-width: var(--bd-top) var(--bd-right) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
  border-image: none; border-color: transparent;
  isolation: isolate;
}
#s2 .board-frame:has(.mine)::before {
  content: ""; position: absolute; z-index: -1; pointer-events: none;
  top: calc(-1 * var(--bd-top)); bottom: calc(-1 * var(--bd-top) * var(--rim-b-near, 1));
  left: calc(-1 * var(--bd-left)); right: calc(-1 * var(--bd-right));
  border: solid transparent;
  border-width: var(--bd-top) var(--bd-left) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-right);
  border-image-source: var(--board-img-near, url("/static/art/board_blue.webp"));
  border-image-slice: var(--board-slice-near, 47 50 48 50);
  border-image-width: 1;
  border-image-repeat: round stretch;
  transform: scaleX(-1);
}
/* the near construct stands by the near roll, on the right */
#s2 .board-frame:has(.mine) > .total {
  right: auto; left: calc(100% + var(--bd-right) + 4px);
}
/* the system line keeps to the free zone left of the centred mask, and
   wraps rather than running off the screen */
#banner, #sysLine {
  left: 6px; right: calc(50% + var(--bocca) * .56);
  white-space: normal; text-align: center; max-width: none;
}
/* THE GRIDS ARE CENTRED, NOT THE LEATHER (founder, 2026-09-03). A frame
   is centred by its border box, and a roll is thicker than the edge
   opposite it, so a centred frame carries its grid off-centre by half
   the difference. Each frame is slid back by that half: the far grid and
   the near grid share one axis, and the rolls hang past it on their own
   sides. */
#s2 .board-frame:has(.theirs) { --bd-right: calc(var(--bd-top) * var(--rim-r-far, .909)); }
#s2 .board-frame { left: calc((var(--bd-right, var(--bd-left)) - var(--bd-left)) / 2); }
/* --------------------------------------------------------------------------
   ONE BOCCA EACH (founder, 2026-09-04). Each player's mouth stands on the
   thin side of their board, opposite the roll, at the board's middle,
   with the name scroll and the score shield stacked above it; the
   medallion and the level tag stand down until the founder draws the
   ensemble. The column is three widths tall and centred on the board, so
   the mouth's centre is the board's centre. The dice are born from the
   caster's own mouth; the offer area lives inside it. Between the boards
   only the system line remains, centred.
   -------------------------------------------------------------------------- */
#s2 .total .pf-medal, #s2 .total .pf-level { display: none; }
#s2 .board-frame:has(.theirs) > .total,
#s2 .board-frame:has(.mine)   > .total {
  top: 50%; transform: translateY(-50%); height: calc(var(--pfw) * 3);
}
#s2 .board-frame:has(.theirs) > .total { right: auto; left: calc(100% + var(--bd-right) + 4px); }   /* the far thin side is the right */
#s2 .board-frame:has(.mine)   > .total { left: auto; right: calc(100% + var(--bd-left) + 4px); }    /* the near thin side is the left */
/* the same stack for both: scroll, shield, mouth */
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll, #s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: 0; }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-shield, #s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield {
  top: calc(var(--pfw) * .40); width: 50%; height: calc(var(--pfw) * .58);
  background-image: url("/static/art/pf_shield.webp");
}
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield .n { inset: 0 0 16% 0; }
#s2 .total .pf-shield .n { font-size: calc(var(--pfw) * .24); }
/* the mouth: a box one column wide under the shield, the mask across it
   (under its bezel, when it has one) and the landing below the mask */
#s2 .total .mouth {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: calc(var(--pfw) * .96); width: var(--pfw); height: calc(var(--pfw) * 1.5);
  --bocca: var(--pfw);
}
#s2 .total .mouth::before {
  content: ""; position: absolute; pointer-events: none; z-index: 2;
  left: 0; top: 0; width: var(--bocca); height: calc(var(--bocca) * var(--stage-aspect, 1.079));
  background:
    var(--mouth-img, url("/static/art/bocca.webp"))
      calc(var(--bocca) * (var(--mouth-cx, .5) - var(--mouth-w, 1) / 2))
      calc(var(--bocca) * (var(--mouth-cy, .5395) - var(--mouth-w, 1) * var(--mouth-aspect, 1.079) / 2))
      / calc(var(--bocca) * var(--mouth-w, 1)) auto no-repeat,
    var(--bezel-img, none) center top / 100% auto no-repeat;
}
/* the offer lands at the mouth's own landing, measured from the mask's top */
#s2 .total .mouth #offerArea {
  top: calc(var(--bocca) * var(--land-y, .80) + var(--land-dy, 0px));
  left: calc(50% + var(--land-dx, 0px));
}
/* the gap between the boards: the system line, centred, and nothing else */
#offerStage::before { display: none; }
#offerStage { min-height: 0; padding: 6px 0; }
#sysLine {
  position: static; transform: none; left: auto; right: auto;
  display: block; margin: 0 auto; max-width: 92%;
}
#sysLine:empty { display: none; }
/* the champion speaks from the far mouth, which stands on the right: the
   bubble opens leftward, its tail on the right */
#s2 .mouth .speech {
  position: absolute; z-index: 20;
  right: 94%; left: auto; top: 8%;
  width: max-content; max-width: min(58vw, 230px);
  margin: 0; padding: 7px 10px 8px;
  font: italic 12.5px / 1.3 var(--serif); color: #2a1b0c;
  text-align: left; white-space: normal; letter-spacing: 0; text-transform: none;
  background: #f5ecd6; border: 1.5px solid #8a6a2c; border-radius: 7px;
  box-shadow: 0 2px 6px rgba(40, 26, 8, .32), inset 0 0 0 1px rgba(255, 250, 235, .7);
  transform-origin: 100% 18px;
  animation: speech-pop 200ms ease-out both;
  cursor: pointer; -webkit-tap-highlight-color: transparent;
}
#s2 .mouth .speech::before {
  content: ""; position: absolute; right: -7px; top: 12px;
  width: 11px; height: 11px; background: #f5ecd6;
  border-right: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(-45deg);
}
#s2 .mouth .speech.gone { animation: speech-fade 240ms ease-in both; pointer-events: none; }
@media (prefers-reduced-motion: reduce) {
  #s2 .mouth .speech { animation: none; }
  #s2 .mouth .speech.gone { animation: none; opacity: 0; }
}
/* --------------------------------------------------------------------------
   THE MASKS MEET IN THE MIDDLE (founder, 2026-09-04). The rolls go back to
   one side, both left as the plates are painted, the grids still centred.
   Between the boards runs a band: the far mask at its left, yours at its
   right, the name beneath each mask and the shield beneath the name; the
   dice are born from the caster's mouth, fly across, and land in the
   band's middle, with the system line under them.
   -------------------------------------------------------------------------- */
/* the near plate as painted again: its roll on the left */
#s2 .board-frame:has(.mine) {
  --bd-left:  calc(var(--bd-top) * var(--rim-l-near, .909));
  --bd-right: calc(var(--bd-top) * var(--rim-r-near, .909));
  border-width: var(--bd-top) var(--bd-right) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
  border-image-source: var(--board-img-near, url("/static/art/board_blue.webp"));
  border-image-slice: var(--board-slice-near, 47 50 48 50);
  border-image-width: 1; border-image-outset: 0; border-image-repeat: round stretch;
  isolation: auto;
}
#s2 .board-frame:has(.mine)::before { display: none; }

/* the band */
#strip {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 6px;
  width: 100%; margin: 6px 0; padding: 0; min-height: 0;
}
#strip > .total {
  position: relative; flex: 0 0 auto; order: 0;
  display: block; padding: 0; margin: 0; border: 0; background: none; box-shadow: none; min-width: 0;
  width: var(--pfw); height: calc(var(--pfw) * 1.8);
}
#strip > #t1 { order: 0; }        /* the far mask, left */
#strip > #offerStage { order: 1; }
#strip > #t0 { order: 2; }        /* yours, right */
/* the stack: mask, name, shield */
#s2 .total .mouth { top: 0; height: calc(var(--bocca) * var(--stage-aspect, 1.079)); }
#s2 .total .pf-scroll { top: calc(var(--pfw) * .88); width: 100%; }
#s2 .total .pf-shield { top: calc(var(--pfw) * 1.22); width: 50%; height: calc(var(--pfw) * .58); }
#s2 .total .pf-shield .n { inset: 0 0 16% 0; }

/* the middle: the offer at its centre, the line beneath */
#strip > #offerStage {
  position: relative; flex: 1 1 auto; min-height: calc(var(--pfw) * 1.8); padding: 0;
  display: block; overflow: visible;
}
#offerStage #offerArea {
  position: absolute; z-index: 4; min-height: 0;
  left: calc(50% + var(--land-dx, 0px)); top: calc(50% + var(--land-dy, 0px));
  transform: translate(-50%, -50%) scale(var(--hero-scale, 1));
}
#offerStage #sysLine {
  position: absolute; left: 0; right: 0; bottom: 2px; transform: none;
  display: block; margin: 0; max-width: none; padding: 0 4px;
}
#sysLine:empty { display: none; }
/* every flight begins at the caster's mouth: the birth offset is measured
   on the screen and handed down in pixels */
#offerArea .die3d {
  --mx: calc(var(--birth-dx, 0px) / var(--hero-scale, 1));
  --my: calc(var(--birth-dy, 0px) / var(--hero-scale, 1));
  --bu: calc(var(--pfw, 80px) / var(--hero-scale, 1));
}
#offerArea[data-flight="across"] .die3d.spat { animation-name: flight-arc; }
/* the far mask stands on the left now: its bubble opens rightward */
#s2 .mouth .speech { left: 94%; right: auto; transform-origin: 0 18px; }
#s2 .mouth .speech::before {
  left: -7px; right: auto;
  border-left: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c; border-right: none;
  transform: rotate(45deg);
}
/* the landing sits in the band's upper part, the line beneath it */
#offerStage #offerArea { top: calc(42% + var(--land-dy, 0px)); }
#offerStage #sysLine { font-size: 10.5px; letter-spacing: .12em; }
/* the line under the landing keeps to one or two rows */
#offerStage #sysLine { font-size: 10px; letter-spacing: .08em; line-height: 1.2; padding: 0 2px; }
/* the line's box: bottom-anchored only (an older rule still pinned its top
   to the band's middle, and the box stretched between the two) */
#offerStage #sysLine { top: auto; height: auto; }
/* the champion's bubble hangs ABOVE his mask, over the far board's foot,
   so it never lies across the landing while dice are flying */
#s2 .mouth .speech {
  left: 0; right: auto; top: auto; bottom: calc(100% + 8px);
  transform-origin: 14px 100%;
}
#s2 .mouth .speech::before {
  left: 14px; right: auto; top: auto; bottom: -7px;
  border-left: none; border-right: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(45deg);
}
/* --------------------------------------------------------------------------
   THE MASKS BULGE FROM THE CORNERS (founder, 2026-09-04). The rolls are
   opposed again -- far left, near right -- so each board's thin side is
   free, and its owner's mask bulges out of the inner corner there, a
   little into the gap. Wick's at the far board's bottom-right, name and
   shield climbing its right side; yours at the near board's top-left,
   name and shield dripping down its left side. Both mouths face the gap,
   which keeps only the landing and the line.
   -------------------------------------------------------------------------- */
/* the near plate mirrored again: its roll on the right */
#s2 .board-frame:has(.mine) {
  --bd-left:  calc(var(--bd-top) * var(--rim-r-near, .909));
  --bd-right: calc(var(--bd-top) * var(--rim-l-near, .909));
  border-width: var(--bd-top) var(--bd-right) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
  border-image: none; border-color: transparent;
  isolation: isolate;
}
#s2 .board-frame:has(.mine)::before { display: block; }
#s2 .board-frame { --bd-b: calc(var(--bd-top) * var(--rim-b-far, 1)); }
#s2 .board-frame:has(.mine) { --bd-b: calc(var(--bd-top) * var(--rim-b-near, 1)); }

/* the columns, in their frames again: 2.15 widths tall, the mask a width
   square-ish at one end, the scroll and shield trailing from the other */
#s2 .board-frame > .total {
  position: absolute; z-index: 6; flex: none; order: 0;
  display: block; padding: 0; margin: 0; border: 0; background: none; box-shadow: none; min-width: 0;
  width: var(--pfw); height: calc(var(--pfw) * 2.15);
}
/* the far column: shield, scroll, mask; the mask's centre 24px past the
   right edge and 8px above the bottom edge, so it bulges out and a little
   down into the gap */
#s2 .board-frame:has(.theirs) > .total {
  top: auto; right: auto; transform: none;
  left: calc(100% + var(--bd-right) + 24px - var(--pfw) / 2);
  bottom: calc(-1 * var(--bd-b) + 8px - var(--pfw) * .54);
}
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-shield { top: 0; }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { top: calc(var(--pfw) * .62); }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth     { top: calc(var(--pfw) * 1.07); }
/* the near column: mask, scroll, shield; the mask's centre 24px past the
   left edge and 8px below the top edge */
#s2 .board-frame:has(.mine) > .total {
  bottom: auto; left: auto; transform: none;
  right: calc(100% + var(--bd-left) + 24px - var(--pfw) / 2);
  top: calc(-1 * var(--bd-top) + 8px - var(--pfw) * .54);
}
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .mouth     { top: 0; }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: calc(var(--pfw) * 1.12); }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield { top: calc(var(--pfw) * 1.57); }
#s2 .total .pf-scroll { width: 100%; }
#s2 .total .pf-shield { width: 50%; height: calc(var(--pfw) * .58); left: 50%; }
#s2 .total .mouth { height: calc(var(--bocca) * var(--stage-aspect, 1.079)); }

/* the gap: the landing in its upper part, the line beneath */
#strip { display: block; margin: 4px 0; min-height: 0; }
#strip > #offerStage { min-height: 88px; }
#offerStage #offerArea { top: calc(44% + var(--land-dy, 0px)); }
#offerStage #sysLine { top: auto; height: auto; }

/* the far mask stands at the right now: its bubble opens leftward, level with it */
#s2 .mouth .speech {
  right: 94%; left: auto; top: 8%; bottom: auto;
  transform-origin: 100% 18px;
}
#s2 .mouth .speech::before {
  right: -7px; left: auto; top: 12px; bottom: auto;
  border-left: none; border-right: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(-45deg);
}
/* the column's height, restated at the weight the older centred rule had */
#s2 .board-frame:has(.theirs) > .total,
#s2 .board-frame:has(.mine)   > .total { height: calc(var(--pfw) * 2.15); }
/* --------------------------------------------------------------------------
   MASKS IN THE MIDDLE, BOARDS CLOSER, BOCCAS BIGGER (founder, 2026-09-04).
   The boards stand --gap apart. Each mask is bigger than the gap and hangs
   centred on it, bulging over both boards' rims, seated on its owner's own
   roll end: the far mask at the left, yours at the right. Wick's name and
   shield climb the far board's left side above his mask; yours drip down
   the near board's right side below yours. The dice land between the
   masks; the line sits at the gap's foot.
   -------------------------------------------------------------------------- */
#s2 { --gap: 72px; --mask: min(104px, 27vw); }
#strip { display: block; margin: 0; min-height: var(--gap); }
#strip > #offerStage { min-height: var(--gap); }
#offerStage #offerArea { top: calc(40% + var(--land-dy, 0px)); }

/* the columns: outside the roll sides, a scroll's width; the mask overhangs
   the column toward the board, centred on the board's edge */
#s2 .board-frame > .total { width: var(--pfw); height: calc(var(--mask) * 1.08 + 6px + var(--pfw) * 1.05); }
#s2 .board-frame:has(.theirs) > .total,
#s2 .board-frame:has(.mine)   > .total { height: calc(var(--mask) * 1.08 + 6px + var(--pfw) * 1.05); }
#s2 .total .mouth {
  --bocca: var(--mask); width: var(--mask); height: calc(var(--mask) * var(--stage-aspect, 1.079));
  transform: none;
}
/* the far column, left of the far board: shield, scroll, mask at the foot,
   the mask's centre on the board's left edge and on the gap's middle */
#s2 .board-frame:has(.theirs) > .total {
  left: auto; top: auto; transform: none;
  right: calc(100% + var(--bd-left) + 2px);
  bottom: calc(-1 * var(--bd-b) - var(--gap) / 2 - var(--mask) * .54);
}
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth     { top: auto; bottom: 0; left: auto; right: calc(-1 * (var(--mask) / 2 + 2px)); }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { top: auto; bottom: calc(var(--mask) * 1.08 + 6px); }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-shield { top: auto; bottom: calc(var(--mask) * 1.08 + 6px + var(--pfw) * .47); }
/* the near column, right of the near board: mask at the head, scroll, shield */
#s2 .board-frame:has(.mine) > .total {
  right: auto; bottom: auto; transform: none;
  left: calc(100% + var(--bd-right) + 2px);
  top: calc(-1 * var(--bd-top) - var(--gap) / 2 - var(--mask) * .54);
}
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .mouth     { top: 0; bottom: auto; right: auto; left: calc(-1 * (var(--mask) / 2 + 2px)); }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { bottom: auto; top: calc(var(--mask) * 1.08 + 6px); }
#s2 :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield { bottom: auto; top: calc(var(--mask) * 1.08 + 6px + var(--pfw) * .47); }
/* the far mask is at the left: its bubble opens rightward, level with it */
#s2 .mouth .speech { left: 94%; right: auto; top: 8%; bottom: auto; transform-origin: 0 18px; }
#s2 .mouth .speech::before {
  left: -7px; right: auto; top: 12px; bottom: auto;
  border-right: none; border-left: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(45deg);
}
/* the line keeps between the masks, in the gap's foot; the gap is 80 so
   the dice above it have their room */
#s2 { --gap: 80px; }
#offerStage #offerArea { top: calc(36% + var(--land-dy, 0px)); }
#offerStage #sysLine { left: calc(var(--mask) + 4px); right: calc(var(--mask) + 4px); bottom: 3px; }
/* the dust piles belong to the table: they leave with it (founder,
   2026-09-04: they lingered into the verdict and the menu) */
body:not(:has(#s2.on)) #dustFloor, body:not(:has(#s2.on)) .dust-plaque { display: none; }
/* the arc out of the mouth falls with gravity (founder, 2026-09-04: not
   floaty): up with a decelerating rise, down ever faster, a short bounce */
@keyframes flight-arc {
  from { transform: translate(var(--mx), var(--my)) scale(.12); opacity: 0;
         animation-timing-function: cubic-bezier(.2, .5, .5, 1); }
  8%   { opacity: 1; }
  28%  { transform: translate(calc(var(--mx) * .72), calc(var(--my) - var(--bu) * .14)) scale(.5);
         animation-timing-function: cubic-bezier(.55, 0, 1, .55); }
  58%  { transform: translate(calc(var(--mx) * .22), calc(var(--bu) * .03)) scale(.96);
         animation-timing-function: cubic-bezier(.1, .6, .4, 1); }
  70%  { transform: translate(calc(var(--mx) * .1), calc(var(--bu) * -.06)) scale(1);
         animation-timing-function: cubic-bezier(.55, 0, 1, .6); }
  80%  { transform: translate(calc(var(--mx) * .03), calc(var(--bu) * .01)) scale(1.02, .96);
         animation-timing-function: cubic-bezier(.2, .6, .5, 1); }
  to   { transform: none; }
}
/* THE DICE FLY OVER THE MASKS (founder, 2026-09-05: "the dice come from
   underneath the faces"). The band the dice live in stood below the
   constructs; it now stands above them, letting taps through to what it
   does not cover, and the dice alone catch a tap. */
#strip { z-index: 9; pointer-events: none; }
#strip #offerArea { pointer-events: auto; }
/* THE THROW IS THE SCENE'S (founder, 2026-09-05). The canvas grows to hold
   the mouth the dice are born in and the arc they fly, so it is not the
   tap target any more: the picker's halves are, cut to the dice at rest.
   The stylesheet's flights are retired -- the scene flies the dice. */
#offerArea .die3d { pointer-events: none; }
#offerArea #slotPicker { pointer-events: auto; }
#offerArea .die3d.in-mouth { transform: none; }
#offerArea .die3d.spat { animation: none; }
/* HIDDEN IS HIDDEN, EVERYWHERE (founder, 2026-09-05: "claim this game"
   at every verdict, logged in or not -- for the third time). The script
   had hidden the row correctly all along; `.row { display: flex }`
   outranks the browser's own [hidden] rule, and this row, the name row
   of the antechamber, the counter-offer pick and the custom wager row
   had never been given the per-id override every other hidden control
   on this page carries. The page's idiom of one override per id is the
   trap that bit three times; this ends it for every element at once. */
[hidden] { display: none !important; }
/* A COLUMN TAPPED BEFORE A DIE IS TAKEN (founder, 2026-09-05: "the middle
   dice jump and the whole arena jumps a bit"). The old shake animated the
   stage's transform, which REPLACED its centring translate for the
   length of the animation: the dice leapt half a stage away and back,
   and the page shifted under them. Now the stage holds still and keeps
   its breathing glow, and the dice host inside it -- which has no
   transform of its own -- wiggles left and right: pick one of us first. */
#offerArea.shake { animation: waiting 1.9s ease-in-out infinite; }
#offerArea.shake .die3d { animation: wiggle 440ms ease-in-out; }
@keyframes wiggle {
  0%, 100% { transform: translateX(0); }
  18%      { transform: translateX(-8px); }
  42%      { transform: translateX(8px); }
  66%      { transform: translateX(-5px); }
  86%      { transform: translateX(3px); }
}
/* THE MAIN MENU SCROLLS ON ANDROID (founder, 2026-09-05: it would not,
   while both iPhone browsers scrolled it). The screen was declared a
   scroll container with overscroll-behavior: contain in the days the
   sheet had a bounded height. Since the page learned to grow with its
   content the screen never overflows and the document is the scroller;
   Blink then lets a scroll container that cannot scroll, marked contain,
   swallow the gesture instead of chaining it to the document, and WebKit
   chains it anyway. Measured: the same wheel moved the page 0px as
   shipped and 187px with the rule lifted. The screen is no scroll
   container at all now; the page scrolls, as it does on iPhone. */
#s1 { overflow-y: visible; overscroll-behavior: auto; }
/* THE FAR BOARD'S SUMS ON ITS LOWER BAND (founder, 2026-09-05: "the upper
   diceboard column-totals should be displayed on the lower bezel, not on
   the top one"). They stood on the far board's top band, its outer edge;
   they now stand on its bottom band, facing the gap, where the eye that
   reads the near board's sums below its columns finds them in the same
   place on both boards. */
#s2 .board-frame:has(.theirs) .colsum {
  top: auto; bottom: calc(-1 * var(--bd-bottom, var(--bd-top)));
  height: var(--bd-bottom, var(--bd-top)); line-height: var(--bd-bottom, var(--bd-top));
}
/* THICK LEATHER (founder, 2026-09-05: the pictures' rim and roll are about
   twice what the page cut them to). Measured on the originals: top 0.75,
   right 0.59, bottom 0.92 and roll 1.43 cells; every band here follows
   the top one by the plate's own slice ratios, and 0.78 of a cell for the
   top lands all four within a few percent of the picture. The sums keep a
   readable size on the wider band rather than growing with it. */
#s2[data-leather="thick"] .board-frame {
  --bd-top: calc(var(--rim-cell, var(--cell)) * 0.78);
  --bd-side: calc(var(--rim-cell, var(--cell)) * 0.71);
}
#s2[data-leather="thick"] .board-frame .colsum {
  font-size: min(calc(var(--bd-top) * 0.5), calc(var(--rim-cell, var(--cell)) * 0.27));
}
/* THE LEATHER ON A SLIDER (founder, 2026-09-05: thick was "TOO thick"; a
   slider to gauge it exactly). --leather-k is the top band as a fraction
   of the rim's cell, set by the page from the dial: the pictures' 0.78 at
   100%, the first thin cut's 0.385 at 49%. The other bands follow by the
   plate's slices, as ever. The boards' cells are sized apart from it, so
   the leather grows outward around the same board. The two-word rule
   above is retired: the attribute it read is no longer set. */
#s2 .board-frame {
  --bd-top:  calc(var(--rim-cell, var(--cell)) * var(--leather-k, 0.385));
  --bd-side: calc(var(--rim-cell, var(--cell)) * var(--leather-k, 0.385) * 0.91);
}
#s2 .board-frame .colsum {
  font-size: min(calc(var(--bd-top) * 0.5), calc(var(--rim-cell, var(--cell)) * 0.27));
}
/* THE LEATHER FOLLOWS THE BOARD'S OWN CELL (founder, 2026-09-05: measured
   against the picture at the same cell size, the roll on a 4x4 was far
   fatter -- the rim had been cut from the 3x3's cell, whatever the board,
   since 2026-09-03). The pictures' rim is a share of the cell it frames;
   so is this one now, on every board: 100% on the dial is the picture on
   a 3x3 and on a 4x4 alike. The rails keep following the 3x3's cell, as
   ruled. */
#s2 .board-frame {
  --bd-top:  calc(var(--cell) * var(--leather-k, 0.385));
  --bd-side: calc(var(--cell) * var(--leather-k, 0.385) * 0.91);
}
/* THE LEATHER IS THE 4x4's, ON EVERY BOARD (founder, 2026-09-05: the 4x4
   at 100% is "the perfect size bezel"; a 3x3 must wear the same roll,
   not a thicker one, and be a smaller board whole). --rim-cell is the
   reference board's cell now, fitted by the page with the leather at the
   dial's thickness, and every band is a share of it -- so the roll is the
   same pixels whatever the board's own cells are. */
#s2 .board-frame {
  --bd-top:  calc(var(--rim-cell, var(--cell)) * var(--leather-k, 0.385));
  --bd-side: calc(var(--rim-cell, var(--cell)) * var(--leather-k, 0.385) * 0.91);
}
/* THE TABLE KEEPS ITS PROPORTION (founder, 2026-09-05: "maintain a maximum
   width to height ratio ... the default ratio is 853x1844"). The wrap is
   never wider than the screen's height times 853/1844, nor than the 430px
   it was capped at: a wide, short window gets a narrower table, a tall
   one does not get a wider one. */
#wrap { max-width: min(430px, calc(100dvh * 853 / 1844)); }
/* THE PURSE BAR IS GONE FROM THE TABLE (founder, 2026-09-05: "the vertical
   space is too precious at this point"). The pile, the pot and the dust
   were printed on a line under the near board with the receipt's link;
   the piles say the dust when tapped, the verdict opens the receipt. The
   script still fills the line, unseen, so nothing it learned is lost. */
#barPurse { display: none !important; }
/* THE PROPORTION, CORRECTED (founder, 2026-09-05: the window a browser
   actually gives the game on an iPhone 15 Pro Max is 1180x2035, not the
   853x1844 given first -- which squeezed the phone's own table to 340px).
   Measured against the SMALL viewport height, the one with the browser's
   bars showing, so the width holds still as the bars slide away. */
#wrap { max-width: min(430px, calc(100svh * 1180 / 2035)); }
/* THE MASKS AT THE SCREEN'S EDGES, THE BOARDS BIGGER (founder, 2026-09-05).
   Each column -- shield, scroll and mask -- stood beside its board's
   roll, and the boards were sized to leave it room. Now the far column
   stands at the wrap's left edge and the near at its right, from a
   measurement the fitter makes (--far-edge, --near-edge: the edge's
   distance from the frame's padding box), the mask flush with the edge,
   and where the board has grown out to meet the column the shield and
   the scroll ride over the roll's leather. */
#s2 .board-frame:has(.theirs) > .total { right: auto; left: calc(2px - var(--far-edge, 0px)); }
#s2 .board-frame:has(.mine)   > .total { left: auto; right: calc(2px - var(--near-edge, 0px)); }
#s2 .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { right: auto; left: 0; }
#s2 :is(.board-frame:has(.mine), .total[data-turned])   .mouth { left: auto; right: 0; }
/* THE DICE COME FROM THE ROLL (founder, 2026-09-06: "the cleanest possible
   version of things"). With the casts born at the leather rolls' ends
   the masks have nothing to do: they hide, the band between the boards
   narrows, and the line beneath the landing takes the whole width. The
   mouth stays a development switch, on the attribute. */
#s2[data-cast="roll"] { --gap: 70px; }
#s2[data-cast="roll"] .total .mouth { display: none; }
#s2[data-cast="roll"] #offerStage #sysLine { left: 10px; right: 10px; }
/* HALF A FACE (founder, 2026-09-06: the die from the mat roll "looks
   stupid. Have only half the faces show"). The masks return, cut in half
   by the screen's edge: the outer half beyond it, the inner half showing,
   the mouth at the edge, so the dice come in from the wings. The wrap
   clips what hangs past it without becoming a scroller. */
#s2[data-cast="half"] { --gap: 76px; }
#s2[data-cast="half"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: calc(-1 * var(--mask) / 2 - 2px); }
#s2[data-cast="half"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: calc(-1 * var(--mask) / 2 - 2px); }
body:has(#s2[data-cast="half"].on) #wrap { overflow-x: clip; }
/* FULL FLAVOUR IS GONE (founder, 2026-09-14: "it does not do anything
   good"). The regalia -- a pillar of identity on the thin side of each
   board, the board stepping aside for it -- stood here from 2026-09-06.
   The clean board is the table. The shaft stays hidden. */
#s2 .total .pf-shaft { display: none; }
/* the boards step aside for their pillars: the far to the left, the near to the right */
/* the pillars: the far one right of the far board, from its top band down
   into the gap; the near one left of the near board, from the gap down to
   its bottom band */
/* the shaft: fluted stone, between the shield and the mask */
/* the far pillar, top to bottom: medallion, scroll, shield, shaft, mouth */
/* the near pillar, mirrored: mouth, shaft, shield, scroll, medallion */
/* the half faces, bigger (founder, 2026-09-06: "the half-faces can be
   bigger now, no?"): with only the inner half on screen, a face of 150px
   shows 75, less than the whole mask did */
#s2[data-cast="half"] { --mask: min(150px, 38vw); }
/* the pillar, corrected on the first sight of it: the lap is a share of
   the frame's own band, so it is declared on the frame; the medallion,
   hidden since the masks became the identity, shows at the pillar's
   head; and the stack is tightened so the shaft has room between the
   shield and the mouth on a 3x3's height */
/* THE FACES AS THE FOUNDER DREW THEM (2026-09-06, "like this"): big, and
   seven tenths of each showing -- the outer three tenths past the screen's
   edge -- standing on the gap's middle and over the boards' corners. */
#s2[data-cast="half"] { --mask: min(164px, 42vw); --gap: 78px; }
#s2[data-cast="half"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: calc(-0.3 * var(--mask) - 2px); }
#s2[data-cast="half"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: calc(-0.3 * var(--mask) - 2px); }
/* half, exactly (founder, 2026-09-06: "not 75. 50%!"): the big faces keep
   their size, and precisely half of each stands on the screen */
#s2[data-cast="half"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: calc(-0.5 * var(--mask)); }
#s2[data-cast="half"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: calc(-0.5 * var(--mask)); }
/* THE PROFILE FACES (founder, 2026-09-06: "make the dice come out of
   these"). A profile's mouth is on the edge of the face, so the whole
   face stands at the screen's edge, its plate's back flush with it, and
   nothing is hidden. The face is drawn at --mask wide and its own aspect
   tall, centred on the gap's middle; a profile looking the wrong way for
   its side is mirrored. */
#s2[data-cast="profile"] { --mask: min(98px, 25vw); --gap: 80px; }
#s2[data-cast="profile"] .total .mouth { transform: var(--mouth-flip, none); }
#s2[data-cast="profile"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: 0; right: auto; }
#s2[data-cast="profile"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: 0; left: auto; }
/* the column's anchor puts the face's middle on the gap's middle, by the
   face's own height */
#s2[data-cast="profile"] .board-frame:has(.theirs) > .total {
  bottom: calc(-1 * var(--bd-b) - var(--gap) / 2 - var(--mask) * var(--stage-aspect, 2.2) / 2);
}
#s2[data-cast="profile"] .board-frame:has(.mine) > .total {
  top: calc(-1 * var(--bd-top) - var(--gap) / 2 - var(--mask) * var(--stage-aspect, 2.2) / 2);
}
/* the scroll and the shield stand clear of the tall face, above it on the
   far side and below it on the near */
#s2[data-cast="profile"] .board-frame > .total { height: calc(var(--mask) * var(--stage-aspect, 2.2) + 6px + var(--pfw) * 1.05); }
#s2[data-cast="profile"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { bottom: calc(var(--mask) * var(--stage-aspect, 2.2) + 6px); }
#s2[data-cast="profile"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-shield { bottom: calc(var(--mask) * var(--stage-aspect, 2.2) + 6px + var(--pfw) * .47); }
#s2[data-cast="profile"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: calc(var(--mask) * var(--stage-aspect, 2.2) + 6px); }
#s2[data-cast="profile"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield { top: calc(var(--mask) * var(--stage-aspect, 2.2) + 6px + var(--pfw) * .47); }
/* BOTH ROLLS ON THE LEFT, BOTH BOARDS CENTRED ALIKE (founder, 2026-09-06:
   "maybe it will be better if both boards had their roll on the same
   side now and they both sit perfectly center"). The near board wore its
   plate mirrored, roll to the right; it wears it as painted now, roll to
   the left like the far board's, and since both frames centre their
   grids by the same offset, the two boards stand exactly over each
   other. */
#s2 .board-frame:has(.mine) {
  --bd-left:  calc(var(--bd-top) * var(--rim-l-near, .909));   /* the roll, on the left */
  --bd-right: calc(var(--bd-top) * var(--rim-r-near, .909));   /* the thin edge, on the right */
  border-width: var(--bd-top) var(--bd-right) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
}
#s2 .board-frame:has(.mine)::before {
  border-width: var(--bd-top) var(--bd-right) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
  transform: none;
}

/* THE FACES, SMALLER AND STRAIGHT (founder, 2026-09-06: "make the faces
   smaller so they don't intrude on the boards ... the masks' backs sit
   straight and flush against the edge of the screen"). Sixty pixels wide
   in a band of ninety-two, so a face reaches only over the leather bands
   above and below the gap, never a cell; and each plate overhangs the
   screen's edge by a seventh of its width, so the edge slices the
   plate's curved back into a straight, flush line. */
#s2[data-cast="profile"] { --mask: min(60px, 15.5vw); --gap: 92px; }
#s2[data-cast="profile"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: calc(-0.14 * var(--mask)); }
#s2[data-cast="profile"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: calc(-0.14 * var(--mask)); }
body:has(#s2[data-cast="profile"].on) #wrap { overflow-x: clip; }
/* THE WHOLE LEATHER CENTRED (founder, 2026-09-06: "don't center the dice
   area, center the whole leather boards, so that the space each mask has
   is equal"). The frames stood offset so their grids were centred; they
   stand at true centre now, roll and all, and the margins beside them are
   equal for the two faces. */
#s2 .board-frame { left: 0; }
/* the faces, larger again (founder, 2026-09-06: "a lot smaller now"):
   seventy-eight wide in a band of ninety-six; still only the leather
   bands above and below the gap under them */
#s2[data-cast="profile"] { --mask: min(78px, 20vw); --gap: 96px; }
/* the band between the boards back to 80px (founder, 2026-09-06: "It was
   perfect before"); the faces keep their size and reach a little further
   over the leather bands, still never a cell */
#s2[data-cast="profile"] { --gap: 80px; }
/* THE TABLE'S OWN GAP, NAMED (2026-09-06). The table puts 4px between each
   board and the strip (the flex gap it has had since the first layout);
   the faces' anchors need that number, so it lives in one place. */
#s2 { --table-gap: 4px; gap: var(--table-gap); }
/* THE FACES LEVEL (founder, 2026-09-06: "make sure the masks are exactly
   the same height and are at the same height on the arena. they are
   currently one higher than the other"). Each face was centred half a
   gap below or above its own board's edge, but the boards stand a gap
   and two table gaps apart, so the far face sat 4px high and the near
   face 4px low. Both centre on the strip's true middle now; and the two
   pictures of a pair are cut to one aspect, so they are one height. */
#s2[data-cast="profile"] .board-frame:has(.theirs) > .total {
  bottom: calc(-1 * var(--bd-b) - var(--table-gap) - var(--gap) / 2 - var(--mask) * var(--stage-aspect, 2.236) / 2);
}
#s2[data-cast="profile"] .board-frame:has(.mine) > .total {
  top: calc(-1 * var(--bd-top) - var(--table-gap) - var(--gap) / 2 - var(--mask) * var(--stage-aspect, 2.236) / 2);
}
/* THE ANGLED FACES (founder, 2026-09-06: "make a version with these 2
   angled masks instead of the side profile ones"). The pair turned
   forty-five degrees: whole oval plates, each with its turned rim to the
   screen's edge and its face to the arena, flush with the edge (the
   columns stand 2px in from the wrap, so the plate steps 2px out); the
   blue at the far left, the green at the near right; one height, centred
   on the strip's true middle; the dice from the mouth's own place on the
   face. A plate 104px wide is 155px tall on the phone, and an oval that
   wide has tapered to nothing where the cells begin, so no face touches
   a cell. */
#s2[data-cast="angled"] { --mask: min(104px, 26.7vw); --gap: 80px; }
#s2[data-cast="angled"] .total .mouth { transform: var(--mouth-flip, none); }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: -2px; right: auto; }
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: -2px; left: auto; }
#s2[data-cast="angled"] .board-frame:has(.theirs) > .total {
  bottom: calc(-1 * var(--bd-b) - var(--table-gap) - var(--gap) / 2 - var(--mask) * var(--stage-aspect, 1.49) / 2);
}
#s2[data-cast="angled"] .board-frame:has(.mine) > .total {
  top: calc(-1 * var(--bd-top) - var(--table-gap) - var(--gap) / 2 - var(--mask) * var(--stage-aspect, 1.49) / 2);
}
#s2[data-cast="angled"] .board-frame > .total { height: calc(var(--mask) * var(--stage-aspect, 1.49) + 6px + var(--pfw) * 1.05); }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { bottom: calc(var(--mask) * var(--stage-aspect, 1.49) + 6px); }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-shield { bottom: calc(var(--mask) * var(--stage-aspect, 1.49) + 6px + var(--pfw) * .47); }
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: calc(var(--mask) * var(--stage-aspect, 1.49) + 6px); }
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-shield { top: calc(var(--mask) * var(--stage-aspect, 1.49) + 6px + var(--pfw) * .47); }
body:has(#s2[data-cast="angled"].on) #wrap { overflow-x: clip; }
/* THE BANDS IN WHOLE PIXELS (founder, 2026-09-06: the faces "exactly ...
   at the same height"). The top band came out 32.29px and the bottom band
   39.98px, and the engine draws a fractional border floored -- 32 and 39
   -- while the faces' anchors added the unrounded numbers, so the far
   face sat a pixel low and the near a third of a pixel high. The bands
   are rounded down where they are defined, so the number the stylesheet
   adds is the number that is drawn, whatever the engine's snapping. An
   engine without round() keeps the fractional bands and that one pixel,
   never a missing border. */
@supports (width: round(down, 1.5px, 1px)) {
  #s2 .board-frame {
    --bd-top: round(down, calc(var(--rim-cell, var(--cell)) * var(--leather-k, 0.385)), 1px);
    --bd-b:   round(down, calc(var(--bd-top) * var(--rim-b-far, 1)), 1px);
  }
  #s2 .board-frame:has(.theirs) { border-bottom-width: var(--bd-b); }
  #s2 .board-frame:has(.mine) {
    --bd-b: round(down, calc(var(--bd-top) * var(--rim-b-near, 1)), 1px);
    border-bottom-width: var(--bd-b);
  }
  #s2 .board-frame:has(.mine)::before { border-bottom-width: var(--bd-b); }
}
/* THE ANGLED FACES SMALLER (founder, 2026-09-06: "the masks are too big,
   they intrude on the leather boards"). Seventy-six wide, 114 tall, and
   the fitter keeps the boards clear of them (sizeCells: the faces' room),
   so a plate stands in the margin beside the boards' corners and its
   paint never reaches the leather. */
#s2[data-cast="angled"] { --mask: min(76px, 19.5vw); }
/* THE ANGLED FACES A FIFTH LARGER (founder, 2026-09-06: "make the angled
   masks like 20% larger"): ninety-one wide, 136 tall for the painted
   pair, 122 for the stone; the fitter keeps the boards clear of them. */
#s2[data-cast="angled"] { --mask: min(91px, 23.4vw); }
/* THE FACES A STEP OUTWARD (founder, 2026-09-06: "move the faces like
   5-10px outwards, so their bezel edge goes out the picture. Then make
   the boards 10% bigger"). Each plate stands 10px past the wrap's edge,
   its turned rim off the picture; the wrap clips it, and the fitter
   counts the step as room given back to the boards. */
#s2[data-cast="angled"] { --face-out: 10px; }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: calc(-2px - var(--face-out)); }
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: calc(-2px - var(--face-out)); }
/* THE CORNERS SWAPPED (founder, 2026-09-06: "My corner (aka the users'
   corner) is left lower. And the other guy's corner is upper right. so
   lets invert things. also left stone face is mine, the right face
   theirs"). Under the angled cast my column stands at the wrap's left
   edge -- the face on the gap's middle, my scroll and shield below it --
   and the champion's at the right, shield and scroll above his face;
   each face a step past the edge as before, each looking into the arena
   (the page dresses them so). His bubble opens leftward now. The
   profiles keep the clean board's sides. */
#s2[data-cast="angled"] .board-frame:has(.theirs) > .total { left: auto; right: calc(2px - var(--far-edge-r, 0px)); }
#s2[data-cast="angled"] .board-frame:has(.mine)   > .total { right: auto; left: calc(2px - var(--near-edge-l, 0px)); }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { left: auto; right: calc(-2px - var(--face-out)); }
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { right: auto; left: calc(-2px - var(--face-out)); }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth .speech { right: 94%; left: auto; transform-origin: 100% 18px; }
#s2[data-cast="angled"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth .speech::before {
  right: -7px; left: auto;
  border-left: none; border-right: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(-45deg);
}
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned]) .mouth .speech { left: 94%; right: auto; transform-origin: 0 18px; }
#s2[data-cast="angled"] :is(.board-frame:has(.mine), .total[data-turned]) .mouth .speech::before {
  left: -7px; right: auto;
  border-right: none; border-left: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(45deg);
}
/* THE DWARF CORNERS (founder, 2026-09-08: the PSDs in godofwarbutdwarf).
   Every layer is placed from the live face by its own PSD offset: the
   PSD's face is 366 wide, so one PSD pixel is --dw-s = --mask / 366 on
   the screen, and the corner scales with the face and nothing else. Mine
   (lower left): the ribbon and the pfp under the face, the construct
   over it, so the fingers close over the face. Theirs (upper right): the
   robe's continuation under the face, the pfp and the construct over.
   Over every cloth layer a tint layer: the leather's colour through the
   cloth's mask, blended as colour, so the folds keep their shading and
   the gold stays gold. The faces come in from the edge by --face-in so
   they show whole; the fitter still budgets the boards with --face-out,
   so the boards do not move. The live name stands on the ribbon; the
   shield says 1 (founder: "just write 1 on there"). */
#s2[data-corners="dwarf"] { --face-in: 14px; --dw-s: calc(var(--mask) / 366); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned])   .mouth { left: calc(-2px - var(--face-out) + var(--face-in)); z-index: 3; }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { right: calc(-2px - var(--face-out) + var(--face-in)); z-index: 3; }
#s2 .total .dw-construct, #s2 .total .dw-construct-tint, #s2 .total .dw-ribbon, #s2 .total .dw-ribbon-tint,
#s2 .total .dw-pfp, #s2 .total .dw-robe, #s2 .total .dw-robe-tint, #s2 .total .dw-level { display: none; }
#s2[data-corners="dwarf"] .total .dw-construct, #s2[data-corners="dwarf"] .total .dw-construct-tint,
#s2[data-corners="dwarf"] .total .dw-ribbon, #s2[data-corners="dwarf"] .total .dw-ribbon-tint,
#s2[data-corners="dwarf"] .total .dw-pfp, #s2[data-corners="dwarf"] .total .dw-robe, #s2[data-corners="dwarf"] .total .dw-robe-tint {
  display: block; position: absolute; pointer-events: none; background-repeat: no-repeat; background-size: 100% 100%;
}
#s2[data-corners="dwarf"] .total .dw-construct-tint, #s2[data-corners="dwarf"] .total .dw-ribbon-tint, #s2[data-corners="dwarf"] .total .dw-robe-tint {
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; mix-blend-mode: color;
}
/* mine */
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon { background-image: url("/static/art/corners/low_ribbon.webp?v=2"); z-index: 1; left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(325 * var(--dw-s))); top: calc(1339 * var(--dw-s)); width: calc(1069 * var(--dw-s)); height: calc(224 * var(--dw-s)); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon-tint { background-color: var(--tint-near); -webkit-mask-image: url("/static/art/corners/low_ribbon_mask.webp?v=2"); mask-image: url("/static/art/corners/low_ribbon_mask.webp?v=2"); z-index: 2; left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(325 * var(--dw-s))); top: calc(1339 * var(--dw-s)); width: calc(1069 * var(--dw-s)); height: calc(224 * var(--dw-s)); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-pfp { background-image: url("/static/art/corners/low_pfp.webp"); z-index: 3; left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(28 * var(--dw-s))); top: calc(1231 * var(--dw-s)); width: calc(326 * var(--dw-s)); height: calc(361 * var(--dw-s)); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-construct { background-image: url("/static/art/corners/low_construct.webp"); z-index: 4; left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(-17 * var(--dw-s))); top: calc(363 * var(--dw-s)); width: calc(446 * var(--dw-s)); height: calc(1245 * var(--dw-s)); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-construct-tint { background-color: var(--tint-near); -webkit-mask-image: url("/static/art/corners/low_construct_mask.webp"); mask-image: url("/static/art/corners/low_construct_mask.webp"); z-index: 5; left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(-17 * var(--dw-s))); top: calc(363 * var(--dw-s)); width: calc(446 * var(--dw-s)); height: calc(1245 * var(--dw-s)); }
/* theirs */
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-robe { background-image: url("/static/art/corners/up_robe.webp"); z-index: 1; right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(-2 * var(--dw-s))); bottom: calc(316 * var(--dw-s)); width: calc(361 * var(--dw-s)); height: calc(189 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-robe-tint { background-color: var(--tint-far); -webkit-mask-image: url("/static/art/corners/up_robe_mask.webp"); mask-image: url("/static/art/corners/up_robe_mask.webp"); z-index: 2; right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(-2 * var(--dw-s))); bottom: calc(316 * var(--dw-s)); width: calc(361 * var(--dw-s)); height: calc(189 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-pfp { background-image: url("/static/art/corners/up_pfp.webp"); z-index: 3; right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(20 * var(--dw-s))); bottom: calc(1205 * var(--dw-s)); width: calc(309 * var(--dw-s)); height: calc(362 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct { background-image: url("/static/art/corners/up_construct.webp?v=2"); z-index: 4; right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(-5 * var(--dw-s))); bottom: calc(338 * var(--dw-s)); width: calc(1247 * var(--dw-s)); height: calc(1249 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct-tint { background-color: var(--tint-far); -webkit-mask-image: url("/static/art/corners/up_construct_mask.webp?v=2"); mask-image: url("/static/art/corners/up_construct_mask.webp?v=2"); z-index: 5; right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(-5 * var(--dw-s))); bottom: calc(338 * var(--dw-s)); width: calc(1247 * var(--dw-s)); height: calc(1249 * var(--dw-s)); }
/* the live name on the ribbon, in the scroll's font; the shield's 1 */
#s2[data-corners="dwarf"] .total .pf-shield { display: none; }
#s2[data-corners="dwarf"] .total .pf-scroll { background: none; box-shadow: none; border: none; width: auto; height: auto; z-index: 6; overflow: visible; }
#s2[data-corners="dwarf"] .total .pf-scroll .who { position: static; inset: auto; padding: 0; display: block; white-space: nowrap; text-align: center; font-size: calc(101 * var(--dw-s)); }
#s2[data-corners="dwarf"] .total .dw-level {
  display: block; position: absolute; z-index: 6; pointer-events: none; white-space: nowrap;
  font: calc(63 * var(--dw-s)) / 1 var(--serif); color: #f0d27a; text-shadow: 0 1px 2px rgba(0, 0, 0, .75);
}
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(872.5 * var(--dw-s))); right: auto; top: calc(1445 * var(--dw-s)); bottom: auto; transform: translate(-50%, -50%); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-level { left: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(459 * var(--dw-s))); top: calc(1434 * var(--dw-s)); transform: translate(-50%, -50%); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(827 * var(--dw-s))); left: auto; bottom: calc(1481 * var(--dw-s)); top: auto; transform: translate(50%, 50%); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-level { right: calc(calc(-2px - var(--face-out) + var(--face-in)) + calc(459.5 * var(--dw-s))); bottom: calc(1486 * var(--dw-s)); transform: translate(50%, 50%); }
/* the lettering measured on the zoom grids (2026-09-08): the names in the
   scroll font at 109 PSD px, the shield's 1 at 84, the upper shield's
   number centred where the painted 65 was */
#s2[data-corners="dwarf"] .total .pf-scroll .who { font-size: calc(109 * var(--dw-s)); }
#s2[data-corners="dwarf"] .total .dw-level { font-size: calc(84 * var(--dw-s)); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { left: calc(-2px - var(--face-out) + var(--face-in) + calc(855 * var(--dw-s))); top: calc(1443 * var(--dw-s)); }
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-level { left: calc(-2px - var(--face-out) + var(--face-in) + calc(469 * var(--dw-s))); top: calc(1433 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { right: calc(-2px - var(--face-out) + var(--face-in) + calc(823.5 * var(--dw-s))); bottom: calc(1481 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-level { right: calc(-2px - var(--face-out) + var(--face-in) + calc(449 * var(--dw-s))); bottom: calc(1465 * var(--dw-s)); }
/* THE MEDALLION'S GROUND FOLLOWS THE LEATHER TOO (founder, 2026-09-08:
   "change the pfp inner bg color to match up nice"): the mosaic behind
   the animal is greyed in the art and tinted through its own mask, like
   the cloth; the animal, the gold rim and the star are left alone. */
#s2 .total .dw-pfp-tint { display: none; }
#s2[data-corners="dwarf"] .total .dw-pfp-tint {
  display: block; position: absolute; pointer-events: none; z-index: 3;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; mix-blend-mode: color;
}
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-pfp-tint { background-color: var(--tint-near); -webkit-mask-image: url("/static/art/corners/low_pfp_mask.webp"); mask-image: url("/static/art/corners/low_pfp_mask.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + calc(28 * var(--dw-s))); top: calc(1231 * var(--dw-s)); width: calc(326 * var(--dw-s)); height: calc(361 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-pfp-tint { background-color: var(--tint-far); -webkit-mask-image: url("/static/art/corners/up_pfp_mask.webp"); mask-image: url("/static/art/corners/up_pfp_mask.webp"); right: calc(-2px - var(--face-out) + var(--face-in) + calc(20 * var(--dw-s))); bottom: calc(1205 * var(--dw-s)); width: calc(309 * var(--dw-s)); height: calc(362 * var(--dw-s)); }
/* THE LEATHER'S OWN COLOUR ON THE CLOTH (founder, 2026-09-08: the
   constructs "add their own dark grey hue to the color"). The cloth's
   brightness is baked to a mid-grey average, and a hard-light blend of
   the leather's colour over it gives that colour exactly at the average,
   the folds darker, the highlights lighter. */
#s2[data-corners="dwarf"] .total .dw-construct-tint, #s2[data-corners="dwarf"] .total .dw-ribbon-tint,
#s2[data-corners="dwarf"] .total .dw-robe-tint, #s2[data-corners="dwarf"] .total .dw-pfp-tint { mix-blend-mode: hard-light; }
/* NEVER BRIGHTER THAN THE LEATHER (founder, 2026-09-08: "tone down the
   brightness of the color so that it never becomes unpleasant ... none of
   the leather variants are unpleasant"). The cloth's lit areas are baked
   to white and the leather's colour is multiplied in: the brightest fold
   is the leather itself, every other fold darker. */
#s2[data-corners="dwarf"] .total .dw-construct-tint, #s2[data-corners="dwarf"] .total .dw-ribbon-tint,
#s2[data-corners="dwarf"] .total .dw-robe-tint, #s2[data-corners="dwarf"] .total .dw-pfp-tint { mix-blend-mode: multiply; }

/* THE SECOND DWARFS, THE LILIES -- named DWARVEN TITANS by the founder, the
   first pair DWARVEN GODS (2026-09-09: lower_dwarf_2.psd
   and upper_dwarf_2.psd). Mine: the telamon kneels on a plinth and holds
   up a crescent tray the face stands in, the medallion on the plinth's
   foot, the ribbon with its crowned shield trailing right; the face is
   the lowest layer, the medallion over it, the construct (whose tray's
   rim and leaves close over the face's foot) over that, the ribbon on
   top. Theirs: a pilaster with a niche the dwarf crouches in, the face
   hung from the corbel under his hands, the ribbon and medallion at the
   capital; face under construct under medallion, and no robe layer. The
   pfps are the first pair's very pictures, so only their places change.
   The names in a bold roman in the leather's colour, as painted; the
   shields' numbers gold. Same --dw-s, same face-in; the switch's
   data-rig picks these placements over the first pair's. */
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .mouth, #s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { z-index: 1; }
/* mine */
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-pfp, #s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-pfp-tint { left: calc(-2px - var(--face-out) + var(--face-in) + 14 * var(--dw-s)); top: calc(1222 * var(--dw-s)); width: calc(326 * var(--dw-s)); height: calc(361 * var(--dw-s)); z-index: 2; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-construct { background-image: url("/static/art/corners/low2_construct.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + -4 * var(--dw-s)); top: calc(319 * var(--dw-s)); width: calc(430 * var(--dw-s)); height: calc(1263 * var(--dw-s)); z-index: 4; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-construct-tint { -webkit-mask-image: url("/static/art/corners/low2_construct_mask.webp"); mask-image: url("/static/art/corners/low2_construct_mask.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + -4 * var(--dw-s)); top: calc(319 * var(--dw-s)); width: calc(430 * var(--dw-s)); height: calc(1263 * var(--dw-s)); z-index: 5; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon { background-image: url("/static/art/corners/low2_ribbon.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + 377 * var(--dw-s)); top: calc(1290 * var(--dw-s)); width: calc(987 * var(--dw-s)); height: calc(301 * var(--dw-s)); z-index: 6; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon-tint { -webkit-mask-image: url("/static/art/corners/low2_ribbon_mask.webp"); mask-image: url("/static/art/corners/low2_ribbon_mask.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + 377 * var(--dw-s)); top: calc(1290 * var(--dw-s)); width: calc(987 * var(--dw-s)); height: calc(301 * var(--dw-s)); z-index: 7; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { left: calc(-2px - var(--face-out) + var(--face-in) + 905.5 * var(--dw-s)); top: calc(1460 * var(--dw-s)); z-index: 8; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll .who { font-size: calc(97.1 * var(--dw-s)); font-weight: 700; color: var(--tint-near); color: color-mix(in srgb, var(--tint-near) 82%, #000); }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-level { left: calc(-2px - var(--face-out) + var(--face-in) + 598 * var(--dw-s)); top: calc(1446 * var(--dw-s)); z-index: 8; font-size: calc(76.4 * var(--dw-s)); font-weight: 700; }
/* theirs */
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-robe, #s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-robe-tint { display: none; }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct { background-image: url("/static/art/corners/up2_construct.webp"); right: calc(-2px - var(--face-out) + var(--face-in) + -16 * var(--dw-s)); bottom: calc(324 * var(--dw-s)); width: calc(1298 * var(--dw-s)); height: calc(1261 * var(--dw-s)); z-index: 4; }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct-tint { -webkit-mask-image: url("/static/art/corners/up2_construct_mask.webp"); mask-image: url("/static/art/corners/up2_construct_mask.webp"); right: calc(-2px - var(--face-out) + var(--face-in) + -16 * var(--dw-s)); bottom: calc(324 * var(--dw-s)); width: calc(1298 * var(--dw-s)); height: calc(1261 * var(--dw-s)); z-index: 5; }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-pfp, #s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-pfp-tint { right: calc(-2px - var(--face-out) + var(--face-in) + 8 * var(--dw-s)); bottom: calc(1211 * var(--dw-s)); width: calc(309 * var(--dw-s)); height: calc(362 * var(--dw-s)); z-index: 6; }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { right: calc(-2px - var(--face-out) + var(--face-in) + 812.5 * var(--dw-s)); bottom: calc(1469 * var(--dw-s)); z-index: 8; }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll .who { font-size: calc(88.6 * var(--dw-s)); font-weight: 700; color: var(--tint-far); color: color-mix(in srgb, var(--tint-far) 82%, #000); }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-level { right: calc(-2px - var(--face-out) + var(--face-in) + 538 * var(--dw-s)); bottom: calc(1477 * var(--dw-s)); z-index: 8; font-size: calc(73.6 * var(--dw-s)); font-weight: 700; }
/* the lilies' names in the page's roman, bold, as they were painted (the first pair keeps the scroll's hand) */
#s2[data-corners="dwarf"][data-rig="2"] .total .pf-scroll .who { font-family: var(--serif); letter-spacing: 0; }
/* THE PLAYER'S BANNER LOWERED (founder, 2026-09-09: "the lower rig name
   banner intrudes on the leather and it confuses the row-sum numbers";
   on a 4x4 its top reached the near board's foot). Gods: the ribbon, the
   name and the shield's number drop by --banner-drop PSD px (48 = 12
   screen px at the 91px face). Titans: the same by 56; and since the
   ribbon's drapery tail continues in the construct, hanging from the
   plinth along the medallion's ring into the ribbon's curl, that tail is
   cut into its own layer under the construct (.dw-robe, the under-cloth)
   and lengthened by the same 56 rows in the art, so it still reaches the
   lowered curl. The medallions stay where they are: at a 745-tall Pro
   Max the Titans' ring has six pixels to the bar. Changing the Titans'
   drop means re-running build_dwarf2_art.py with TAIL_DROP. */
#s2[data-corners="dwarf"][data-rig="1"] { --banner-drop: 48; }
#s2[data-corners="dwarf"][data-rig="2"] { --banner-drop: 56; }
#s2[data-corners="dwarf"][data-rig="1"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon, #s2[data-corners="dwarf"][data-rig="1"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon-tint { top: calc((1339 + var(--banner-drop)) * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="1"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: calc((1443 + var(--banner-drop)) * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="1"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-level { top: calc((1433 + var(--banner-drop)) * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="1"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-robe, #s2[data-corners="dwarf"][data-rig="1"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-robe-tint { display: none; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon, #s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-ribbon-tint { top: calc((1290 + var(--banner-drop)) * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { top: calc((1460 + var(--banner-drop)) * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-level { top: calc((1446 + var(--banner-drop)) * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-robe { background-image: url("/static/art/corners/low2_tail.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + 207 * var(--dw-s)); top: calc(1225 * var(--dw-s)); width: calc(218 * var(--dw-s)); height: calc(351 * var(--dw-s)); z-index: 3; }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .dw-robe-tint { background-color: var(--tint-near); -webkit-mask-image: url("/static/art/corners/low2_tail_mask.webp"); mask-image: url("/static/art/corners/low2_tail_mask.webp"); left: calc(-2px - var(--face-out) + var(--face-in) + 207 * var(--dw-s)); top: calc(1225 * var(--dw-s)); width: calc(218 * var(--dw-s)); height: calc(351 * var(--dw-s)); z-index: 3; }
/* THE SCORE FLAPS (founder, 2026-09-10: broad-score-flaps.psd). A stitched
   leather tab at mid-height on the upper board's left and on the lower
   board's right, whichever rigs are on, carrying "score:" and the live
   total. Each is sized and placed as in the founder's picture, as a share
   of the plate it hangs from (the reference board's plate, roll included,
   is 887 x 850 there; the left flap 248 x 280, the right 259 x 279, both a hair
   under the plate's edge), so it follows the board through every cell
   size. The tab is the board's own leather: the paint greyed by hue and
   the leather's tint multiplied in through its mask, the yellow thread
   and the cream untouched. The lettering's ink is --ink-far / --ink-near,
   chosen by the leather's brightness in applySkins; the column sums on
   the leather take the same ink. */
#s2 .board-frame .flap {
  position: absolute; z-index: -1; pointer-events: none; isolation: isolate;
  width: var(--flap-w); height: var(--flap-h);
  background-repeat: no-repeat; background-size: 100% 100%;
}
#s2 .board-frame .flap::before {
  content: ""; position: absolute; inset: 0; mix-blend-mode: multiply;
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
}
#s2 .board-frame:has(.theirs) .flap {
  /* THE FAR FLAP CLOSER TO THE ROLL (founder, 2026-09-10, twice: "a visible
     gap there", then "just move the upper score flap closer to the roll").
     The plates' roll side carries 15 transparent px of its 263-px slice
     (5.7% of the left band) and a shadowed edge after that, so the leather
     begins inside its own box; the tab slides --tuck under the box's edge,
     the roll over it. Its lettering is centred on the whole tab, as the near
     flap's is (founder, 2026-09-10: "bring the font more towards the roll,
     so it looks centered on the flap"). */
  --tuck: calc(var(--plate-bl) * 0.057 + 3px);
  --flap-w: calc(var(--plate-w) * 0.2796); --flap-h: calc(var(--plate-h) * 0.3294);
  left: calc(-1 * var(--plate-bl) + var(--tuck) - var(--flap-w)); top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3012);
  background-image: url("/static/art/corners/flap_left.webp");
}
#s2 .board-frame:has(.theirs) .flap::before { background-color: var(--tint-far); -webkit-mask-image: url("/static/art/corners/flap_left_mask.webp"); mask-image: url("/static/art/corners/flap_left_mask.webp"); }
#s2 .board-frame:has(.mine) .flap {
  --flap-w: calc(var(--plate-w) * 0.2920); --flap-h: calc(var(--plate-h) * 0.3282);
  left: calc(var(--plate-w) - var(--plate-bl) - var(--plate-w) * 0.0056); top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000);
  background-image: url("/static/art/corners/flap_right.webp");
}
#s2 .board-frame:has(.mine) .flap::before { background-color: var(--tint-near); -webkit-mask-image: url("/static/art/corners/flap_right_mask.webp"); mask-image: url("/static/art/corners/flap_right_mask.webp"); }
/* the lettering, as painted: "score:" small over the number, both centred */
#s2 .board-frame .flap .flap-label, #s2 .board-frame .flap .flap-n {
  position: absolute; left: 0; right: 0; text-align: center; transform: translateY(-50%);
  font-family: var(--serif); line-height: 1; white-space: nowrap; font-weight: normal;
}
#s2 .board-frame .flap .flap-label { top: 23%; font-size: calc(var(--flap-h) * 0.2); font-style: normal; letter-spacing: .02em; }
#s2 .board-frame .flap .flap-n { top: 58%; font-size: calc(var(--flap-h) * 0.44); }
/* the label a few pixels higher than the number, the number a little smaller,
   and smaller again at three digits so 384, the most a board can hold, sits
   in the tab with room (founder, 2026-09-10) */
#s2 .board-frame .flap .flap-n[data-len="3"] { font-size: calc(var(--flap-h) * 0.36); }
/* A MAGIC POOF (founder, 2026-09-15: "a magic poof! Cloud like thing and
   the new score appears"). Four lobes of light on the figure's own
   ::after, drawn in ems of its type so the cloud is the right size on any
   tongue; the figure ducks out under it and the new one steps out as it
   thins. Gold at the heart, parchment at the edge -- the page owns no
   other palette. */
/* no position is set here on purpose: the figure is absolutely placed on
   its tongue already, which makes it the containing block its cloud needs,
   and saying "relative" knocked it out of that placement and down to the
   tongue's corner */
#s2 .board-frame .flap .flap-n.poofing::after {
  content: ""; position: absolute; left: 50%; top: 52%; width: 2.9em; height: 2.6em;
  pointer-events: none; z-index: 2; filter: blur(1.4px);     /* softer: it is a cloud, not four circles (founder, 2026-09-15: "its kinda ugly") */
  background:
    radial-gradient(closest-side circle at 34% 60%, rgba(255, 250, 233, .72), rgba(255, 244, 214, 0) 78%),
    radial-gradient(closest-side circle at 68% 46%, rgba(255, 247, 224, .7), rgba(255, 240, 205, 0) 76%),
    radial-gradient(closest-side circle at 50% 30%, rgba(255, 252, 241, .66), rgba(255, 246, 220, 0) 74%),
    radial-gradient(closest-side circle at 46% 76%, rgba(250, 236, 197, .64), rgba(250, 235, 195, 0) 78%),
    radial-gradient(closest-side circle at 50% 52%, rgba(255, 224, 152, .42), rgba(255, 226, 150, 0) 70%);
  animation: score-poof 940ms cubic-bezier(.25, .6, .3, 1) both;
}
/* THE FIGURE KEEPS ITS OWN PLACE (founder, 2026-09-15: "why does the new
   number come from below and in such a staggered motion?"). It is centred
   on its line by transform: translateY(-50%), and a keyframe that names
   `transform` replaces that -- so the figure fell half its own height and
   sprang back up, which is what he saw. Every frame below carries the
   translate, and the spring is gone with it: the figure only fades. */
#s2 .board-frame .flap .flap-n.leaving  { animation: score-vanish 330ms ease-in both; }
#s2 .board-frame .flap .flap-n.arriving { animation: score-appear 460ms cubic-bezier(.3, .6, .35, 1) both; }
@keyframes score-poof {
  0%   { transform: translate(-50%, -50%) scale(.3); opacity: 0; }
  20%  { opacity: .9; }
  55%  { transform: translate(-50%, -50%) scale(1.1); opacity: .8; }
  100% { transform: translate(-50%, -50%) scale(1.5); opacity: 0; }
}
@keyframes score-vanish {
  from { opacity: 1; transform: translateY(-50%) scale(1); }
  to   { opacity: 0; transform: translateY(-50%) scale(.9); }
}
@keyframes score-appear {
  from { opacity: 0; transform: translateY(-50%) scale(.96); }
  to   { opacity: 1; transform: translateY(-50%) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  #s2 .board-frame .flap .flap-n.poofing::after { animation: none; display: none; }
  #s2 .board-frame .flap .flap-n.leaving, #s2 .board-frame .flap .flap-n.arriving { animation: none; }
}
#s2 .board-frame:has(.theirs) .flap .flap-label, #s2 .board-frame:has(.theirs) .flap .flap-n { color: var(--ink-far, #f1e4c4); text-shadow: var(--ink-far-shadow, 0 1px 1px rgba(0, 0, 0, .6)); }
#s2 .board-frame:has(.mine) .flap .flap-label, #s2 .board-frame:has(.mine) .flap .flap-n { color: var(--ink-near, #f1e4c4); text-shadow: var(--ink-near-shadow, 0 1px 1px rgba(0, 0, 0, .6)); }
/* the column sums on the leather, in the same ink (founder, 2026-09-10) */
#s2 .board-frame:has(.theirs) .colsum, #s2 .board-frame:has(.theirs) .col.cemented .colsum { color: var(--ink-far, #f2dfae); text-shadow: var(--ink-far-shadow, 0 1px 1px rgba(0, 0, 0, .6)); }
#s2 .board-frame:has(.mine) .colsum, #s2 .board-frame:has(.mine) .col.cemented .colsum { color: var(--ink-near, #f2dfae); text-shadow: var(--ink-near-shadow, 0 1px 1px rgba(0, 0, 0, .6)); }
/* THE SUMS OFF THE SEAM (founder, 2026-09-10: "the column totals should be a
   pixel or two higher, so that they clear the seam on the leather"): the
   band's stitching runs under the numbers' feet; two pixels up clears it. */
#s2 .board-frame .colsum { translate: 0 -2px; }
/* on a 4x4 the far flap goes in two pixels more (founder, 2026-09-10: "on the
   3x3 its perfect. on the 4x4 it still needs to go in 2 px") */
#s2[data-cols="4"] .board-frame:has(.theirs) .flap { --tuck: calc(var(--plate-bl) * 0.057 + 5px); }
/* THE POWER PINS, AND THE NEAR ROLL BACK ON THE RIGHT (founder, 2026-09-10:
   "I see these as round pins on the leather rolls. That would mean of course
   moving the bottom roll to the right. We should test this and see how it
   looks"). Both arrangements sit behind the skins menu's "the rolls" dial.

   OPPOSED. The plate is painted with its roll on the left, so the near board
   wears it mirrored on the pseudo-element that has been waiting in this file
   since 2026-09-03: the frame keeps a transparent border for layout with its
   side widths swapped, the pseudo-element wears the plate with the original
   widths and a horizontal flip. What is new is that the frame is then shoved
   right by the difference between the two bands, so the GRIDS still stand
   exactly over each other -- the founder's other ruling (2026-09-06, "they
   both sit perfectly center") kept while the rolls oppose. The face does not
   move: the padding box lands where it always did.

   THE PINS. Three to a board, set into its own roll, so each player's powers
   are on their own leather: his on the left band, mine on whichever side my
   roll is. The hide inside the gold ring is masked and takes the board's
   tint like the flaps and the cloth; the gold is left alone. A spent power
   wears the lead-grey plate. They are placeholders until the mechanic is
   ruled: no power is named, and nothing is bound to them. */
#s2 .board-frame { --pin: min(34px, calc(var(--bd-left) * 0.82)); }
#s2[data-rolls="opposed"] .board-frame:has(.mine) { --pin: min(34px, calc(var(--bd-right) * 0.82)); }
#s2 .board-frame .pin {
  position: absolute; z-index: 2; width: var(--pin); height: var(--pin);
  background-image: url("/static/art/corners/pin.webp");
  background-repeat: no-repeat; background-size: 100% 100%;
  border: 0; padding: 0; isolation: isolate;
}
#s2 .board-frame .pin::after {
  content: ""; position: absolute; inset: 0; mix-blend-mode: multiply;
  -webkit-mask-image: url("/static/art/corners/pin_mask.webp"); mask-image: url("/static/art/corners/pin_mask.webp");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
}
#s2 .board-frame .pin[data-spent] { background-image: url("/static/art/corners/pin_spent.webp"); }
#s2 .board-frame .pin[data-spent]::after { display: none; }
#s2 .board-frame:has(.theirs) .pin::after { background-color: var(--tint-far); }
#s2 .board-frame:has(.mine)   .pin::after { background-color: var(--tint-near); }
#s2 .board-frame .pin[data-slot="1"] { top: calc(18% - var(--pin) / 2); }
#s2 .board-frame .pin[data-slot="2"] { top: calc(50% - var(--pin) / 2); }
#s2 .board-frame .pin[data-slot="3"] { top: calc(82% - var(--pin) / 2); }
/* his roll is the left band; a pin sits in the middle of it, a shade inward
   so the score flap's tuck never crosses it */
#s2 .board-frame:has(.theirs) .pin { left: calc(-1 * var(--bd-left) + (var(--bd-left) - var(--pin)) / 2 + 3px); }
#s2[data-rolls="left"] .board-frame:has(.mine) .pin { left: calc(-1 * var(--bd-left) + (var(--bd-left) - var(--pin)) / 2 + 3px); }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .pin { right: calc(-1 * var(--bd-right) + (var(--bd-right) - var(--pin)) / 2 + 3px); left: auto; }
/* ---- opposed: the near plate mirrored, the grids still aligned ---- */
#s2[data-rolls="opposed"] .board-frame:has(.mine) {
  --bd-left:  calc(var(--bd-top) * var(--rim-r-near, .909));   /* the thin edge, now on the left */
  --bd-right: calc(var(--bd-top) * var(--rim-l-near, .909));   /* the roll, now on the right */
  border-width: var(--bd-top) var(--bd-right) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-left);
  border-image: none; border-color: transparent;
  isolation: isolate;
  left: calc(var(--bd-right) - var(--bd-left));
}
#s2[data-rolls="opposed"] .board-frame:has(.mine)::before {
  display: block;
  border-width: var(--bd-top) var(--bd-left) calc(var(--bd-top) * var(--rim-b-near, 1)) var(--bd-right);
  transform: scaleX(-1);
}
/* the flap tucks under whatever it meets: the thin edge, or now the roll */
#s2[data-rolls="opposed"] .board-frame:has(.mine) .flap { z-index: -2; }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .flap {
  --tuck: calc(var(--plate-br) * 0.057 + 3px);
  left: calc(var(--plate-w) - var(--plate-bl) - var(--tuck));
}
/* THE PINS OFF THE LEATHER (founder, 2026-09-10: "I dont really like the
   pins there, they make the area too busy"). They keep their shape and
   leave the board. Mine seat in the bar beside CAST, where an action
   belongs and a 40px target is honest. His stack in his own corner above
   his score flap, small: they are read, never tapped. The board's leather
   is left to the board again. */
#s2 .board-frame:has(.mine) .pin { display: none; }
#s2 .board-frame:has(.theirs) {
  --flap-w: calc(var(--plate-w) * 0.2796);
  --tuck: calc(var(--plate-bl) * 0.057 + 3px);
  --flap-top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3012);
}
#s2 .board-frame:has(.theirs) .pin {
  --pin: 22px;
  left: calc(-1 * var(--plate-bl) + var(--tuck) - var(--flap-w) / 2 - var(--pin) / 2);
}
#s2 .board-frame:has(.theirs) .pin[data-slot="3"] { top: calc(var(--flap-top) - 10px - var(--pin)); }
#s2 .board-frame:has(.theirs) .pin[data-slot="2"] { top: calc(var(--flap-top) - 16px - var(--pin) * 2); }
#s2 .board-frame:has(.theirs) .pin[data-slot="1"] { top: calc(var(--flap-top) - 22px - var(--pin) * 3); }
/* mine, in the bar */
/* its own room in the bar's row: it keeps its width and the row wraps
   rather than letting the pins ride over AUTO */
#s2 #bar > #powers { order: 1; flex: 0 0 auto; }
#bar #powers { display: flex; align-items: center; justify-content: center; gap: 7px; padding: 0 6px; }
#bar #powers[hidden] { display: none; }
#bar #powers .pin {
  width: 40px; height: 40px; padding: 0; border: 0; background: none; cursor: pointer;
  background-image: url("/static/art/corners/pin.webp");
  background-repeat: no-repeat; background-size: 100% 100%;
  position: relative; isolation: isolate; flex: 0 0 auto;
}
#bar #powers .pin::after {
  content: ""; position: absolute; inset: 0; mix-blend-mode: multiply;
  background-color: var(--tint-near, #2c3f6e);
  -webkit-mask-image: url("/static/art/corners/pin_mask.webp"); mask-image: url("/static/art/corners/pin_mask.webp");
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%;
}
#bar #powers .pin[data-spent] { background-image: url("/static/art/corners/pin_spent.webp"); cursor: default; }
#bar #powers .pin[data-spent]::after { display: none; }
/* THE PINS IN EACH PLAYER'S OWN CORNER (founder, 2026-09-10: the roll
   "serves more of an aesthetic role, to showcase the nice leather"). So
   neither the leather nor the bar: the bar cannot hold three of them
   beside CAST on a 393-wide screen without crushing AUTO. They stack in
   the parchment margin of each player's own column, in line with that
   player's score flap: his above his, mine below mine. Same size on both
   sides; mine are the tappable ones. */
#bar #powers { display: none !important; }
#s2 .board-frame:has(.theirs) .pin { --pin: 28px; }
#s2[data-rolls] .board-frame:has(.mine) .pin { --pin: 28px; display: block; }
#s2 .board-frame:has(.mine) {
  --flap-w: calc(var(--plate-w) * 0.2920);
  --flap-bottom: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.6294);
  --flap-mid: calc(var(--plate-w) - var(--plate-bl) - var(--plate-w) * 0.0056 + var(--flap-w) / 2);
}
#s2[data-rolls] .board-frame:has(.mine) .pin { left: calc(var(--flap-mid) - var(--pin) / 2); right: auto; }
#s2[data-rolls] .board-frame:has(.mine) .pin[data-slot="1"] { top: calc(var(--flap-bottom) + 6px); }
#s2[data-rolls] .board-frame:has(.mine) .pin[data-slot="2"] { top: calc(var(--flap-bottom) + 6px + var(--pin) + 4px); }
#s2[data-rolls] .board-frame:has(.mine) .pin[data-slot="3"] { top: calc(var(--flap-bottom) + 6px + var(--pin) * 2 + 8px); }
#s2 .board-frame:has(.theirs) .pin[data-slot="3"] { top: calc(var(--flap-top) - 6px - var(--pin)); }
#s2 .board-frame:has(.theirs) .pin[data-slot="2"] { top: calc(var(--flap-top) - 10px - var(--pin) * 2); }
#s2 .board-frame:has(.theirs) .pin[data-slot="1"] { top: calc(var(--flap-top) - 14px - var(--pin) * 3); }
/* THE ROUND FUSE (founder, 2026-09-10: "a burning rope, exactly like
   hearthstone does it, right under the top board for the opponent and right
   over the lower board for the player. it enters the middle of the leather
   roll", and then: "id go more with a sparkle than a flame"). The rope's far
   end is buried in the middle of the roll's band and runs out along the
   board's inner edge to the plate's other corner. A spark sits at the free
   end and rides back toward the roll while the rope shortens under it, a
   charred stub always just behind it; when the spark reaches the leather the
   round is out. The rope is only the round's last ten seconds, laid late. Three frames of spark flicker under it. The guide line
   stands aside while a rope burns, since the near rope lies across its row. */
/* ON THE BEZEL, AND THIN (founder, 2026-09-11: "a lot thinner than that and
   directly on the leather bezel"). A cord of seven pixels laid along the
   plate's own band: on my board's top band, on his board's bottom band, on
   the strip between the stitched seam and the rounded rim, so the column
   sums inside the seam stay clear of it.

   Placed as a SHARE OF THE BORDER, not a distance from the box edge: a
   quarter of every plate's border is transparent margin, and it scales with
   the board, so a fixed inset that sat on the leather at one width hung
   below it at another (seen in a real window at Pro Max width, 2026-09-11).

   Unlit it is invisible, not display:none, so its rope, ash and spark are
   fetched with the board rather than in the first rope's opening moments. */
#s2 .board-frame .fuse {
  position: absolute; z-index: 3; height: 7px; pointer-events: none; visibility: hidden;
  left: calc(-1 * var(--bd-left) / 2); right: calc(-1 * var(--bd-right));
}
#s2[data-fuse="off"] .board-frame .fuse { display: none !important; }
#s2 .board-frame .fuse.lit { visibility: visible; }
/* and when the spark reaches the leather, the whole thing goes out rather
   than sitting there lit on a round that is already over */
#s2 .board-frame .fuse.lit { animation: fuse-out 420ms linear calc(var(--fuse-ms, 10000ms) + var(--fuse-in, 0ms)) forwards; }
@keyframes fuse-out { from { opacity: 1; } to { opacity: 0; } }
#s2 .board-frame:has(.theirs) .fuse { top: calc(100% + var(--bd-b, var(--bd-top)) * 0.545); }
#s2 .board-frame:has(.mine)   .fuse { bottom: calc(100% + var(--bd-top) * 0.55); }
/* opposed rolls: the near rope enters from the right instead */
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse {
  left: calc(-1 * var(--bd-left)); right: calc(-1 * var(--bd-right) / 2);
}
#s2 .board-frame .fuse .rope {
  position: absolute; left: 0; top: 0; bottom: 0; width: 100%;
  background-image: url("/static/art/corners/fuse_rope.webp");
  background-repeat: repeat-x; background-size: auto 100%;
  /* the buried end: the cord fades out over the roll's own width rather
     than stopping in mid-air, so it reads as going into the leather */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px);
  mask-image: linear-gradient(to right, transparent 0, #000 20px);
}
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse .rope {
  -webkit-mask-image: linear-gradient(to left, transparent 0, #000 20px);
  mask-image: linear-gradient(to left, transparent 0, #000 20px);
}
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse .rope { left: auto; right: 0; }
#s2 .board-frame .fuse .ash {
  position: absolute; right: 0; top: 0; bottom: 0; width: 12px;
  background-image: url("/static/art/corners/fuse_ash.webp");
  background-repeat: no-repeat; background-size: 100% 100%;
}
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse .ash { right: auto; left: 0; transform: scaleX(-1); }
#s2 .board-frame .fuse .spark {
  position: absolute; left: 100%; top: 50%; width: 26px; height: 26px; margin: -13px 0 0 -13px;
  background-image: url("/static/art/corners/fuse_spark.webp");
  background-repeat: no-repeat; background-size: 300% 100%; background-position-x: 0%;
}
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse .spark { left: auto; right: 100%; margin: -13px -13px 0 0; }
/* --fuse-in is negative when a view arrives with the rope already part
   burnt: the animation starts that far in rather than from the beginning */
#s2 .board-frame .fuse.lit .rope  { animation: fuse-burn var(--fuse-ms, 10000ms) linear var(--fuse-in, 0ms) forwards; }
#s2 .board-frame .fuse.lit .spark { animation: fuse-ride var(--fuse-ms, 10000ms) linear var(--fuse-in, 0ms) forwards,
                                               fuse-flicker 240ms steps(3) infinite; }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse.lit .spark {
  animation: fuse-ride-r var(--fuse-ms, 10000ms) linear var(--fuse-in, 0ms) forwards, fuse-flicker 240ms steps(3) infinite;
}
/* snuffed: the burn and the spark hold where they are while the whole
   thing fades (the fade itself is scripted, from the current opacity).
   After the animation shorthands above, which would otherwise reset the
   play state to running. */
#s2 .board-frame .fuse.lit.out .rope, #s2 .board-frame .fuse.lit.out .spark { animation-play-state: paused !important; }
/* held: the server's beat for a move's cinematic; the whole rope -- its
   burn, its spark and its end fade -- stands still and then burns on */
#s2 .board-frame .fuse.lit.held, #s2 .board-frame .fuse.lit.held .rope, #s2 .board-frame .fuse.lit.held .spark { animation-play-state: paused !important; }
@keyframes fuse-burn    { from { width: 100%; } to { width: 0%; } }
/* fuse-ride and fuse-ride-r: the spark's track, on the compositor -- see
   THE ROPE BURNS ON THE COMPOSITOR, at the end of this sheet */
@keyframes fuse-flicker { from { background-position-x: 0%; } to { background-position-x: 150%; } }
#s2.fuse-lit #sysLine { opacity: 0; transition: opacity .25s; }
@media (prefers-reduced-motion: reduce) {
  #s2 .board-frame .fuse.lit .spark { animation: fuse-ride var(--fuse-ms, 10000ms) linear forwards; }
}
/* the round's length wears digits rather than a device, so the glyph is
   set in the page's own roman at the size the other dials' marks read at */
#setupRow2 #timeCycle .glyph { font-family: var(--serif); font-size: 21px; letter-spacing: .02em; }
/* THE BANK (backend, 2026-09-11): each seat's chess time, drawn only where
   the table plays on one, in that player's own corner column beside their
   score flap -- his below it, mine above it, so both sit against the flap.
   The seat on the clock wears the brighter ink. */
#s2 .board-frame .bank {
  position: absolute; z-index: 3; width: 62px; text-align: center;
  font: 15px/18px var(--serif); letter-spacing: .04em;
  color: rgba(70, 52, 28, .62); pointer-events: none;
}
#s2 .board-frame .bank[hidden] { display: none; }
#s2 .board-frame .bank.running { color: #6a3418; text-shadow: 0 1px 0 rgba(255, 248, 230, .5); }
#s2 .board-frame:has(.theirs) .bank {
  left: calc(-1 * var(--plate-bl) + var(--tuck) - var(--flap-w) / 2 - 31px);
  top: calc(var(--flap-top) + var(--plate-h) * 0.3294 + 7px);
}
#s2 .board-frame:has(.mine) .bank {
  left: calc(var(--flap-mid) - 31px);
  /* on the parchment above the flap (whose top is plate-h * 0.3 in from
     the outer edge), clear of it by 42px, the round digits between */
  top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 58px);
}
/* THE ROUND IN DIGITS (founder, 2026-09-11): m:ss to the deadline on the
   acting seat's corner, under the bank when chess time shows one, in its
   place when not. Same clock as the rope, so the eye can check the two. */
#s2 .board-frame .round {
  position: absolute; z-index: 3; width: 62px; text-align: center;
  font: 13px/16px var(--serif); letter-spacing: .08em; font-variant-numeric: tabular-nums;
  color: #6a3418; text-shadow: 0 1px 0 rgba(255, 248, 230, .5); pointer-events: none;
}
#s2 .board-frame .round[hidden] { display: none; }
#s2 .board-frame:has(.theirs) .round {
  left: calc(-1 * var(--plate-bl) + var(--tuck) - var(--flap-w) / 2 - 31px);
  top: calc(var(--flap-top) + var(--plate-h) * 0.3294 + 28px);
}
#s2 .board-frame:has(.mine) .round {
  left: calc(var(--flap-mid) - 31px);
  top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 36px);   /* 20px above the flap's top edge */
}
#s2 .board-frame:has(.theirs) .bank { top: calc(var(--flap-top) + var(--plate-h) * 0.3294 + 8px); }
#s2 .board-frame:has(.theirs):has(.bank[hidden]) .round { top: calc(var(--flap-top) + var(--plate-h) * 0.3294 + 8px); }

/* AND THE NEAR CLOCK CLEARS THE HALF PLATE (founder, 2026-09-15: "yes,
   move it a bit"). It stood twenty pixels above its own score tab, which
   was empty parchment while the dwarves held the plates in the band. At
   the focus stops the far plate is pushed to that very edge and the digits
   came to rest against its chin, so the pair drops sixteen pixels and sits
   just above the tab instead. Below the tab is the pins' ground. */
#s2[data-focus="faces"] .board-frame:has(.mine) .round,
#s2[data-focus="on"] .board-frame:has(.mine) .round { top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 20px); }
#s2[data-focus="faces"] .board-frame:has(.mine) .bank,
#s2[data-focus="on"] .board-frame:has(.mine) .bank { top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 42px); }

/* ===========================================================================
   FOCUS (founder, 2026-09-11): the action alone. The corner blocks -- rig,
   face, name band, shield, medal -- and the pins are not displayed; the
   column's padding goes; the far board's tongue wears the near one's cut
   and place, on the right, in the far leather; the far timers stand beside
   it as the near ones do. The stage between the boards, the words and the
   piles stay: focus removes decoration, not state. The boards' size is the
   page's (sizeCells): the whole width, a leaner chrome, a higher ceiling.
   =========================================================================== */
/* SMOOTHLY (founder, 2026-09-11): the corner blocks fade rather than
   vanish, the padding and the frames' step slide, and the far tongue and
   its digits fade across their change of side while the page eases the
   cells (tweenTable). */
#s2 .board-frame > .total { transition: opacity .4s ease, visibility 0s linear 0s; }
#s2[data-focus="on"] .board-frame > .total { opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .4s ease, visibility 0s linear .4s; }
body:has(#s2.on) #s2 { transition: padding-left .4s ease; }
#s2 .board-frame { left: 0; transition: left .4s ease; }
#s2 .board-frame:has(.theirs) .flap, #s2 .board-frame:has(.theirs) .bank, #s2 .board-frame:has(.theirs) .round { transition: opacity .16s ease; }
#s2.focusing .board-frame:has(.theirs) .flap, #s2.focusing .board-frame:has(.theirs) .bank, #s2.focusing .board-frame:has(.theirs) .round { opacity: 0; }
#s2[data-focus="on"] .board-frame .pin { display: none !important; }   /* the near pins' roll rules outrank a plain class */
/* THE GRID STANDS CENTRED, NOT THE LEATHER (founder, 2026-09-11): the roll
   is thicker than the thin edge, so a centred frame carries its grid to the
   right of the screen's middle; each frame steps left by half that
   surplus. AMENDED 2026-09-15: where a stop sends both tongues to one
   side AND holds them whole, what has to ride level is the whole of it,
   leather through tongue, and --tab-step stands instead of this step. No
   stop does that today; the machinery is kept because the last stop is one
   figure away from it. */
/* AND THE PAIR RIDES CENTRED, NOT THE FRAME (founder, 2026-09-15: "you can
   intrude on the middle zone and also have the checkered boards not
   dead-centered", and "don't push the leathers over the edge"). With both
   score tongues sent to the same side, a dead-centred frame carries its
   tongue off the screen; stepping the pair half a tongue to the left puts
   the same breath at each end of it. --tab-step is that half, measured off
   the page by the sizing pass. It stands instead of the grid's old step,
   not beside it: two steps in the same direction walked the boards into
   the left edge. */
/* THE TONGUES TOUCH THE EDGES (founder, 2026-09-15: "half the faces need a
   48 cell. And the leather tongues that touch the edge of the screen").
   The middle stop keeps a tongue at each edge, and at the size he asked
   for they cannot both stand on the screen with the boards in one column
   -- so the boards leave the column. Each slides by what its own tongue
   wants, written by the sizing pass: the far board right until its tongue
   meets the left edge, the near board left until its tongue meets the
   right. The last stop holds only part of its tongue, so nothing steps
   there and the founder's own centring stands. */
#s2[data-focus="faces"] .board-frame:has(.theirs),
#s2[data-focus="on"] .board-frame:has(.theirs) { left: var(--far-shift, 0px); }
#s2[data-focus="faces"] .board-frame:has(.mine),
#s2[data-focus="on"] .board-frame:has(.mine) { left: var(--near-shift, 0px); }
/* AND THE MIDDLE STOP'S NEAR BOARD SEVEN LOWER (founder, 2026-09-15: "lets
   lower the writing 7px and the lower board as well, 7px lower"). Moved by
   `top`, not by a margin or a taller band: the boards stand in a centred
   column, so anything that made the column taller would have shared itself
   between the two and lifted the FAR board by half of it. This moves the
   near board alone and changes no layout. */
#s2[data-focus="faces"] .board-frame:has(.mine) { top: 7px; }
/* and each plate takes its own push, since its board stands at its own x
   (seatFaces). The dwarf corners' own rule is the one being answered
   here, so this must sit after it and match it term for term. */
#s2[data-focus="faces"] :is(.board-frame:has(.mine), .total[data-turned]) .mouth { left: calc(-2px - var(--face-out-near, var(--face-out)) + var(--face-in)); }
#s2[data-focus="faces"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { right: calc(-2px - var(--face-out-far, var(--face-out)) + var(--face-in)); }
/* LARGER, AND LEVEL WITH EACH OTHER (founder, 2026-09-15: "they need to be
   larger, aligned vertically and we need to show a bit more of them"). The
   plate is scaled here rather than the --mask dial rewritten, so every
   screen keeps its own dial as the base; the lift is seatFaces', which
   puts both plates on the measured middle of the band between the boards
   -- each is laid against its own board otherwise, half of an assumed gap
   away, and the gap at this stop is nothing like the assumed one. */
#s2[data-focus="faces"] .total .mouth {
  --bocca: calc(var(--mask) * 1.22);
  width: calc(var(--mask) * 1.22); height: calc(var(--mask) * 1.22 * var(--stage-aspect, 1.079));
}
/* the lift is composed WITH the seat's own mirror, never instead of it: a
   stone bocca is painted looking right, and the far seat flips it so it
   looks into the arena (dressMouth). Writing a bare translate here turned
   the far face back out of the picture (founder, 2026-09-15: "the mouth on
   the right is facing the wrong way"). */
#s2[data-focus="faces"] :is(.board-frame:has(.mine), .total[data-turned]) .mouth { transform: translateY(var(--near-lift, 0px)) var(--mouth-flip, scaleX(1)); }
#s2[data-focus="faces"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .mouth { transform: translateY(var(--far-lift, 0px)) var(--mouth-flip, scaleX(1)); }
/* SUPERSEDED 2026-09-15 (founder: "so that the leathers fit onscreen"):
   the last stop's boards take the same per-board step as the middle
   stop's, which stands each one so its own tongue meets its own edge.
   That is a truer centring than the grid's old half-skew step, since it
   counts the tongue the grid never did. */
body:has(#s2.on) #s2[data-focus="on"] { padding-left: 0; }
#s2[data-focus="on"] > #guide, #s2[data-focus="on"] > #talkStrip, #s2[data-focus="on"] > #barPurse { margin-left: 0; }
/* THE FAR TONGUE CROSSES AT THE LAST STOP (2026-09-15). Both scores hang
   off the near one's side there, so a single band carries them and the
   boards take what the other band was holding. The middle stop keeps one
   at each edge, by the founder's own call -- "move the upper tongue on the
   other side" -- and pays a band for each. */
#s2[data-focus="on"] .board-frame:has(.theirs) .flap {
  --flap-w: calc(var(--plate-w) * 0.2920); --flap-h: calc(var(--plate-h) * 0.3282);
  left: calc(var(--plate-w) - var(--plate-bl) - var(--plate-w) * 0.0056); top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000);
  background-image: url("/static/art/corners/flap_right.webp");
}
#s2[data-focus="on"] .board-frame:has(.theirs) .flap::before {
  -webkit-mask-image: url("/static/art/corners/flap_right_mask.webp"); mask-image: url("/static/art/corners/flap_right_mask.webp");
}
#s2[data-focus="on"] .board-frame:has(.theirs) .bank,
#s2[data-focus="on"] .board-frame:has(.theirs) .round {
  left: calc(var(--plate-w) - var(--plate-bl) - var(--plate-w) * 0.0056 + var(--plate-w) * 0.146 - 31px);
}
#s2[data-focus="on"] .board-frame:has(.theirs) .bank  { top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 58px); }
#s2[data-focus="on"] .board-frame:has(.theirs) .round { top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 36px); }
#s2[data-focus="on"] .board-frame:has(.theirs):has(.bank[hidden]) .round { top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 36px); }

/* THE MIDDLE STEP OF FOCUS (founder, 2026-09-12: the boccas alone;
   amended 2026-09-15: "half of each, exactly"). The rigs, the name bands,
   the shields, the medals and the levels fade; both plates stay where the
   dwarfs held them and the dice are still born from them, but each is
   pushed outward until only its inner half is on the picture -- the step
   is --face-out, written by the sizing pass, so the plates slide out as
   the cells grow into the width they leave. The tongue keeps its side. */
/* A FACE THAT HAS CRUMBLED DOES NOT COME BACK (founder, 2026-09-15: "on
   defeat the face explodes but there is another one underneath or it
   reappears immediately"). The verdict film hides the plate with an inline
   style; this is the hold that outlives a repaint, and it takes the
   dwarf's own little portrait with it, which is the other face in that
   corner. Only the deal of a fresh table lifts it. */
#s2 .total .mouth.crumbled { visibility: hidden !important; opacity: 0 !important; }
#s2 .total.crumbled .dw-pfp, #s2 .total.crumbled .dw-pfp-tint { display: none !important; }
#s2 .total > :not(.mouth) { transition: opacity .4s ease; }
#s2[data-focus="faces"] .total > :not(.mouth) { opacity: 0; pointer-events: none; }   /* everything but the bocca: rig, ribbon, robe, pfp, name, shield, medal, level */
#s2[data-cast="angled"] .total .mouth { transition: left .4s ease, right .4s ease; }
/* EXCEPT WHERE THE PLATES ARE SEATED BY MEASUREMENT (2026-09-15). seatFaces
   reads how far a plate hangs past its own leather and writes the push that
   makes that half a plate. A transition on the very property it is reading
   makes every reading a picture of the last one arriving, so the answer
   chases itself for three or four ticks. The boards still slide; the plates
   take their seats at once. */
#s2[data-focus="faces"] .total .mouth { transition: none; }

/* THE PAUSE (founder, 2026-09-12): AUTO's dress, beside CAST and BRIBE; lit
   while the table is paused; dim when it is not this seat's to lift. */
#pauseBtn {
  flex: none; min-width: 62px; padding: 6px 8px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1px;
  font-size: 12px; letter-spacing: .16em; color: #e6d7ad;
  background: linear-gradient(180deg, rgba(72,58,28,.92), rgba(46,36,16,.92));
  border: 1px solid #6b5512; border-radius: 4px;
  box-shadow: inset 0 1px 0 rgba(255,246,205,.22), 0 2px 4px rgba(0,0,0,.45);
}
#pauseBtn[hidden] { display: none; }
#pauseBtn .oath { font-size: 8px; letter-spacing: .18em; opacity: .7; font-style: italic; }
#pauseBtn.on { color: #2a1d06; background: var(--leaf); border-color: #8a6f18; box-shadow: inset 0 1px 0 rgba(255,250,220,.7), 0 2px 4px rgba(0,0,0,.45); }
#pauseBtn[disabled] { opacity: .45; }
/* the line under the stage while the table is paused */
#pauseLine { text-align: center; font: 12px/18px var(--serif); letter-spacing: .12em; text-transform: uppercase; color: #8a3a2a; }
#pauseLine[hidden] { display: none; }

/* —————————————————————————————————————
   THE CURTAIN (founder, 2026-09-13). A phone lying sideways cannot hold the
   table, and the web cannot turn the phone back; this plate asks. It is
   the parchment itself (class .parchment, so the tooth and the stains are
   the page's own) fixed over everything — above the dev drawers at 9999
   — and shown only where all three are true: landscape, a short window,
   a coarse pointer. A desktop window dragged short has a fine pointer and
   never sees it. The little phone turns upright on a loop; under reduced
   motion it simply stands upright. */
#turnCurtain {
  display: none;
  position: fixed; inset: 0; z-index: 10000;
  flex-direction: column; align-items: center; justify-content: center; gap: 18px;
  color: var(--ink); font-family: var(--serif); font-size: 21px; letter-spacing: .08em;
}
#turnCurtain p { margin: 0; position: relative; z-index: 1; }
#turnCurtain .phone {
  position: relative; z-index: 1; box-sizing: border-box;
  width: 28px; height: 50px; border: 3px solid var(--ink); border-radius: 7px;
  transform: rotate(-90deg);
  animation: turnUpright 2.6s ease-in-out infinite;
}
#turnCurtain .phone::after {                                    /* the home bar */
  content: ""; position: absolute; left: 50%; bottom: 4px; width: 10px; height: 2px;
  margin-left: -5px; background: var(--ink); border-radius: 1px;
}
@keyframes turnUpright {
  0%, 22%   { transform: rotate(-90deg); }
  58%, 100% { transform: rotate(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  #turnCurtain .phone { animation: none; transform: none; }
}
@media (orientation: landscape) and (max-height: 560px) and (pointer: coarse) {
  #turnCurtain { display: flex; }
}

/* On a desktop the same curtain asks for height, not a turn: a window
   dragged down to a phone's landscape (founder, 2026-09-13: "when we get
   close to mobile landscape, plaster on the flip-it wallpaper"). */
#turnCurtain .window-words { display: none; }
@media (orientation: landscape) and (max-height: 500px) and (pointer: fine) {
  #turnCurtain { display: flex; }
  #turnCurtain .phone-words { display: none; }
  #turnCurtain .window-words { display: inline; }
}

/* THE TOOLTIPS UNDER THE DESKTOP ZOOM (2026-09-13): lifted out of the
   zoomed column so the library places them right, their words are zoomed
   back to the arena's size here; a phone sets no --tip-zoom. */
wa-tooltip::part(body) { zoom: var(--tip-zoom, 1); }

/* THE DIE AND THE HAND UNDER THE WRITING (founder, 2026-09-14). The
   plaque reads word, oath, and under them the auto-cast switch: a small
   ivory die, still when auto is off, turning on its Y axis when auto is
   on. When the round is ours to call by hand the die gives way to a
   manicule pointing up at the word, nudging: press. The die is six faces
   in CSS 3D, its pips radial gradients; the switch's box is wider than
   the die so a thumb finds it. */
#s2 #bar > #cast { position: relative; overflow: visible; gap: 2px; }
#s2 #bar > #cast .dieSwitch {
  display: flex; align-items: center; justify-content: center;
  width: 44px; height: 22px; margin-top: 1px; perspective: 64px;
}
#s2 #bar > #cast .cube {
  position: relative; width: 13px; height: 13px; transform-style: preserve-3d;
  transform: rotateX(-26deg) rotateY(40deg);
}
#s2 #bar > #cast.auto .cube { animation: dieTurn 2.4s linear infinite; }
@keyframes dieTurn {
  from { transform: rotateX(-26deg) rotateY(0deg); }
  to   { transform: rotateX(-26deg) rotateY(360deg); }
}
#s2 #bar > #cast .cube b {
  position: absolute; inset: 0; box-sizing: border-box; border-radius: 2.5px;
  background-color: #f1e4c4; border: 1px solid #6b5512; backface-visibility: hidden;
  box-shadow: inset 0 0 2px rgba(90, 65, 10, .35);
}
/* the faces: opposite faces sum to seven, as a die's do */
#s2 #bar > #cast .cube .f1 { transform: translateZ(6.5px);
  background-image: radial-gradient(circle at 50% 50%, #2a1d06 0 1.2px, transparent 1.7px); }
#s2 #bar > #cast .cube .f6 { transform: rotateY(180deg) translateZ(6.5px);
  background-image: radial-gradient(circle at 28% 22%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 72% 22%, #2a1d06 0 1.1px, transparent 1.6px),
                    radial-gradient(circle at 28% 50%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 72% 50%, #2a1d06 0 1.1px, transparent 1.6px),
                    radial-gradient(circle at 28% 78%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 72% 78%, #2a1d06 0 1.1px, transparent 1.6px); }
#s2 #bar > #cast .cube .f2 { transform: rotateY(90deg) translateZ(6.5px);
  background-image: radial-gradient(circle at 28% 28%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 72% 72%, #2a1d06 0 1.1px, transparent 1.6px); }
#s2 #bar > #cast .cube .f5 { transform: rotateY(-90deg) translateZ(6.5px);
  background-image: radial-gradient(circle at 26% 26%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 74% 26%, #2a1d06 0 1.1px, transparent 1.6px),
                    radial-gradient(circle at 50% 50%, #2a1d06 0 1.1px, transparent 1.6px),
                    radial-gradient(circle at 26% 74%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 74% 74%, #2a1d06 0 1.1px, transparent 1.6px); }
#s2 #bar > #cast .cube .f3 { transform: rotateX(90deg) translateZ(6.5px);
  background-image: radial-gradient(circle at 26% 26%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 50% 50%, #2a1d06 0 1.1px, transparent 1.6px),
                    radial-gradient(circle at 74% 74%, #2a1d06 0 1.1px, transparent 1.6px); }
#s2 #bar > #cast .cube .f4 { transform: rotateX(-90deg) translateZ(6.5px);
  background-image: radial-gradient(circle at 28% 28%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 72% 28%, #2a1d06 0 1.1px, transparent 1.6px),
                    radial-gradient(circle at 28% 72%, #2a1d06 0 1.1px, transparent 1.6px), radial-gradient(circle at 72% 72%, #2a1d06 0 1.1px, transparent 1.6px); }
/* the hand: a manuscript's pointing index, turned to point up at the word */
#s2 #bar > #cast .hand {
  display: none; align-items: center; justify-content: center;
  width: 44px; height: 22px; margin-top: 1px;
  font: 21px/1 var(--serif); color: #ffeec2; text-shadow: 0 1px 1px rgba(0, 0, 0, .45);
  transform: rotate(-90deg); animation: beckon 1.1s ease-in-out infinite;
}
#s2 #bar > #cast.offered .hand { display: flex; }
#s2 #bar > #cast.offered .dieSwitch { display: none; }
@keyframes beckon {
  0%, 100% { transform: rotate(-90deg) translateX(0); }
  50%      { transform: rotate(-90deg) translateX(3px); }
}
/* the dialog's X: the library's button paints its icon in its own neutral, invisible on parchment */
/* THE X OF THE PAGE'S OWN (2026-09-14): the library's close button draws
   its icon from a data: URL the page's Content-Security-Policy refuses, so
   it never painted; it is hidden, and #menuX stands in the header's slot,
   a ring of parchment with the cross in ink. */
/* #tableX wears the page's round door (.dialog-x) and sits in the slot's flow */
#s2 #bar > #cast.auto {
  background: linear-gradient(180deg, #5c1711 0%, #43100b 100%);
  color: rgba(255, 238, 194, .62);
  box-shadow: 0 2px 0 rgba(80, 16, 10, .8), 0 4px 10px rgba(0, 0, 0, .35);
}
#s2 #bar > #cast.auto:active { background: linear-gradient(180deg, #5c1711 0%, #43100b 100%); }
/* the pill answers a tap even while the plaque itself, summoning, takes
   none: pointer events are switched back on for it alone (2026-09-13) */
#s2 #bar > #cast.summoning .dieSwitch, #s2 #bar > #cast.idle .dieSwitch { pointer-events: auto; }
/* THE BAR STANDS STILL (founder, 2026-09-13: "I do not want buttons popping
   in and out of existence on that menu. We need muscle memory to develop
   there"). Every hand keeps its place for the whole match; one that cannot
   be used right now stands dim and idle, takes no tap and no press. The
   CAST plaque idles in the dark red of its auto look, the pill on it live. */
#s2 #bar > button.idle { pointer-events: none; opacity: .42; }
#s2 #bar > #cast.idle, #s2 #bar > #cast.idle:active {
  opacity: 1;
  background: linear-gradient(180deg, #5c1711 0%, #43100b 100%);
  color: rgba(255, 238, 194, .62);
  box-shadow: 0 2px 0 rgba(80, 16, 10, .8), 0 4px 10px rgba(0, 0, 0, .35);
}

/* A BURST AT THE FUSE'S END (2026-09-14): a flash and eight sparks, on the
   body above everything, scaled with the arena's zoom on a desktop (a
   transform, so the point it is placed at is not scaled with it) */
.fuse-burst { position: fixed; z-index: 90; width: 0; height: 0; pointer-events: none; transform: scale(var(--k, 1)); }
.fuse-burst b {
  position: absolute; left: -16px; top: -16px; width: 32px; height: 32px; border-radius: 50%;
  background: radial-gradient(circle, #fff6d8 0%, #ffd60a 30%, rgba(255, 120, 20, .65) 58%, rgba(255, 80, 0, 0) 72%);
  animation: burst-flash 480ms ease-out forwards;
}
.fuse-burst i {
  position: absolute; left: -2px; top: -2px; width: 4px; height: 4px; border-radius: 50%;
  background: #ffe9a8; box-shadow: 0 0 6px 2px rgba(255, 160, 30, .85);
  animation: burst-spark 620ms cubic-bezier(.1, .7, .3, 1) forwards;
}
@keyframes burst-flash { 0% { transform: scale(.25); opacity: 1; } 55% { opacity: .95; } 100% { transform: scale(2.4); opacity: 0; } }
@keyframes burst-spark { 0% { transform: rotate(var(--a)) translateX(0); opacity: 1; } 100% { transform: rotate(var(--a)) translateX(30px); opacity: 0; } }

/* THE PAUSE VEIL (founder, 2026-09-14): the screen goes dark; DADI, the
   word, who paused it, the count, and the door out. Above everything; the
   card scaled with the arena on a desktop. */
#pauseVeil {
  position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center;
  background: radial-gradient(80% 55% at 50% 50%, rgba(22, 17, 12, .87), rgba(14, 10, 6, .975));
  /* no backdrop blur (2026-09-28): under a veil 87-97% dark it could not be
     seen, and it was recomputed whenever anything behind it moved */
}
/* THE TABLE'S OWN WRITING GOES QUIET UNDER IT (founder, 2026-09-16: "make
   everything visually appealing"). The middle line read straight through
   the dark -- "tap CAST to call the round", in iron gall on parchment,
   under a paused table, which is both the brightest thing behind the veil
   and the one thing on the screen that was not true. */
#s2[data-paused="1"] #sysLine { opacity: 0; }
#pauseVeil[hidden] { display: none; }
/* THE BAR STANDS THROUGH IT (founder, 2026-09-15: "when a game is paused,
   the options menu and cast button should remain visible ... so that the
   user can edit his stuff during a pause"). The veil darkens the table and
   the bar rises over it, its columns with it -- the bar is a stacking
   context, so an opened stack comes up lit with it.

   The two hands that act on the ROUND go dim meanwhile, the way any hand
   that cannot be used does. CAST goes idle by its own painter, and the
   stylesheet has always left the die switch on an idle plaque reachable,
   which is exactly the control the founder wants there. */
#s2[data-paused="1"] #bar { z-index: 90; }
#s2[data-paused="1"] #bar #bribeBtn,
#s2[data-paused="1"] #bar #powerBtn { pointer-events: none; opacity: .42; }
/* TWO THIRDS THE ART, ONE THIRD THE PAUSE (founder, 2026-09-15: "like the
   whole thing on 2/3 of the screen and the remaining 1/3 should show the
   pause stuff"). The veil becomes a column: the borrowed mark and beast
   above, the pause below. Only the SIZE is set here -- the beast's blink,
   the die's turn and its rays are the elements' own, and they carry on
   wherever they are hung. The word DADI goes from the card while they are
   up: the mark says it, and better. */
/* THE COLUMN ENDS WHERE THE BAR BEGINS (founder, 2026-09-16: "the below
   side of the screen, the one with the actual pause content is very spread
   all the way down the page and its reaching under the footer menu").
   `--pv-floor` is the bar's own measured height, written by paintPauseVeil
   when the veil goes up; the art and the card then divide what is left,
   and nothing this column lays out can fall behind the bar. */
/* ONE PIECE, NOT TWO BANDS WITH A HOLE BETWEEN THEM (founder, 2026-09-16:
   "the word paused is still miles under the monkey. Why? How are you
   judging this empty space in the middle of the screen like a good design
   choice?"). The column used to be cut into a 64% band and a 36% band,
   each centring its own contents -- and since both contents are smaller
   than their band, all the slack fell in the middle. About 180 pixels of
   nothing between the monkey's feet and the word.

   Now the art and the pause block are each their own height and the whole
   column is centred, so the slack goes to the outside, where it reads as
   margin. The beast takes the screen's width and is held back by its
   height: what is left once the mark, the block and the bar have had
   theirs -- so on a short phone he gives up size rather than pushing the
   door out behind the bar. */
/* A CEILING TO BREATHE UNDER (founder, 2026-09-16: "I feel that the look
   greatly benefits from leaving a ceiling room to breathe"). The 54 is
   before anything is laid out, and the column's own centring gives the
   ceiling its share of whatever is left over on top of that -- so the
   composition never rides the top of the screen, at any height.

   AND NO VIEWPORT UNIT ANSWERS "HOW MUCH ROOM IS THERE" (founder's phone,
   the same afternoon: the monkey hit his head on the subtitle and pushed
   RESUME into the bar). The beast's height was capped with
   calc(100vh - 470px), and on iOS Safari `vh` is the LARGE viewport, the
   one with the toolbars hidden -- so with a toolbar showing he was sized
   for a taller screen than the one being looked at. `--pv-room` is the
   veil's own measured box less the bar and less what the block below him
   needs, written by paintPauseVeil. He takes the width he can and never
   more room than there is. */
#pauseVeil { flex-direction: column; justify-content: center; gap: 0;
  padding-top: 54px; padding-bottom: var(--pv-floor, 0px); }
#pauseVeil .pv-art {
  flex: 0 0 auto; min-height: 0; width: 100%; pointer-events: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0;
  padding: 0 8px;
  /* a seventh smaller than he was, for the air around him (founder,
     2026-09-16: "a 15% smaller monkey, so it looks a bit more exquisite") */
  --beastW: min(75vw, 340px, calc(var(--pv-room, 300px) * 0.85));
}
#pauseVeil .pv-art:empty { display: none; }
/* THE MARK IS A CORNER PLAQUE, NOT THE HEADLINE (founder, 2026-09-16:
   "only show the logo in the upper right corner", "and erase the subtitle
   from it"). It comes out of the column entirely and sits in the top right
   -- the STAGING ribbon owns the other corner -- so the monkey has the
   page. And it is a DIFFERENT picture from the landing's: see lendTheArt,
   which hangs the dark cut, the letters alone with the warm rim a dark
   ground takes away from a letter drawn with a dark outline. */
#pauseVeil .pv-art .incipit {
  /* halfway between the ceiling and his crown -- `--pv-mark-top` is
     measured by paintPauseVeil once the column has settled, because where
     his crown lands depends on the room he was given (founder, 2026-09-16:
     "place the dadi a little lower, halfway between the top and the
     monkeys head") */
  position: absolute; top: var(--pv-mark-top, 12px); left: 50%; transform: translateX(-50%); margin: 0;
  width: min(30vw, 122px);
}
#pauseVeil .pv-art .incipit img.wordmark,
#pauseVeil .pv-art .incipit img.logo-full { width: 100%; display: block; position: relative; z-index: 1; }
/* THE LETTERS RUN RED TO BLUE AND BACK (founder, 2026-09-16: "I think a
   swiping red-blue logo would be cooler if not too expensive on the
   canvas"). Nothing goes near the canvas: the dice keep their own, and
   this is two layers of the mark, 122 pixels wide, composited by the
   browser.

   UNDER: a red-and-blue band, seen only through the letters -- the field
   cut carries their silhouette in its alpha, and the band slides one whole
   period every nine seconds, which is why it loops without a seam.
   OVER: the leaf cut, the gold filigree lifted off its field, so the
   ornament and the letter outlines stay gold while the colour moves
   underneath them. That is the picture as it was drawn, only the field is
   no longer painted.

   `mask-mode: alpha` is not decoration. Left at its default the mask was
   read for LUMINANCE, and a silhouette that is white-on-transparent has no
   luminance to give: the whole band was masked away and the letters came
   out hollow. Safari reaches the same place by its own road, the -webkit-
   mask, which reads alpha without being asked. */
#pauseVeil .pv-art .incipit { isolation: isolate; }
#pauseVeil .pv-art .incipit::before {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(100deg, #a81d26 0%, #a81d26 8%, #1e3a93 34%,
                                      #2d49a6 50%, #1e3a93 66%, #a81d26 92%, #a81d26 100%);
  background-size: 366px 100%; background-repeat: repeat-x;
  -webkit-mask-image: url("/static/art/logo_codex_field.webp");
          mask-image: url("/static/art/logo_codex_field.webp");
  -webkit-mask-size: 100% 100%;          mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;        mask-repeat: no-repeat;
  mask-mode: alpha;
  animation: mark-sweep 9s linear infinite;
}
/* one whole tile, so the end of the loop is the start of it */
@keyframes mark-sweep { from { background-position: 0 0; } to { background-position: -366px 0; } }
@media (prefers-reduced-motion: reduce) {
  #pauseVeil .pv-art .incipit::before { animation: none; background-position: -120px 0; }
}
/* no mask at all, and the band would be a rectangle behind the letters */
@supports not ((-webkit-mask-image: url(a)) or (mask-image: url(a))) {
  #pauseVeil .pv-art .incipit::before { display: none; }
}
#pauseVeil .pv-art #splashBeast { width: 100%; max-width: none; display: block; margin: 0; }
#pauseVeil .pv-art .beast-stage { margin: 0; }
#pauseVeil:has(.pv-art .beast-stage) .pv-dadi { display: none; }
#pauseVeil:has(.pv-art .beast-stage) .pv-card { flex: 0 0 auto; min-height: 0; padding: 0 24px; display: flex;
  flex-direction: column; align-items: center; justify-content: center; }
/* the word follows the monkey's own feet, one clear step down -- it was
   flush against them (founder: "pause is crammed up the monkeys bottom") */
#pauseVeil:has(.pv-art .beast-stage) .pv-word { margin-top: 16px; }
#pauseVeil:has(.pv-art .beast-stage) #pvBtn { margin-top: 16px; }
#pauseVeil .pv-card { text-align: center; color: #e6d2a0; zoom: var(--tip-zoom, 1); padding: 24px; }
#pauseVeil .pv-dadi {
  font: 700 46px/1 var(--serif); letter-spacing: .32em; text-indent: .32em; color: #c9a227;
  text-shadow: 0 2px 0 rgba(0, 0, 0, .5), 0 0 24px rgba(201, 162, 39, .35);
}
/* ONE BLOCK, NOT FOUR ANNOUNCEMENTS (founder, 2026-09-16: "very spread all
   the way down the page"). The word, who did it, the figure and what the
   figure counts belong together, and at 12, 18, 10 and 26 pixels apart
   they did not read that way. */
#pauseVeil .pv-word { font: italic 24px/1.2 var(--serif); letter-spacing: .22em; text-indent: .22em; margin-top: 10px; color: #f1e2b8; }
#pauseVeil .pv-by { font: 12px/1.4 var(--sans); letter-spacing: .14em; text-transform: uppercase; opacity: .72; margin-top: 12px; }
#pauseVeil .pv-count { font: 42px/1 var(--serif); letter-spacing: .06em; margin-top: 4px; color: #fff3d0; font-variant-numeric: tabular-nums; }
#pauseVeil .pv-legend { font: 11px/1.4 var(--sans); letter-spacing: .14em; text-transform: uppercase; opacity: .6; margin-top: 3px; }
#pauseVeil .pv-count[hidden], #pauseVeil .pv-legend[hidden] { display: none; }
#pauseVeil #pvBtn {
  display: inline-block; margin: 18px auto 0; min-width: 160px; height: 52px; padding: 0 22px; border-radius: 7px;
  font: 700 14px/1 var(--sans); letter-spacing: .18em; color: #ffeec2;
  background: linear-gradient(180deg, #8d2019 0%, #6d1710 100%); border: 2px solid #c9a227;
  box-shadow: 0 3px 0 rgba(80, 16, 10, .8), 0 6px 14px rgba(0, 0, 0, .42);
}
#pauseVeil #pvBtn:active { background: linear-gradient(180deg, #a52a20 0%, #7d1c14 100%); }
#pauseVeil #pvBtn:disabled { opacity: .45; }

/* THE LUCK READING, UNDER THE NEAR SEAT'S ROUND DIGITS (founder, 2026-09-18:
   "nothing fancy"): one line, the score and the house's word, in the
   digits' own ink and width, sitting between them and the tongue. A tap
   arms it and the plaque goes dark iron-gall, the page's seal for a
   choice that costs. The table's own measure wears the same line under
   the far seat's digits, and is not a button. */
#s2 .board-frame .luck, #s2 .board-frame .meter-badge {
  /* as wide as its words, centred on the digits: "mostly skill" clipped
     at the digits' own 62px */
  position: absolute; z-index: 3; width: auto; min-width: 62px; margin: 0; padding: 1px 4px;
  min-height: 0; display: flex; align-items: baseline; justify-content: center; gap: 4px; white-space: nowrap;
  transform: translateX(-50%);
  font: 10px/1.25 var(--serif); letter-spacing: .04em; color: #6a3418;
  text-shadow: 0 1px 0 rgba(255, 248, 230, .5);
  background: none; border: 0; border-radius: 8px; box-shadow: none;
  -webkit-tap-highlight-color: transparent;
}
#s2 .board-frame .luck { cursor: pointer; }
#s2 .board-frame .meter-badge { pointer-events: none; }
#s2 .board-frame .luck[hidden], #s2 .board-frame .meter-badge[hidden] { display: none; }
#s2 .board-frame .luck .luck-n, #s2 .board-frame .meter-badge .luck-n { font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
#s2 .board-frame .luck .luck-word, #s2 .board-frame .meter-badge .luck-word {
  font: 700 7.5px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; opacity: .8;
}
#s2 .board-frame:has(.mine) .luck {
  left: var(--flap-mid);
  top: calc(-1 * var(--plate-bt) + var(--plate-h) * 0.3000 - 19px);   /* under the digits, above the tongue */
}
#s2 .board-frame:has(.theirs) .meter-badge {
  left: calc(-1 * var(--plate-bl) + var(--tuck) - var(--flap-w) / 2);
  top: calc(var(--flap-top) + var(--plate-h) * 0.3294 + 46px);       /* under the far digits */
}
#s2 .board-frame .luck.armed {
  color: #f4e6bf; text-shadow: 0 1px 2px rgba(0, 0, 0, .65);
  background: linear-gradient(180deg, #4c3312, #2a1c0a); box-shadow: 0 0 0 1px rgba(201, 162, 39, .5);
}
#s2 .board-frame .luck.armed .luck-word { opacity: 1; color: #f0d98a; }
#s2 .board-frame .luck:active { transform: translate(-50%, 1px); }
#s2 .board-frame .luck:disabled { opacity: .5; }

/* THE GAMBLING-STRATEGY METER ON THE TITLE PAGE (founder, 2026-09-18):
   a scale from the lapis of strategy to the house's red of gambling,
   the pin at the house's measured share, the word and the number under
   it. Unmeasured, the pin fades to the middle and the number is withheld. */
#meterRow { width: min(100%, 312px); margin: -4px auto 0; }
#meterRow[hidden] { display: none; }
#meterRow .meter-scale {
  position: relative; height: 8px; border-radius: 4px;
  background: linear-gradient(90deg, #2c3f6e 0%, #4e6ea8 30%, #c9a227 50%, #b8642a 72%, #8d2019 100%);
  border: 1px solid rgba(123, 101, 52, .5);
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .28), 0 1px 0 rgba(255, 252, 242, .5);
}
#meterRow .meter-pin {
  position: absolute; top: 50%; left: calc(var(--meter, 50) * 1%);
  width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%;
  background: radial-gradient(circle at 40% 35%, #fff8e2, #e0c890 70%, #b8963e);
  border: 1.5px solid #7b6534; box-shadow: 0 1px 3px rgba(0, 0, 0, .45);
  transition: left .35s cubic-bezier(.2, 1, .3, 1);
}
#meterRow.unmeasured .meter-pin { opacity: .35; }
#meterRow .meter-read {
  display: flex; align-items: baseline; justify-content: space-between; margin-top: 4px; padding: 0 2px;
  font: 700 8px/1.2 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: #4a3826; opacity: .8;
}
#meterRow .meter-word { font: 12px/1.2 var(--serif); letter-spacing: .12em; text-transform: uppercase; color: var(--ink); opacity: 1; }
#meterRow.unmeasured .meter-word { opacity: .6; }

/* ==========================================================================
   S10-S13 -- THE MENU, THE OPTIONS, THE PORTRAIT, THE ACCOUNT (2026-09-21)
   ========================================================================== */
#s10, #s11, #s12, #s13 {
  gap: 14px; padding: 18px 14px calc(18px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}
/* the head: the medallion, the name, the purse */
.hub-head { display: flex; align-items: center; gap: 14px; padding: 2px 6px; }
.hub-medal { width: 64px; height: 64px; flex: 0 0 auto; border-radius: 50%; box-shadow: 0 2px 6px rgba(0, 0, 0, .35); }
.hub-who { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.hub-name { font-family: var(--serif); font-size: 20px; letter-spacing: .06em; color: var(--ink); text-transform: capitalize;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hub-purse { font: 700 9.5px/1.3 var(--sans); letter-spacing: .16em; text-transform: uppercase; color: #7a5a12; }
.hub-purse b { font: 13px/1 var(--serif); letter-spacing: .04em; color: var(--ink); }
#acctName { width: 100%; max-width: 240px; min-height: 38px; font-size: 18px; text-align: left; padding: 4px 10px; }
/* the doors: the book's own dress, standing on the page */
.hub-doors { display: flex; flex-direction: column; gap: 10px; width: min(100%, 340px); margin: 0 auto; }
.hub-doors .menu-door { background: rgba(255, 252, 240, .55); }
.hub-foot { text-align: center; margin-top: 6px; }
button.quiet { background: none; border: 0; box-shadow: none; min-height: 32px;
  font-size: 10px; letter-spacing: .22em; text-transform: uppercase; color: #7a5a12; }
/* the options: a switch is a plaque that takes the seal when it is on */
.opt-group { display: flex; flex-direction: column; gap: 8px; width: min(100%, 340px); margin: 0 auto; }
.opt-group .legend { margin: 6px 4px 0; }
.opt-switch {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  width: 100%; min-height: 52px; padding: 8px 14px; text-align: left;
}
.opt-switch .word { font-family: var(--serif); font-size: 15px; letter-spacing: .14em; }
.opt-switch .oath { font: 9px/1.2 var(--sans); letter-spacing: .12em; text-transform: uppercase; opacity: .7; }
.opt-switch.picked::before { content: ""; }
.opt-switch.picked .oath { opacity: .85; color: #f0d98a; }
/* the portrait: the medallion large, the beasts and the fields beneath */
.pt-stage { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 4px 0 2px; }
.pt-medal { width: min(56vw, 220px); height: auto; border-radius: 50%; box-shadow: 0 6px 18px rgba(0, 0, 0, .35); }
.pt-name { font-family: var(--serif); font-size: 18px; letter-spacing: .08em; color: var(--ink); text-transform: capitalize; }
#s12 .wd-row .legend { margin: 2px 4px 0; }
#s12 .wd-rail { display: flex; gap: 8px; overflow-x: auto; overscroll-behavior-x: contain; padding: 8px 4px 10px;
  scroll-snap-type: x proximity; scrollbar-width: none; }
#s12 .wd-rail::-webkit-scrollbar { display: none; }
#s12 .wd-tile { position: relative; flex: 0 0 auto; width: 70px; scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 4px 0 2px; margin: 0;
  min-height: 0; min-width: 0; background: none; border: 0; box-shadow: none; color: var(--ink); cursor: pointer;
  -webkit-tap-highlight-color: transparent; }
#s12 .wd-tile:active { transform: none; }
#s12 .wd-tile .tile { display: block; width: 56px; height: 56px; flex: 0 0 auto; border-radius: 50%; }
#s12 .wd-tile .pt-field { box-shadow: inset 0 0 0 2px rgba(201, 162, 39, .6), 0 2px 4px rgba(0, 0, 0, .4); }
#s12 .wd-tile .name { font: 9.5px/1.15 var(--sans); letter-spacing: .03em; text-align: center; max-width: 70px; color: #4a3826; }
#s12 .wd-tile.chosen .tile { outline: 2px solid var(--gold); outline-offset: 2px; }
#s12 .wd-tile.chosen .name { color: #7a5a12; font-weight: 700; }
#s12 #ptKeep[hidden] { display: none; }
/* the account's note, in the rules' own voice */
.acct-note { width: min(100%, 340px); margin: 0 auto; font-family: var(--serif); font-size: 13px; line-height: 1.45;
  color: #4a3826; text-align: center; }

/* ==========================================================================
   S9 -- THE PROFILE: A WARDROBE WITH A MIRROR (founder, 2026-09-18)
   ========================================================================== */
#s9 {
  gap: 12px; padding: 18px 14px calc(18px + env(safe-area-inset-bottom, 0px));
  overflow-y: auto; -webkit-overflow-scrolling: touch; overscroll-behavior: contain;
}
#s9 .wd-head {
  display: flex; align-items: baseline; justify-content: space-between; padding: 0 4px;
  font-family: var(--serif); letter-spacing: .08em; color: var(--ink);
}
#s9 .wd-who { font-size: 18px; text-transform: capitalize; }
#s9 .wd-dust { font-size: 12px; letter-spacing: .16em; text-transform: uppercase; color: #7a5a12; }
#s9 .wd-dust .n { font-size: 15px; letter-spacing: .04em; color: var(--ink); }
/* THE MIRROR. A recess in the vellum, ruled in gilt, the arena's own
   parchment behind the pieces so the leathers sit on what they will sit on. */
#s9 .wd-stage {
  position: relative; border-radius: 10px; padding: 10px 10px 8px;
  border: 1px solid rgba(140, 104, 30, .55);
  box-shadow: inset 0 3px 10px rgba(56, 38, 14, .28), inset 0 -2px 0 rgba(255, 250, 235, .35), 0 1px 0 rgba(255, 252, 242, .5);
  background: rgba(255, 250, 235, .18);
}
/* THE MIRROR'S SCALE IS THE ARENA'S OWN, HALVED. The arena hangs its
   corner from the mask's width (--mask) and draws the construct in units
   of a 366th of it (--dw-s); the mirror keeps those very numbers at half
   the mask, so the dwarf, the face he holds and the die before it stand
   in the proportions the table gives them. */
#s9 .wd-arena {
  position: relative; height: var(--wd-h);
  --wd-h: 244px; --mask: 52px; --dws: calc(var(--mask) / 366);
}
/* the board: the plate worn as the table wears it, a nine-slice around the
   cells, each side of the rim as wide as the plate draws it */
#s9 .wd-frame {
  --c: 34px; --bd-top: calc(var(--c) * 0.385);
  position: absolute; right: 5%; top: 50%; transform: translateY(-50%);
  border-style: solid;
  border-image-source: var(--wd-board-img, url("/static/art/board_blue.webp"));
  border-image-slice: var(--wd-board-slice, 47 50 48 50);
  border-image-width: 1;
  border-width: var(--bd-top) calc(var(--bd-top) * var(--wd-rim-r, .909))
                calc(var(--bd-top) * var(--wd-rim-b, 1)) calc(var(--bd-top) * var(--wd-rim-l, .909));
}
#s9 .wd-board { display: flex; gap: 4px; padding: 5px; }
#s9 .wd-col { display: flex; flex-direction: column; gap: 4px; }
#s9 .wd-cell {
  width: var(--c); height: var(--c); position: relative; border-radius: 4px;
  display: flex; align-items: center; justify-content: center;
  background: var(--wd-cell-face, url("/static/art/cell_blue.webp")) center / cover no-repeat;
  box-shadow: inset 0 4px 6px rgba(48, 33, 12, .40), inset 0 -3px 4px rgba(255, 250, 236, .32),
              inset 0 0 0 1px rgba(70, 54, 26, .28);
}
#s9 .wd-cell .die {
  width: 90%; height: 90%; margin: 0; border-radius: 5px;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .22), inset 0 -3px 5px rgba(0, 0, 0, .30),
              inset 0 0 0 1px rgba(50, 34, 16, .55), 0 2px 4px rgba(0, 0, 0, .55);
}
/* the tongue: the table's own flap, in the leather's tint, on the right */
#s9 .wd-flap {
  position: absolute; z-index: -1; pointer-events: none; isolation: isolate;
  left: calc(100% - 2px); top: 30%; width: 31%; aspect-ratio: 0.89;
  background: url("/static/art/corners/flap_right.webp") no-repeat; background-size: 100% 100%;
  font-family: var(--serif); line-height: 1; color: var(--wd-ink, #f1e4c4);
  text-shadow: 0 1px 1px rgba(0, 0, 0, .6);
}
#s9 .wd-flap::before {
  content: ""; position: absolute; inset: 0; mix-blend-mode: multiply; background-color: var(--wd-tint);
  -webkit-mask: url("/static/art/corners/flap_right_mask.webp") no-repeat 0 0 / 100% 100%;
          mask: url("/static/art/corners/flap_right_mask.webp") no-repeat 0 0 / 100% 100%;
}
#s9 .wd-flap .flap-label, #s9 .wd-flap .flap-n {
  position: absolute; left: 0; right: 0; text-align: center; transform: translateY(-50%); white-space: nowrap; font-weight: normal;
}
#s9 .wd-flap .flap-label { top: 23%; font-size: 8px; }
#s9 .wd-flap .flap-n { top: 58%; font-size: 17px; }
/* the mouth: the near face, looking into the arena, and the die in front
   of it -- dice.js's own stage, which carries its own margins */
/* the face on the dwarf's hands: the arena sets the construct's top a
   mask-height under the face's top, so the face's foot rests on the
   raised hands; the same sum here, from the corner's measured top */
#s9 .wd-mouth {
  position: absolute; left: 8px; width: var(--mask); aspect-ratio: 1 / var(--wd-mouth-aspect, 1.079);
  top: calc(var(--wd-h) - 1245 * var(--dws) - var(--mask) * var(--wd-mouth-aspect, 1.079) + 50 * var(--dws));
  background: var(--wd-mouth-img, url("/static/art/bocca.webp")) center / contain no-repeat;
  transform: var(--wd-mouth-flip, scaleX(1)); z-index: 3;
}
/* the die before the face: dice.js's stage carries 72px of margin each
   side of a 62px die and 30px above and below, so its host is placed by
   where the die's own centre lands, at .78 of its size */
#s9 .wd-die {
  position: absolute; z-index: 4; transform: scale(.78); transform-origin: 0 0;
  left: calc(8px + var(--mask) + 22px - 80px);
  top: calc(var(--wd-h) - 1245 * var(--dws) - var(--mask) * 1.079 * .45 - 48px);
}
/* the corner: the construct that holds the face, cloth tinted to the
   leather as the table tints it -- the mask blended as colour */
#s9 .wd-corner {
  position: absolute; left: 10px; bottom: 0; width: calc(446 * var(--dws)); height: calc(1245 * var(--dws));
  background: var(--wd-rig-img, none) no-repeat; background-size: 100% 100%; z-index: 2;
}
#s9 .wd-stage[data-rig="dwarf2"] .wd-corner { width: calc(430 * var(--dws)); height: calc(1263 * var(--dws)); }
#s9 .wd-corner .wd-corner-tint {
  position: absolute; inset: 0; background-color: var(--wd-tint); mix-blend-mode: color;
  -webkit-mask: var(--wd-rig-mask, none) no-repeat 0 0 / 100% 100%;
          mask: var(--wd-rig-mask, none) no-repeat 0 0 / 100% 100%;
}
#s9 .wd-stage[data-rig="faces"] .wd-corner { display: none; }
#s9 .wd-stage[data-rig="faces"] .wd-mouth { top: 34%; left: 14px; width: calc(var(--mask) * 1.5); }
#s9 .wd-stage[data-rig="faces"] .wd-die { top: calc(34% + var(--mask) * .5 - 48px); left: calc(14px + var(--mask) * 1.5 + 18px - 80px); }
/* the ribbon: a try-on is named as one */
#s9 .wd-ribbon {
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%);
  padding: 5px 12px; border-radius: 12px; white-space: nowrap;
  font: 700 10px/1 var(--sans); letter-spacing: .14em; text-transform: uppercase;
  color: #f4e6bf; background: linear-gradient(180deg, #4c3312, #2a1c0a);
  border: 1px solid var(--gold); box-shadow: 0 2px 6px rgba(30, 18, 6, .4);
}
#s9 .wd-ribbon b { color: #f0d98a; font-weight: 700; }
#s9 .wd-ribbon[hidden] { display: none; }
/* THE RAILS: the tiles the picker already paints, laid in a row that
   scrolls sideways, a name under each and a price on the ones not yet
   the player's */
#s9 .wd-row .legend { margin: 2px 4px 0; }
#s9 .wd-rail {
  display: flex; gap: 8px; overflow-x: auto; overscroll-behavior-x: contain;
  padding: 8px 4px 10px; scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;                 /* a rail is swiped; the bar lay across the first tile's name */
}
#s9 .wd-rail::-webkit-scrollbar { display: none; }
#s9 .wd-tile {
  position: relative; flex: 0 0 auto; width: 70px; scroll-snap-align: start;
  display: flex; flex-direction: column; align-items: center; gap: 5px;
  padding: 4px 0 2px; margin: 0; min-height: 0; min-width: 0;
  background: none; border: 0; box-shadow: none; color: var(--ink); cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
#s9 .wd-tile:active { transform: none; }
#s9 .wd-tile .tile {
  display: block; width: 56px; height: 56px; flex: 0 0 auto; border-radius: 6px;
  background: rgba(0, 0, 0, .35); overflow: hidden;
}
#s9 .wd-tile .tile .die {
  width: 100%; height: 100%; margin: 0; border-radius: inherit;
  box-shadow: inset 0 2px 0 rgba(255, 255, 255, .22), inset 0 -3px 5px rgba(0, 0, 0, .30),
              inset 0 0 0 1px rgba(50, 34, 16, .55), 0 2px 4px rgba(0, 0, 0, .55);
}
#s9 .wd-tile .cell-tile { background-size: cover; background-position: center;
  box-shadow: inset 0 0 0 1px rgba(60, 40, 15, .5), inset 0 1px 0 rgba(255, 250, 235, .18), 0 2px 4px rgba(0, 0, 0, .55); }
#s9 .wd-tile .plate-tile { background-size: contain; background-repeat: no-repeat; background-color: rgba(0, 0, 0, .35); }
#s9 .wd-tile .rig-tile { background: rgba(0, 0, 0, .18) center / contain no-repeat; background-image: inherit; }
#s9 .wd-tile .rig-tile.bare::after {
  content: "\2022\2022"; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font: 22px/1 var(--serif); letter-spacing: .3em; color: #7a5a12;
}
#s9 .wd-tile .rig-tile { position: relative; }
#s9 .wd-tile .name {
  font: 9.5px/1.15 var(--sans); letter-spacing: .03em; text-align: center; text-transform: capitalize;
  max-width: 70px; color: #4a3826;
}
#s9 .wd-tile.chosen .tile { outline: 2px solid var(--gold); outline-offset: 2px; }
#s9 .wd-tile.chosen .name { color: #7a5a12; font-weight: 700; }
#s9 .wd-tile.trying .tile { outline: 2px dashed var(--gold); outline-offset: 2px; }
#s9 .wd-tile.locked .tile { filter: saturate(.55) brightness(.82); }
#s9 .wd-tile .price {
  position: absolute; top: 0; right: 2px; z-index: 2;
  padding: 2px 5px; border-radius: 9px;
  font: 700 8.5px/1 var(--sans); letter-spacing: .06em; white-space: nowrap;
  color: #f0d98a; background: linear-gradient(180deg, #4c3312, #2a1c0a);
  border: 1px solid rgba(201, 162, 39, .7); box-shadow: 0 1px 3px rgba(0, 0, 0, .4);
}
#s9 .wd-foot { margin-top: 4px; }
#s9 .wd-foot button { max-width: 180px; }
#s9 #wdUnlock { font-size: 14px; letter-spacing: .1em; white-space: nowrap; }
#s9 #wdUnlock[hidden] { display: none; }

/* THE RETURN'S VEIL (2026-09-14): up while a tab that was away is caught
   up by a whole repaint rather than by theatre */
#catchVeil { position: fixed; inset: 0; z-index: 85; background: rgba(16, 11, 7, .94); pointer-events: auto; }
#catchVeil[hidden] { display: none; }

/* SETTINGS, A MODAL (founder, 2026-09-14): the vendored dialog dressed as
   the page's parchment, bound in gold, with the page's own X and CLOSE;
   one door in it for now, ZOOM LOCK, lit while it holds */
#settingsPop { --width: min(92vw, 420px); --spacing: 14px; --wa-color-overlay-modal: rgba(16, 11, 7, .78); }
#settingsPop::part(dialog) {
  background: #e7d1a5 url("/static/art/dadi-parchment-neutral-seamless-web-512.webp");
  color: var(--ink); border: 2px solid #c9a227; border-radius: var(--radius-l);
  box-shadow: 0 0 0 1px rgba(90, 68, 26, .35), 0 18px 50px rgba(0, 0, 0, .6);
  max-height: calc(86dvh / var(--tip-zoom, 1));
}
#settingsPop::part(header) { padding: 12px 14px 8px; border-bottom: 2px solid transparent; border-image: var(--leaf) 1; align-items: center; }
#settingsPop::part(title) { font-family: var(--serif); font-size: 22px; letter-spacing: .06em; color: var(--ink); }
#settingsPop::part(close-button) { display: none; }
#settingsPop::part(header-actions) { display: flex; align-items: center; }
#settingsPop #settingsX { position: static; flex: none; margin: 0; }
#settingsPop::part(body) { display: flex; flex-direction: column; gap: 12px; padding: 14px 14px 6px; }
#settingsPop::part(footer) { display: flex; justify-content: center; padding: 8px 14px 16px; }
#settingsPop .doors > button {
  flex: 1 1 0; min-width: 0; height: 52px; padding: 0 4px;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
  position: static; margin: 0; width: auto; z-index: auto;
}
#settingsPop .doors > button .word { font-family: var(--sans); font-size: 11px; letter-spacing: .12em; line-height: 1; font-weight: 700; }
#settingsPop .doors > button .oath { font-size: 8.5px; letter-spacing: .06em; opacity: .7; line-height: 1.1; font-style: italic; }
#settingsPop #zoomLock.on { color: #2a1d06; background: var(--leaf); border-color: #6b5512; }
body:has(#settingsPop[open]) #footBar { display: none; }

/* YOUR OWN LOOK, ON PARCHMENT (2026-09-14): the picks wear the skins panel's
   shape but the modal's ink */
#settingsPop .pick-label { color: #6b5512; opacity: .9; }
#settingsPop .pick-head { color: var(--ink); background: rgba(90, 68, 26, .08); border-color: rgba(90, 68, 26, .45); box-shadow: none; }
#settingsPop .pick.open .pick-head { border-color: #8a6f18; }
#settingsPop .pick-caret { color: #8a6f18; }
#settingsPop .pick-list { background: rgba(0, 0, 0, .07); border-color: rgba(90, 68, 26, .3); max-height: 34vh; }
#settingsPop .opt { color: var(--ink); }
#settingsPop .opt .name { opacity: .85; }
#settingsPop .opt.chosen .name { color: #8a6f18; }
#settingsPop .tile { background: rgba(0, 0, 0, .12); }

/* THE BOARDS INTRUDE ON THE MIDDLE (founder, 2026-09-15: "bigger boards,
   both in step 1 and especially in step 2 of the focus; you can intrude on
   the middle zone"). The band between the boards is a minimum height on the
   stage; the dice themselves are laid over it absolutely and cost no layout
   at all, so cutting the band gives its pixels to the boards and the dice
   simply fly across their inner edges. The system line keeps its own room.
   ------------------------------------------------------------------------ */
/* FIVE PIXELS FURTHER OUT, EACH WAY (founder, 2026-09-15: "move the boards
   further from the center in focus1. 5px up and 5px down"). The band
   between them is what holds them apart, so it takes what it is given and
   the column shares it evenly: the far board rises, the near one drops by
   the same. Five each way, then five more at his word -- 40px of band
   became 60. CHROME follows it up, so the height model still tells the
   truth on a screen where height is what binds. */
#s2[data-focus="faces"] #strip, #s2[data-focus="faces"] #offerStage { min-height: 60px; }
/* THE LAST STOP'S BAND IS NOT A CUT ANY MORE (founder, 2026-09-15: "on
   focus2 ... its looking kinda cramped now"). It was cut to 26px when the
   band held nothing but a line of words; it now holds the words AND the
   dice, side by side, and the boards are bound by the screen's width at
   that stop with height to spare -- so the band takes what it needs and the
   cells do not pay a pixel for it. 72px was too much of a good thing
   (founder: "and now there's too much space in the middle"); 44 leaves the
   words and the dice their air and no more. */
#s2[data-focus="on"] #strip,    #s2[data-focus="on"]    #offerStage { min-height: 44px; }


/* THE LINE KEEPS CLEAR OF THE DICE (2026-09-15). With the band cut to a
   sliver the dice lie across it, and the system line, which sat at the
   band's foot, sat under them. At the focus stops it steps down onto the
   near board's own leather, where nothing else is written. */
/* THE WORDS SIT CLEAR OF THE LEATHER (founder, 2026-09-15: "the focus1
   middle writing is now seating on the lower leather. Raise it by 5px").
   They hung two pixels under the stage, which was parchment until the band
   was opened and the near board came up to meet it -- measured, they lay
   twelve pixels ON the leather, so five would not have freed them. Fifteen
   does, with three to spare. */
#s2[data-focus="faces"] #offerStage #sysLine { bottom: auto; top: calc(100% - 8px); }   /* seven back down at his word (2026-09-15), less the two a thirteen-pixel line grows downward, so its three of clearance stand */
/* the last stop keeps its words IN the band, beside the dice rather than
   under them: they have the left half of it and the dice the right
   (founder, 2026-09-15) */
#s2[data-focus="on"] #offerStage #sysLine {
  bottom: auto; top: 50%; transform: translateY(-50%);
  text-align: right; padding-right: 5px;      /* the words stop five pixels short of the divide; the dice start five past it (founder, 2026-09-15: "the middle line between the 2 divs ... a 5px clearance both ways") */
}

/* THE BEZEL IS GONE (founder, 2026-09-15: "the red border is ugly. Make it
   go away completely and use the space for content, dont use that space as
   a border anymore"). It stood at the two focus stops for one day. The band
   it framed belongs to the boards now: the breath at the edge drops to two
   pixels there, and the leathers take the rest. */

/* THE MIDDLE CALLS THE ROUND (founder, 2026-09-21: "maybe we need to tap
   the mid screen to draw the die/dice manually"). The strip lets taps
   through to the constructs beneath it; while the round is ours to call
   by hand the band takes them (drawStageCall), and a ghost die breathes
   at the landing: a dashed outline of the die that is not there yet, one
   pip, the line's own vermilion. */
#strip #offerStage.callable { pointer-events: auto; cursor: pointer; -webkit-tap-highlight-color: transparent; }
/* the row stands at the landing; one outline per die the offer will
   have (founder, 2026-09-22: "there should be 2 if its a 2-die game").
   THE SECOND LEANS ON THE FIRST (founder, 2026-09-22: "the 2nd die
   outline should be a bit at an angle and leaning on the other one. You
   know, like dice"): it tips fourteen degrees about its own lower-left
   corner, which stays on the ground beside the first, so its top-left
   corner comes to rest against the first's shoulder -- TOUCHING, NOT
   OVERLAPPING (founder, 2026-09-22: "Dont overlap them. Just touch one
   to the other"). A 33px box tipped fourteen degrees about that pivot
   reaches 7.4px past its own left edge at the top corner, so the boxes
   stand 8px apart and the corner meets the first's side. The tilt rides
   a variable so the breathing, which sets the transform, keeps it. */
#offerArea .ghost-dice {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: flex-end; gap: 10px; pointer-events: none;
}
#offerArea .ghost-die {
  --tilt: 0deg; --pivot: 50% 50%;
  display: block; width: 30px; height: 30px; flex: none;
  border: 1.5px dashed rgba(138, 58, 42, .8); border-radius: 7px;
  background: radial-gradient(circle at 50% 50%, rgba(138, 58, 42, .85) 0 2.4px, transparent 3.2px);
  transform-origin: var(--pivot);
  animation: ghost-breathe 1.6s ease-in-out infinite;
}
#offerArea .ghost-die + .ghost-die { --tilt: -14deg; --pivot: 12% 94%; margin-left: -2px; }
@keyframes ghost-breathe {
  0%, 100% { opacity: .45; transform: rotate(var(--tilt)) scale(.95); }
  50%      { opacity: .95; transform: rotate(var(--tilt)) scale(1.05); }
}
@media (prefers-reduced-motion: reduce) { #offerArea .ghost-die { animation: none; opacity: .8; } }

/* THE AUTO SIGN (founder, 2026-09-21: "the auto we also place around
   there somewhere maybe"; 2026-09-22: "that auto pill is ugly AF. Lets
   make it a circular arrows sign that is either static or spins"). Two
   arrows chasing each other round a circle, inline SVG in the band's own
   vermilion -- no box, no word: faint and still while auto is off, full
   ink and turning while it is on. At the band's top-right, inside the
   edge of whichever mask stands there (fitSysLine measures it, as it does
   the line's); the last stop has no mask to keep clear of: the corner.
   The button is bigger than the sign so a thumb finds it. */
#offerStage #stageAuto {
  position: absolute; z-index: 5; top: 0; right: calc(var(--mask) + 4px);
  pointer-events: auto; cursor: pointer; -webkit-tap-highlight-color: transparent;
  display: grid; place-items: center;
  margin: 0; padding: 0; min-height: 0; min-width: 0; width: 32px; height: 32px;
  color: var(--vermilion, #8a3a2a); opacity: .5;
  background: none; border: 0; border-radius: 50%; box-shadow: none;
  transition: opacity .25s ease; transform: none;
}
#offerStage #stageAuto svg {
  width: 19px; height: 19px; display: block;
  fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 1px 0 rgba(255, 250, 235, .7));
}
#offerStage #stageAuto[hidden] { display: none; }
#offerStage #stageAuto:hover, #offerStage #stageAuto:active { transform: none; box-shadow: none; }
#offerStage #stageAuto.on { opacity: 1; }
#offerStage #stageAuto.on svg { animation: auto-turn 2.4s linear infinite; }
@keyframes auto-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { #offerStage #stageAuto.on svg { animation: none; } }
#s2[data-focus="on"] #offerStage #stageAuto { right: 4px; }

/* THE TABLE'S PICKER HONOURS THE HOUSE (founder, 2026-09-22): a priced
   tile the pile does not own is dimmed, wears its price after its name,
   and does not go on when tapped -- the wardrobe is where it is bought. */
#skins .pick-list .opt.locked, #settingsPop .pick-list .opt.locked { opacity: .55; }
#skins .pick-list .opt.locked .name, #settingsPop .pick-list .opt.locked .name { color: #8a6a2c; }

/* --------------------------------------------------------------------------
   THE DRAWER (founder, 2026-09-22: "a match info/stats pane that can slide
   in and out of view ... A drawer rising from the bottom maybe best").
   A parchment page for this match that rises from behind the bar and
   stops at its top edge, so the bar keeps every hand; while it is shut
   the only part of it on screen is the pull, a leather tongue on the
   bar's top edge at the right. The scrim darkens the table above the bar
   and takes the tap that shuts it. Both are fixed like the bar, inside
   #s2, so the desktop's zoom treats them as it treats the bar. The pot
   that stood as words on the far rim is gone: nothing about the money is
   in the arena but the bag on a bribed column.
   -------------------------------------------------------------------------- */
body:not(:has(#s2.on)) #drawer, body:not(:has(#s2.on)) #drawerScrim { display: none; }
#drawerScrim {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(22, 16, 9, .5); opacity: 0; pointer-events: none;
  transition: opacity .26s ease;
}
#drawerScrim.open { opacity: 1; pointer-events: auto; }
#drawer {
  position: fixed; left: 0; right: 0; z-index: 55;
  bottom: calc(66px + env(safe-area-inset-bottom, 0px) - 4px);   /* four pixels tucked under the bar: no hairline while it moves */
  max-height: min(78svh, 680px);
  display: flex; flex-direction: column;
  padding: 12px 16px 18px; border-radius: 14px 14px 0 0;
  box-shadow: 0 -6px 18px rgba(20, 12, 4, .35), inset 0 1px 0 rgba(255, 250, 235, .6);
  color: var(--ink);
  transform: translateY(calc(100% + 4px));                       /* shut: its top edge on the bar's, the tuck paid back */
  transition: transform .28s cubic-bezier(.2, .7, .3, 1);
}
#drawer.open { transform: translateY(0); }
/* in the hand: no easing between the finger's positions */
#drawer.dragging, #drawerScrim.dragging { transition: none; }
@media (prefers-reduced-motion: reduce) { #drawer, #drawerScrim { transition: none; } }
/* the pull: the bar's own leather, a tongue with a gilt grip */
#drawerPull {
  position: absolute; top: -20px; right: 14px; z-index: 1;
  width: 58px; height: 20px; min-width: 0; min-height: 0; margin: 0; padding: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  border: 1px solid rgba(201, 162, 39, .5); border-bottom: 0; border-radius: 9px 9px 0 0;
  background: linear-gradient(180deg, #2a1d0b 0%, #1d1408 100%);
  box-shadow: 0 -2px 6px rgba(20, 12, 4, .35);
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: none; transform: none;
}
#drawerPull i { display: block; width: 20px; height: 2px; border-radius: 1px; background: rgba(201, 162, 39, .85); }
#drawerPull { touch-action: none; }                        /* the pull is a handle */
/* the bar is a handle too while the drawer is shut: no vertical pan of
   the browser's own may start on it (taps, and a sideways pan, still may) */
#s2 #bar { touch-action: pan-x pinch-zoom; }
#drawerPull:hover, #drawerPull:active { transform: none; box-shadow: 0 -2px 6px rgba(20, 12, 4, .35); }
/* open, the pull has done its work and would sit under the X: it fades,
   and the head's swipe, the X and the scrim shut the drawer */
#drawer.open #drawerPull { opacity: 0; pointer-events: none; transition: opacity .2s ease; }
/* the head: the title, the two names, the page's own X inside the corner */
#drawer .dr-head {
  position: relative; flex: none;
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 6px 44px 10px 2px; border-bottom: 1px solid rgba(201, 162, 39, .55);
  touch-action: none;                                     /* the head is a handle: the finger drags, the page does not scroll */
}
#drawer .dr-title { font: 700 17px/1.2 var(--serif); letter-spacing: .2em; text-transform: uppercase; color: #6a3418; }
#drawer .dr-sub { font: italic 15px/1.2 var(--serif); color: rgba(43, 32, 24, .72); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#drawer #drawerX.dialog-x { top: -6px; right: -6px; }
/* the page: three heads, and under each a ledger of entry, leader, figure */
#drawer .dr-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: 4px 2px 0; }
#drawer .dr-sec {
  margin: 14px 0 4px; font: 700 12px/1 var(--sans); letter-spacing: .22em; text-transform: uppercase; color: #8a6a2c;
}
#drawer .dr-sec:first-child { margin-top: 2px; }
#drawer .dr-row { display: flex; align-items: baseline; gap: 8px; padding: 5px 0; font: 16px/1.4 var(--serif); }
#drawer .dr-row .k { flex: none; color: #4a3520; letter-spacing: .03em; }
#drawer .dr-row .lead { flex: 1 1 12px; min-width: 12px; border-bottom: 1px dotted rgba(74, 53, 32, .45); transform: translateY(-4px); }
#drawer .dr-row .v { flex: 0 1 auto; max-width: 60%; text-align: right; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
#drawer .dr-row .v.soft { font-weight: 400; color: rgba(43, 32, 24, .62); }
#drawer .dr-row .v a { color: #6a3418; text-decoration: underline; text-underline-offset: 2px; }

/* THE MALOCCHIO IN THE OPTIONS (founder, 2026-09-22): the seven digits
   large and spaced, a ledger's number in the page's ink; the hint under
   them; RINSE is an ordinary switch that arms and asks. */
.opt-number { display: flex; flex-direction: column; gap: 5px; padding: 8px 14px 6px; }
.opt-number .digits {
  font: 700 28px/1 var(--serif); letter-spacing: .24em; color: var(--ink);
  font-variant-numeric: tabular-nums; text-shadow: 0 1px 0 rgba(255, 250, 235, .6);
}
.opt-number .digits.none { opacity: .45; }
.opt-number .hint { font: 9px/1.3 var(--sans); letter-spacing: .1em; text-transform: uppercase; opacity: .7; }

/* --------------------------------------------------------------------------
   THE ROLLING BAND (founder, 2026-09-22: names to thirty-two, "and just
   appear as a rolling band if it doesnt fit"). The scroll box is the
   ribbon's writable span -- measured on the layers, in the construct's
   own units: 600 on the first rig's near ribbon, 580 on its far banner,
   380 and 390 on the second rig's, whose laurels take the ends -- with its
   overflow hidden. A name that fits is centred, as it always was. One
   that does not starts at its head, glides to its tail, rests, glides
   back, and so on while the table stands; its edges fade so the letters
   enter and leave the cloth rather than being cut by it. rollNames
   measures the overhang and sets --roll and the glide's length.
   -------------------------------------------------------------------------- */
#s2 .total .pf-scroll { overflow: hidden; }
#s2 .total .pf-scroll .who { white-space: nowrap; }
#s2[data-corners="dwarf"] .total .pf-scroll {
  display: flex; justify-content: center; align-items: center; overflow: hidden;
  width: calc(600 * var(--dw-s)); height: auto;
}
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { width: calc(580 * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { width: calc(380 * var(--dw-s)); }
#s2[data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { width: calc(390 * var(--dw-s)); }
#s2[data-corners="dwarf"] .total .pf-scroll .who { flex: none; display: block; padding: 0 2px; }
/* THE FIRST RIG'S BANDS, A LITTLE LONGER (founder, 2026-09-22: "Go with
   your call"). The near ribbon's parchment ran 280..800 of its 1069 and
   is cut to 280..860, the folds and curls squeezed to pay for it; the far
   banner's ran 146..690 of the construct and is cut to 146..730 over the
   folds before the shield. The scroll boxes follow: the measured bands,
   centred where the parchment now is. The second rig's laurels are the
   founder's to move. */
#s2[data-corners="dwarf"] :is(.board-frame:has(.mine), .total[data-turned]) .pf-scroll { width: calc(580 * var(--dw-s)); left: calc(-2px - var(--face-out) + var(--face-in) + 885 * var(--dw-s)); }
#s2[data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .pf-scroll { width: calc(584 * var(--dw-s)); right: calc(-2px - var(--face-out) + var(--face-in) + 803.5 * var(--dw-s)); }
/* rolling: the head first, then the glide; the box's edges fade */
#s2 .total .pf-scroll.rolling { justify-content: flex-start; }
#s2 .total .pf-scroll.rolling .who {
  animation: name-roll var(--roll-s, 6s) ease-in-out infinite;
  will-change: transform;
}
#s2 .total .pf-scroll.rolling {
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
  mask-image: linear-gradient(90deg, transparent 0, #000 7%, #000 93%, transparent 100%);
}
@keyframes name-roll {
  0%, 12%   { transform: translateX(0); }
  50%, 62%  { transform: translateX(calc(-1 * var(--roll, 0px))); }
  100%      { transform: translateX(0); }
}
@media (prefers-reduced-motion: reduce) {
  #s2 .total .pf-scroll.rolling .who { animation: none; }
  #s2 .total .pf-scroll.rolling { justify-content: flex-start; }
}
/* the hub's name keeps to one line and ends in an ellipsis: it is a
   heading, not a band */
#hubName { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* --------------------------------------------------------------------------
   THE MENU'S DOORS ARE THE CODEX PLAQUES (founder, 2026-09-23: "The
   buttons should be the blue ones, like the initial play button" -- "Not
   these buttons brother, the one under the animated monkey"). The plaque
   under the splash beast: lapis, gilt-rimmed, a red-and-gold cross at
   each end, the word painted in gilt capitals. The set was cut and banked
   on 2026-08-24 for these screens: PLAY keeps its painted word, at the
   landing's own size; the six other doors take the blank, and their word
   is set on it in the page's serif -- gilt from light to deep with a dark
   edge, as near the painted hand as type gets -- one short word each, as
   the set speaks. Seven stand in a column; the screen scrolls if it must.
   -------------------------------------------------------------------------- */
.hub-doors { --door: min(295px, 74vw); gap: 6px; width: auto; align-items: center; }
.hub-doors .menu-door {
  position: relative;
  background: url("/static/art/cbtn_blank.webp") center / 100% 100% no-repeat;
  border: none; border-radius: 0; box-shadow: none;
  flex: none; width: var(--door); height: calc(var(--door) / 4.293); min-height: 0; max-width: none;
  padding: 0; margin: 0; justify-content: center; align-items: center; gap: 0;
  text-decoration: none; transition: none;
}
.hub-doors .menu-door:hover { transform: none; box-shadow: none; filter: brightness(1.06); }
.hub-doors .menu-door:active { transform: none; box-shadow: none; filter: brightness(1.15); background-color: transparent; }
.hub-doors .menu-door .word {
  display: block; padding-bottom: calc(var(--door) * 0.006);
  font-family: var(--serif); font-weight: 700; font-size: calc(var(--door) * 0.105);
  letter-spacing: .07em; text-transform: uppercase; line-height: 1;
  background: linear-gradient(180deg, #fbe28a 0%, #e8b93f 46%, #b98518 54%, #f0cd62 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  -webkit-text-stroke: calc(var(--door) * 0.0022) rgba(52, 30, 4, .9);
  filter: drop-shadow(0 1px 1px rgba(0, 0, 0, .55));
}
/* PLAY is the landing's own plaque, its word painted into the art */
.hub-doors #hubPlay { background-image: url("/static/art/cbtn_play.webp"); height: calc(var(--door) / 4.211); }
.hub-doors #hubPlay .word { color: transparent; background: none; -webkit-text-stroke: 0; filter: none; font-size: 16px; }   /* the art speaks; readers still hear PLAY */

/* --------------------------------------------------------------------------
   THE LEAF (founder, 2026-09-24): the codex's transition and loading screen.
   A sheet of parchment over the whole window, above the bar and the veil
   and under the landscape curtain. Shut, it is clipped to nothing; the
   turn in sweeps its slanted left edge from off the right to off the left
   (the sheet comes from the right, as a leaf turned by the right hand);
   held, it covers; the turn out sweeps its right edge away to the left.
   A gilt edge rides the moving side. In the lower margin the drolleries
   play; a vine draws itself up the left; a wait writes its line.
   -------------------------------------------------------------------------- */
#leaf {
  position: fixed; inset: 0; z-index: 9000; display: none; overflow: hidden;
  color: var(--ink);
  clip-path: polygon(108% 0, 100% 0, 100% 100%, 116% 100%);
}
#leaf.on { display: block; }
#leaf.in { animation: leaf-in .44s cubic-bezier(.45, 0, .2, 1) forwards; }
#leaf.held { clip-path: polygon(-16% 0, 100% 0, 100% 100%, -8% 100%); }
#leaf.out { animation: leaf-out .44s cubic-bezier(.5, 0, .55, 1) forwards; }
@keyframes leaf-in {
  from { clip-path: polygon(108% 0, 100% 0, 100% 100%, 116% 100%); }
  to   { clip-path: polygon(-16% 0, 100% 0, 100% 100%, -8% 100%); }
}
@keyframes leaf-out {
  from { clip-path: polygon(-16% 0, 100% 0, 100% 100%, -8% 100%); }
  to   { clip-path: polygon(-16% 0, -16% 0, -8% 100%, -8% 100%); }
}
/* the gilt edge rides the sweeping side: the same path, the same clock */
#leaf .leaf-edge {
  position: absolute; top: -4%; bottom: -4%; width: 10px; left: 108%; margin-left: -5px;
  transform: skewX(-2deg); pointer-events: none; z-index: 3;
  background: linear-gradient(90deg, rgba(201, 162, 39, 0), rgba(240, 210, 110, .95) 45%, rgba(201, 162, 39, 0));
  box-shadow: -14px 0 26px rgba(40, 24, 6, .28);
  opacity: 0;
}
#leaf.in .leaf-edge { animation: leaf-edge-in .44s cubic-bezier(.45, 0, .2, 1) forwards; }
#leaf.out .leaf-edge { animation: leaf-edge-out .44s cubic-bezier(.5, 0, .55, 1) forwards; }
@keyframes leaf-edge-in  { from { left: 108%; opacity: 1; } 92% { opacity: 1; } to { left: -16%; opacity: 0; } }
@keyframes leaf-edge-out { from { left: 100%; opacity: 1; } 92% { opacity: 1; } to { left: -16%; opacity: 0; } }
/* the rim: a double gilt rule inset from the sheet's edge */
#leaf .leaf-rim {
  position: absolute; inset: 12px; pointer-events: none; z-index: 1;
  border: 1px solid rgba(201, 162, 39, .75); border-radius: 3px;
  box-shadow: inset 0 0 0 3px rgba(255, 250, 235, .55), inset 0 0 0 4px rgba(201, 162, 39, .55);
}
/* the vine, drawn up the left margin as the leaf comes down */
#leaf .leaf-vine {
  position: absolute; left: 14px; bottom: 9%; width: 84px; height: 294px; z-index: 1; pointer-events: none;
  fill: none; stroke: #b8892e; stroke-width: 2.2; stroke-linecap: round;
}
#leaf .leaf-vine .stem { stroke-dasharray: 720; stroke-dashoffset: 720; }
#leaf.held .leaf-vine .stem, #leaf.out .leaf-vine .stem { animation: leaf-stem 1.1s cubic-bezier(.3, .2, .3, 1) forwards; }
#leaf .leaf-vine .leaves ellipse, #leaf .leaf-vine .leaves circle { fill: rgba(184, 137, 46, .85); stroke: rgba(122, 88, 24, .8); stroke-width: .8; opacity: 0; }
#leaf .leaf-vine .leaves .bud { fill: #8c3b2e; stroke: rgba(80, 30, 20, .7); }
#leaf.held .leaf-vine .leaves > *, #leaf.out .leaf-vine .leaves > * { animation: leaf-bloom .5s ease-out forwards; }
#leaf .leaf-vine .leaves > :nth-child(1) { animation-delay: .25s; } #leaf .leaf-vine .leaves > :nth-child(2) { animation-delay: .35s; }
#leaf .leaf-vine .leaves > :nth-child(3) { animation-delay: .45s; } #leaf .leaf-vine .leaves > :nth-child(4) { animation-delay: .55s; }
#leaf .leaf-vine .leaves > :nth-child(5) { animation-delay: .65s; } #leaf .leaf-vine .leaves > :nth-child(6) { animation-delay: .75s; }
#leaf .leaf-vine .leaves > :nth-child(7) { animation-delay: .85s; } #leaf .leaf-vine .leaves > :nth-child(8) { animation-delay: .95s; }
#leaf .leaf-vine .leaves > :nth-child(9) { animation-delay: 1.05s; }
@keyframes leaf-stem { to { stroke-dashoffset: 0; } }
@keyframes leaf-bloom { from { opacity: 0; transform: scale(.4); transform-box: fill-box; transform-origin: center; } to { opacity: 1; transform: scale(1); transform-box: fill-box; transform-origin: center; } }
/* the line: a rubric initial and the words, mid-page, and three dots that
   breathe while the work is done */
#leaf .leaf-line {
  position: absolute; left: 12%; right: 12%; top: 38%; z-index: 2; text-align: center;
  font: 21px/1.35 var(--serif); letter-spacing: .04em; color: var(--ink);
}
#leaf .leaf-line[hidden] { display: none; }
#leaf .leaf-initial {
  display: inline-block; font-size: 2.1em; line-height: .8; vertical-align: -.12em; margin-right: .06em;
  color: #8c3b2e; font-weight: 700; text-shadow: 0 1px 0 rgba(255, 245, 220, .8), 0 0 1px rgba(201, 162, 39, .6);
}
#leaf .leaf-dots i { display: inline-block; width: .22em; height: .22em; margin-left: .18em; border-radius: 50%; background: currentColor; opacity: .25; animation: leaf-dot 1.4s ease-in-out infinite; vertical-align: .05em; }
#leaf .leaf-dots i:nth-child(2) { animation-delay: .2s; }
#leaf .leaf-dots i:nth-child(3) { animation-delay: .4s; }
@keyframes leaf-dot { 0%, 100% { opacity: .25; } 40% { opacity: .9; } }
/* the lower margin: where the drolleries play */
#leaf .leaf-margin { position: absolute; left: 0; right: 0; bottom: 15%; height: 200px; z-index: 2; pointer-events: none; overflow: visible; }
#leaf .critter { position: absolute; bottom: 0; left: 0; transform-origin: 50% 100%; filter: drop-shadow(0 3px 3px rgba(60, 40, 12, .32)); }
#leaf .critter .face { display: block; transform-origin: 50% 100%; }
#leaf .critter img { display: block; width: 100%; transform-origin: 50% 100%; }
/* the walker: the wrapper crosses, the beast bounds */
#leaf .critter.walker { animation: leaf-cross var(--cross, 3.4s) linear infinite; }
#leaf .critter.walker img { animation: leaf-bound .58s cubic-bezier(.45, 0, .55, 1) infinite; }
#leaf .critter.walker.back { animation-direction: reverse; }
#leaf .critter.walker.back .face { transform: scaleX(-1); }
@keyframes leaf-cross { from { transform: translateX(-36vw); } to { transform: translateX(112vw); } }
@keyframes leaf-bound {
  0%, 100% { transform: translateY(0) scale(1, 1); }
  38%      { transform: translateY(-36px) scale(.95, 1.06) rotate(-3deg); }
  72%      { transform: translateY(0) scale(1.07, .93) rotate(2deg); }
}
/* the peeker: up from the foot of the page, a look, and down again */
#leaf .critter.peeker { transform: translateX(-50%) translateY(105%); animation: leaf-peek 3.4s cubic-bezier(.4, 0, .3, 1) infinite; }
@keyframes leaf-peek {
  0%        { transform: translateX(-50%) translateY(105%) rotate(0); }
  22%, 60%  { transform: translateX(-50%) translateY(10%) rotate(-2deg); }
  40%       { transform: translateX(-50%) translateY(6%) rotate(2.5deg); }
  100%      { transform: translateX(-50%) translateY(105%) rotate(0); }
}
/* the juggler: the jester stands; three dice go round above his hands */
#leaf .critter.juggler { transform: translateX(-50%); }
#leaf .critter.juggler img { animation: leaf-sway 1.8s ease-in-out infinite; }
@keyframes leaf-sway { 0%, 100% { transform: rotate(-1.5deg); } 50% { transform: rotate(1.5deg); } }
#leaf .critter.juggler .dice { position: absolute; left: 50%; top: 22%; width: 0; height: 0; }
#leaf .critter.juggler .orbit { position: absolute; left: -17px; top: -17px; width: 34px; height: 34px; animation: leaf-dice 1.8s linear infinite; }
#leaf .critter.juggler .orbit .die, #leaf .critter.juggler .orbit canvas, #leaf .critter.juggler .orbit .pip { width: 34px !important; height: 34px !important; display: block; }
#leaf .critter.juggler .orbit .pip { border-radius: 7px; background: #f1e5c8; box-shadow: inset 0 0 0 2px #8a6a2c; }
@keyframes leaf-dice {
  0%   { transform: translate(-64px, 0) rotate(0deg); }
  33%  { transform: translate(0, -132px) rotate(130deg); }
  66%  { transform: translate(64px, 0) rotate(250deg); }
  100% { transform: translate(-64px, 0) rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
  #leaf, #leaf .leaf-edge, #leaf .critter, #leaf .critter img, #leaf .critter .orbit, #leaf .leaf-vine * , #leaf .leaf-dots i { animation: none !important; }
}

/* ==========================================================================
   THE LANDING FITS THE GLASS (founder, 2026-09-26: "the initial screen with
   the animated monkey, it is longer than an iphone window and it jump and
   fits height but shrinks width. Unacceptable. Make the whole screen fit
   the viewport, with a minimum height where it begins to scroll").

   Measured in WebKit at six iPhones' visible heights before this block:
   the page ran 71 to 149 px past the window on every one (and further on
   a real phone, where Safari counts vh against the window with its bars
   hidden -- and every cap here was written in vh); the column was held to
   the TABLE's height ratio, 318 px of a 375 px SE; two of the die's magic
   stars, invisible at rest and 509 px wide, pushed the document to 594 px
   on a 375 px phone, and Safari zooms a page out to fit content wider than
   the phone -- the jump, and the shrink; and the mark, the beast and the
   pitch had no box until their art arrived, so every arrival shoved the
   page down.

   So: the landing is exactly the glass, full width, never shorter than
   540 px -- below that the page scrolls. The die's magic is clipped to the
   page. Every picture's box is reserved before it arrives. The four pieces
   keep the proportions the founder settled on a phone (the mark 0.81 of
   the beast's width, the pitch 0.716, the door 0.619) and scale together as
   one picture to the height the landing actually has, measured on its own
   box (container units): the same arithmetic in the desktop's zoomed
   column. The clearances are fractions of the width, because the frame's
   corner sprays are half the width on every portrait phone: the mark
   starts under the top flowers, the door ends above the bottom ones.
   ========================================================================== */
/* the table's height ratio is the table's alone: every other screen is the
   phone's own width */
body:not(:has(#s2.on)) #wrap { max-width: 430px; }
body:has(#s0.on) #wrap { padding: 0; min-height: max(100dvh, 540px); }
body:has(#s0.on) #page { padding: 0; }
#s0 {
  container-type: size;                   /* the stack measures the box it stands in */
  overflow: hidden; overflow: clip;       /* the die's magic never widens the page */
  padding: 0; gap: 0; min-height: 0;
  justify-content: center; align-items: center;
  --pt: max(12cqw, 40px);                 /* under the top sprays' flowers */
  --pb: calc(11.4cqw + 12px);             /* over the bottom sprays' flowers */
  --gap: clamp(6px, 1.6cqh, 18px);
  /* the stack's height in beast widths: the mark 0.81 / 2.0619, the door
     0.619 / 4.211, the pitch 0.716 / 2.2613, and the beast 1 / his own
     proportion (set by the page from the plate before it arrives) */
  --stack: calc(0.8565 + 1 / var(--beast-ar, 1.0063));
  --avail: calc(100cqh - var(--pt) - var(--pb) - 3 * var(--gap));
  --beastW: min(460px, calc(100cqw - 30px), calc(var(--avail) / var(--stack)));
  --door: calc(var(--beastW) * 0.619);
}
#s0 > .incipit { flex: none; margin: var(--pt) 0 0; }
#s0 .incipit img.logo-full { width: calc(var(--beastW) * 0.81); height: auto; aspect-ratio: 1200 / 582; }
#s0 .beast-stage { flex: none; margin: var(--gap) 0 0; aspect-ratio: var(--beast-ar, 1.0063); }
#s0 .pitch-art { flex: none; width: calc(var(--beastW) * 0.716); height: auto; aspect-ratio: 900 / 398; margin: var(--gap) 0 0; }
#s0 .row.landing:has(#landPlay) { flex: none; margin: var(--gap) 0 var(--pb); }
#s0 .row.landing button#landPlay { width: var(--door); height: calc(var(--door) / 4.211); margin: 0; }
/* the owl's frame carries more weeds, so its beast and door step back, as
   they always have; the same fit, its own proportions */
body.rim-owl #s0 {
  padding: 0; min-height: 0;
  --pt: max(18cqw, 62px);
  --pb: calc(12cqw + 16px);
  --stack: calc(0.8866 + 1 / var(--beast-ar, 1.2346));
  --beastW: min(365px, calc(100cqw - 96px), calc(var(--avail) / var(--stack)));
  --door: calc(var(--beastW) * 0.632);
}
body.rim-owl #s0 .incipit img.logo-full { width: calc(var(--beastW) * 0.895); }
body.rim-owl #s0 .pitch-art { width: calc(var(--beastW) * 0.684); }
/* the owl's picture was sized on its own at the stage's old width; it follows the stage now */
body.rim-owl #s0 #splashBeast { width: 100%; }

/* THE CORNER SPRAYS, SMALLER ON A PHONE (founder, 2026-09-26: "The corner
   decor might be too big on phones. It eats up valuable space from the
   animated rig"). The corner plates were half the screen's width on every
   portrait phone; they are 36% of it now, and the height they gave back
   goes to the landing's stack -- the rig first among it. A desktop keeps
   its corners: the 330 px cap binds there long before the width does. The
   table has its own, smaller frame and is untouched. */
:root { --fw: min(36vw, 40vh, 330px); }

/* THE THREE BARS STAY IN THEIR CUT (2026-09-26). The top-right plate was cut
   "menu-safe for the burger" (founder's kit, 2026-08-24): at the old size
   the bars' box stood exactly in that cut -- native x 959..1212, y 171..423
   of the 1303-wide plate. The bars were a fixed thumb's size while the
   plate shrank with the smaller sprays, and they came to rest on its stem.
   Now the drawn bars are placed and sized in the plate's own pixels, so
   they sit in the cut at any size (never larger than the 48 px they were
   on a desktop), and the TAP stays a thumb's width however small the
   drawing gets: a transparent margin round the bars, at least 44 px. */
.burger {
  --bsz: min(48px, calc(253 * var(--fk)));
  width: var(--bsz); height: var(--bsz);
  top: calc(var(--fi) + 271 * var(--fk) - var(--bsz) / 2);
  right: calc(var(--fi) + 183.5 * var(--fk) - var(--bsz) / 2);
  padding: calc(var(--bsz) * 0.228) calc(var(--bsz) * 0.205);
}
.burger span { height: max(2.5px, calc(var(--bsz) * 0.08)); }
.burger::after { content: ""; position: absolute; inset: min(0px, calc((var(--bsz) - 44px) / 2)); }

/* --------------------------------------------------------------------------
   THE LANDING, SECOND PASS (founder, 2026-09-26: "The left side corner
   flowers are larger than the right side. So make the left side flowers
   smaller, to match the right flowers. Now remove the 'cast the dice.
   Shape your fate' line and make the button and monkey larger and the go
   a bit lower with the button").

   THE LEFT SPRAYS MATCH THE RIGHT. The plates are each scaled by their own
   wire so the four draw one line; at that scale the left plates' flowers
   stood about 1.4 times the right plates'. The left two are drawn at 0.72
   of their size. The rails still run to the boxes' edges, so the joints
   stay closed: the top and bottom rails start where the smaller left
   plates now end, and the left rail runs between them.
   -------------------------------------------------------------------------- */
:root { --fl: 0.72; }                     /* the left plates' share of their size */
.bf-tl {
  width: calc(1182.6 * var(--fk) * var(--fl)); height: calc(1191.8 * var(--fk) * var(--fl));
  top: calc(var(--fi) - 3.1 * var(--fk) * var(--fl)); left: calc(var(--fi) - 2.1 * var(--fk) * var(--fl));
}
.bf-bl {
  width: calc(1136.8 * var(--fk) * var(--fl)); height: calc(1136.8 * var(--fk) * var(--fl));
}
.bf-t { left: calc(var(--fi) + 1179.5 * var(--fk) * var(--fl)); }
.bf-b { left: calc(var(--fi) + 1135.8 * var(--fk) * var(--fl)); }
.bf-l { top: calc(var(--fi) + 1187.7 * var(--fk) * var(--fl)); bottom: calc(var(--fi) + 1135.8 * var(--fk) * var(--fl)); }
/* THE PITCH IN THREE LINES (the plate re-cut), THE BEAST AND THE DOOR
   LARGER, THE DOOR LOWER. The stack's height in beast widths follows: the
   mark 0.81 / 2.0619, the pitch 0.716 / 3.0201, the door 0.70 / 4.211. */
#s0 {
  --pb: calc(8cqw + 10px);                /* the door sits lower: the sprays under it are smaller */
  /* the pitch plate re-cut with the plate's own leading (2026-09-27: its
     last line sat 16 px close under the others): 900 x 314, so the pitch
     term is 0.716 / 2.8662 and the stack grows by 0.0127 */
  --stack: calc(0.8377 + 1 / var(--beast-ar, 1.0063));
  --door: calc(var(--beastW) * 0.70);
}
#s0 .pitch-art { aspect-ratio: 900 / 314; }
body.rim-owl #s0 { --stack: calc(0.8748 + 1 / var(--beast-ar, 1.2346)); --door: calc(var(--beastW) * 0.70); }

/* THE WIRE KEEPS ONE THICKNESS (2026-09-26). The left plates drawn at 0.72
   drew their wire at 0.72 too, and a thinner wire met each full-width rail
   in a small step -- the fault the founder named at these very corners on
   2026-08-25 ("look at the coupling differences"). So the rails now run
   OVER the left plates, at full thickness, from just past each corner knot,
   and the plates' thinner wire lies hidden under them. Where each knot ends
   was measured on the plate: along the top-left's top at native x 90 and
   down its side at y 109; along the bottom-left's foot at x 99 and up its
   side at 104 from the bottom -- converted to the plates' drawing units
   (1182.6 / 700 per native px for the top-left, 1136.8 / 700 for the
   bottom-left) less each plate's own offset. The knot keeps the corner. */
.bf-t, .bf-b, .bf-l { z-index: 42; }
.bf-t { left: calc(var(--fi) + 149.9 * var(--fk) * var(--fl)); }
.bf-l { top: calc(var(--fi) + 181.2 * var(--fk) * var(--fl)); bottom: calc(var(--fi) + 168.9 * var(--fk) * var(--fl)); }
.bf-b { left: calc(var(--fi) + 160.8 * var(--fk) * var(--fl)); }

/* THE LEFT SPRAYS, MATCHED AGAIN (founder, 2026-09-26: "now the left corner
   flowers are smaller than the right ones. Make them larger, so they are
   equal to the right flowers"). At 0.72 the left flower heads matched the
   right ones but the sprays' reach fell short, and the eye reads both. So
   each left plate is matched to the right plate beside it on both counts
   -- the geometric mean of the scale that equals its ink and the scale that
   equals its reach, measured at a 15 Pro: the top-left against the
   top-right 0.86, the bottom-left against the bottom-right 0.82. The rails
   still start at each knot's end, scaled with its own plate. */
:root { --fl-tl: 0.86; --fl-bl: 0.82; }
.bf-tl {
  width: calc(1182.6 * var(--fk) * var(--fl-tl)); height: calc(1191.8 * var(--fk) * var(--fl-tl));
  top: calc(var(--fi) - 3.1 * var(--fk) * var(--fl-tl)); left: calc(var(--fi) - 2.1 * var(--fk) * var(--fl-tl));
}
.bf-bl { width: calc(1136.8 * var(--fk) * var(--fl-bl)); height: calc(1136.8 * var(--fk) * var(--fl-bl)); }
.bf-t { left: calc(var(--fi) + 149.9 * var(--fk) * var(--fl-tl)); }
.bf-l { top: calc(var(--fi) + 181.2 * var(--fk) * var(--fl-tl)); bottom: calc(var(--fi) + 168.9 * var(--fk) * var(--fl-bl)); }
.bf-b { left: calc(var(--fi) + 160.8 * var(--fk) * var(--fl-bl)); }


/* ==========================================================================
   THE FAR SEAT TURNS TO FACE ITS PLAYER (founder, 2026-09-26: "the 1v1
   same-seat game, the opponents seat needs to be facing the other way,
   no?" -- "Yes, lets do that"). Only in a same-screen game on a phone or
   tablet (the page sets the hooks: .total[data-turned] on #t1,
   [data-far="turned"] on #s2 and #s3).

   THE CORNER. Every rule for what sits inside the near seat's corner column
   also answers to .total[data-turned], and the far seat's own inside rules
   step aside for it (the selector rewrite of this date; specificity
   unchanged, and nothing matches differently while no seat is turned). The
   far column's box stays where the far rules put it -- the exact mirror of
   the near box -- and is turned half round in place, so the near design
   lands in the far corner upright for the player at the top. Inside it the
   near seat's variables read the far seat's values: its tint, its push at
   the half-faces stop, and its lift, negated because a lift measured on the
   screen moves the other way inside a turned box.
   ========================================================================== */
#s2 .total[data-turned] {
  rotate: 180deg;
  --tint-near: var(--tint-far);
  --face-out-near: var(--face-out-far, var(--face-out));
  --near-lift: calc(-1 * var(--far-lift, 0px));
}
/* THE FAR BOARD'S LETTERING turns where it stands. The tongue's words go
   through their own box (the score's poof animates the number's transform);
   the sums keep their nudge (`translate`) and turn about their centre; the
   badge's centring translate comes first. The board, the leather and the
   dice do not turn. */
#s2 .board-frame .flap .flap-face { position: absolute; inset: 0; }
#s2[data-far="turned"] .board-frame:has(.theirs) .flap .flap-face,
#s2[data-far="turned"] .board-frame:has(.theirs) .colsum,
#s2[data-far="turned"] .board-frame:has(.theirs) .bank,
#s2[data-far="turned"] .board-frame:has(.theirs) .round { rotate: 180deg; }
/* the badge is wider than the tongue it is centred on, and its far end lies
   over the leather roll; turned, its order reverses too, so its number stays
   on the parchment end, legible (a 4x4 put it on the red roll) */
#s2[data-far="turned"] .board-frame:has(.theirs) .meter-badge { transform: translateX(-50%) rotate(180deg); flex-direction: row-reverse; }
/* THE MIDDLE LINE faces whoever's turn it is -- stamped on the line as its
   words are laid, so a line fading out keeps facing the player it was for.
   At the boards-alone stop the words stand in the band's left half, right-
   aligned to stop 5px short of the dice; turned in place that alignment
   would mirror to the outer edge, so the turned line aligns left and the
   turn brings its words back to 5px from the divide. */
#s2[data-far="turned"] #offerStage #sysLine[data-acting="1"] { transform: rotate(180deg); }
#s2[data-far="turned"][data-focus="on"] #offerStage #sysLine[data-acting="1"] {
  transform: translateY(-50%) rotate(180deg);
  text-align: left; padding-left: 5px; padding-right: 2px;
}
/* THE VERDICT's top line is player 2's own */
#s3[data-far="turned"] #vTop { rotate: 180deg; }

/* THE GUEST SEAT CARRIES NO NAME (founder, 2026-09-26: "Same-seat does not
   really need a name ribbon, as the player is a guest and the game does not
   really count to anything. So there is no identity attached to that
   seat"). The far seat of a same-screen table shows no name ribbon, no
   name and no level: the ribbon's cloth and its tint (which carry the
   shield), the scroll, the level digits, and the faces style's scroll and
   shield. The figure, its pillar, the medallion and the mask stay: they are
   the seat's furniture, not an identity. On a desktop, where the far seat
   keeps its own design and its banner is painted into the figure's art,
   the name and the number go and the painted cloth stays. */
#s2[data-guest="1"] .board-frame:has(.theirs) .total .dw-ribbon,
#s2[data-guest="1"] .board-frame:has(.theirs) .total .dw-ribbon-tint,
#s2[data-guest="1"] .board-frame:has(.theirs) .total .pf-scroll,
#s2[data-guest="1"] .board-frame:has(.theirs) .total .pf-shield,
#s2[data-guest="1"] .board-frame:has(.theirs) .total .dw-level { display: none; }
/* AND NEITHER DOES THE HOST'S (founder, the same day: "drop it for both
   players. But keep the profile pic, we will see what we place there.
   Maybe a champion pic or smth"). The near seat's name ribbon, name and
   level go as well; both medallions stay, the place a picture will go. */
#s2[data-guest="1"] .board-frame:has(.mine) .total .dw-ribbon,
#s2[data-guest="1"] .board-frame:has(.mine) .total .dw-ribbon-tint,
#s2[data-guest="1"] .board-frame:has(.mine) .total .pf-scroll,
#s2[data-guest="1"] .board-frame:has(.mine) .total .pf-shield,
#s2[data-guest="1"] .board-frame:has(.mine) .total .dw-level { display: none; }
/* ...ON A DESKTOP TOO. There the far seat is not turned and wears its own
   pillar, whose band is painted into the pillar itself, so hiding a layer
   cannot take it away. It wears a bare cut of the same painting instead:
   the band, its shield, laurel and drape gone, the capital made whole again
   from its own clean side (up_construct), or the medallion trimmed to its
   own outline where the ribbon's curl ran behind it (up2_construct). The
   cuts and how they were made: scratchpad cut_bare_pillars.py, described in
   RIFF_NOTES under this date. */
#s2[data-guest="1"][data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct { background-image: url("/static/art/corners/up_construct_bare.webp"); }
#s2[data-guest="1"][data-corners="dwarf"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct-tint { -webkit-mask-image: url("/static/art/corners/up_construct_bare_mask.webp"); mask-image: url("/static/art/corners/up_construct_bare_mask.webp"); }
#s2[data-guest="1"][data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct { background-image: url("/static/art/corners/up2_construct_bare.webp"); }
#s2[data-guest="1"][data-corners="dwarf"][data-rig="2"] .board-frame:has(.theirs) :where(.total:not([data-turned])) .dw-construct-tint { -webkit-mask-image: url("/static/art/corners/up2_construct_bare_mask.webp"); mask-image: url("/static/art/corners/up2_construct_bare_mask.webp"); }
/* The second rig's near design hangs the band's tail behind its medallion on
   a layer of its own (the robe slot, low2_tail). With the band gone the tail
   would dangle from nothing, so it goes too, on the near seat and on a
   turned far seat. In the first rig that slot is the dwarf's real robe and
   stays. */
#s2[data-guest="1"][data-corners="dwarf"][data-rig="2"] :is(.board-frame:has(.mine), .total[data-turned]) :is(.dw-robe, .dw-robe-tint) { display: none; }

/* THE DIALS TAB STANDS CLEAR OF THE CORNER (2026-09-26). It sat on the
   bottom-left spray and hid half its flower, which made that corner read
   small while the founder was judging the four corners against each other.
   It now stands at the foot's centre, between the two bottom sprays.
   Staging only, like the dials themselves. */
#dieDialsTab { left: 50%; bottom: 10px; transform: translateX(-50%); min-height: 26px; padding: 0 12px; }

/* ART CUT TO THE SCREEN (founder, 2026-09-26: "Why are phone-screen assets
   f-ing 690kb? ... We should have that"). The CSS backgrounds of the landing
   and the frame take their cut here: the full cut on a desktop, the phone
   cut under 600 px wide (a 430-wide phone at 3x needs 471 px of plate and
   702 px of PLAY plaque). The masters stay for anything not yet cut.
   tools/art_variants.py writes the cuts. */
#s0 .row.landing button#landPlay, .hub-doors #hubPlay { background-image: url("/static/art/cbtn_play.w880.webp"); }
.bf-tr { background-image: url("/static/art/bf_tr.w700.webp"); }
.bf-br { background-image: url("/static/art/bf_br.w700.webp"); }
body.rim-owl .bf-tr { background-image: url("/static/art/ow_tr.webp"); }
body.rim-owl .bf-br { background-image: url("/static/art/ow_br.webp"); }
@media (max-width: 600px) {
  #s0 .row.landing button#landPlay, .hub-doors #hubPlay { background-image: url("/static/art/cbtn_play.w720.webp"); }
  .bf-tr { background-image: url("/static/art/bf_tr.w480.webp"); }
  .bf-br { background-image: url("/static/art/bf_br.w480.webp"); }
  body.rim-owl .bf-tr { background-image: url("/static/art/ow_tr.webp"); }
  body.rim-owl .bf-br { background-image: url("/static/art/ow_br.webp"); }
}

/* THE REVIEW'S ART FIXES (2026-09-26).
   - The menu's PLAY plaque draws 291-295 px wide on a phone and needs the
     880 cut at 3x; the 720 phone cut was 17-19% short (soft). Only the
     landing's PLAY (168-280 px) takes the phone cut now.
   - A plain (1x) desktop draws the corner plates at 290-334 px and the
     landing PLAY at 250-345 px: it takes the smaller cuts, a dense screen
     the larger. Phones keep their cut from the query above. */
.bf-tr {
  background-image: -webkit-image-set(url("/static/art/bf_tr.w480.webp") 1x, url("/static/art/bf_tr.w700.webp") 2x);
  background-image: image-set(url("/static/art/bf_tr.w480.webp") 1x, url("/static/art/bf_tr.w700.webp") 2x);
}
.bf-br {
  background-image: -webkit-image-set(url("/static/art/bf_br.w480.webp") 1x, url("/static/art/bf_br.w700.webp") 2x);
  background-image: image-set(url("/static/art/bf_br.w480.webp") 1x, url("/static/art/bf_br.w700.webp") 2x);
}
#s0 .row.landing button#landPlay {
  background-image: -webkit-image-set(url("/static/art/cbtn_play.w720.webp") 1x, url("/static/art/cbtn_play.w880.webp") 2x);
  background-image: image-set(url("/static/art/cbtn_play.w720.webp") 1x, url("/static/art/cbtn_play.w880.webp") 2x);
}
.hub-doors #hubPlay { background-image: url("/static/art/cbtn_play.w880.webp"); }
@media (max-width: 600px) {
  #s0 .row.landing button#landPlay { background-image: url("/static/art/cbtn_play.w720.webp"); }
  .bf-tr { background-image: url("/static/art/bf_tr.w480.webp"); }
  .bf-br { background-image: url("/static/art/bf_br.w480.webp"); }
  .hub-doors #hubPlay { background-image: url("/static/art/cbtn_play.w880.webp"); }
}

/* A plain desktop takes the menu's PLAY plaque at the landing's own 720 cut
   (already in the cache), a dense screen the 880 (2026-09-26). */
.hub-doors #hubPlay {
  background-image: -webkit-image-set(url("/static/art/cbtn_play.w720.webp") 1x, url("/static/art/cbtn_play.w880.webp") 2x);
  background-image: image-set(url("/static/art/cbtn_play.w720.webp") 1x, url("/static/art/cbtn_play.w880.webp") 2x);
}
@media (max-width: 600px) { .hub-doors #hubPlay { background-image: url("/static/art/cbtn_play.w880.webp"); } }

/* THE LEFT CORNERS WEAR THEIR OWN FLOWERS AGAIN (founder, 2026-09-27: "Now
   the corner flowers are mirrored from one side to the other. Noo. Bad bad
   :) Bring back the left side flowers pls"). The mirror is gone; the left
   plates are their own paintings, at the scale between the two the founder
   judged: at full size they read larger than the right, at 0.86 / 0.82
   smaller, so the top-left is drawn at 0.93 and the bottom-left at 0.91.
   The rails still start at each knot's end, scaled with its plate (the
   --fl-tl / --fl-bl rules above). Each left plate takes its own cut: the
   480 on a phone, the 480 or 700 by density elsewhere. */
:root { --fl-tl: 0.93; --fl-bl: 0.91; }
.bf-tl {
  background-image: -webkit-image-set(url("/static/art/bf_tl.w480.webp") 1x, url("/static/art/bf_tl.w700.webp") 2x);
  background-image: image-set(url("/static/art/bf_tl.w480.webp") 1x, url("/static/art/bf_tl.w700.webp") 2x);
}
.bf-bl {
  background-image: -webkit-image-set(url("/static/art/bf_bl.w480.webp") 1x, url("/static/art/bf_bl.w700.webp") 2x);
  background-image: image-set(url("/static/art/bf_bl.w480.webp") 1x, url("/static/art/bf_bl.w700.webp") 2x);
}
@media (max-width: 600px) {
  .bf-tl { background-image: url("/static/art/bf_tl.w480.webp"); }
  .bf-bl { background-image: url("/static/art/bf_bl.w480.webp"); }
}

/* THE DIALS PANE, NO WIDER THAN 600 PX (founder, 2026-09-27: "On desktop,
   the dials pane that pops up is full screen. That is too wide and
   tiresome. Cap its width pls at 600px"). It stays pinned 8 px from each
   side, so a phone is unchanged; on a wider screen the auto margins centre
   it over the DIALS tab. Staging only, like the dials. */
#dieDials { max-width: 600px; margin-left: auto; margin-right: auto; }

/* ==========================================================================
   THE DIE'S ENTRANCE, ONE GESTURE (founder, 2026-09-27: "The landing die
   appearance sequence is staggered in an odd way. It could be smoother.
   Also the arc under it is skewed in an odd way. It could have its origin
   in a more natural place").

   Filmed first: the old coil began in mid-air, 16 px above the palm, squashed
   to 55% of its width into tall, leaning loops; a faint die popped in half
   way up it, grew in two pushes, stopped, shrank from 1.03 to 1; and the
   float and lean then began at a RANDOM point of their cycle, so the die
   jumped as much as 6 px and tipped at the moment the entrance ended.

   Now: the swirl rises from the palm (the markup's path), drawn in one
   ease; the die rises from the palm along the swirl's axis, a little behind
   its drawing front, growing and brightening on one curve; and while it
   settles, the float and the lean ease into the very pose their cycles begin
   from (the rig starts them at phase 0 for this beast), so nothing jumps.
   ========================================================================== */
body.beast-royal_baboon .magic-spring-strand.is-primary,
body.beast-royal_baboon .magic-spring-strand.is-secondary {
  transform-origin: 118px 166px;               /* the swirl's die end */
  transform: scale(var(--magic-arc-scale, 1), var(--magic-arc-source-scale, 1));
}
#magicTraceSpring.is-active path {
  animation: springTraceRise var(--magic-duration, 1100ms) cubic-bezier(.45, .05, .35, 1) both;
}
@keyframes springTraceRise {
  0%   { opacity: 0; stroke-dashoffset: 100; }
  8%   { opacity: .78; }
  62%  { opacity: .86; stroke-dashoffset: 0; }
  80%  { opacity: .7; stroke-dashoffset: 0; }
  100% { opacity: 0; stroke-dashoffset: -6; }
}
/* the die starts in the palm (in its own box: 15% right, 158% down, at the
   die's first size; divided by --die-grow, the palm stays where it is) */
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"] #dieTurnly {
  transform: translate(calc(15% / var(--die-grow, 1)), calc(158% / var(--die-grow, 1))) scale(calc(.12 / var(--die-grow, 1)));
  transform-origin: 50% 50%;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-emerging #dieTurnly {
  animation: dieConjureRise var(--conjure-emerge-duration, 700ms) cubic-bezier(.32, .08, .22, 1) both;
}
@keyframes dieConjureRise {
  0%   { opacity: 0;   transform: translate(calc(15% / var(--die-grow, 1)), calc(158% / var(--die-grow, 1))) scale(calc(.12 / var(--die-grow, 1))); }
  22%  { opacity: .6; }
  48%  { opacity: .95; }
  100% { opacity: 1;   transform: translate(0, 0) scale(1.03); }
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-settling #dieTurnly {
  animation: dieConjureSettle var(--conjure-settle-duration, 360ms) cubic-bezier(.3, .6, .3, 1) both;
}
/* while the die settles, the float and the lean glide into their first pose */
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-settling #dieBob {
  transform: translate3d(-1.36%, calc(var(--die-bob) * -.28 * var(--die-motion)), 0);
  transition: transform var(--conjure-settle-duration, 360ms) cubic-bezier(.3, .6, .3, 1);
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="entrance"].is-die-settling #dieTilt {
  transform: rotate(calc(var(--die-lean) * -.52));
  transition: transform var(--conjure-settle-duration, 360ms) cubic-bezier(.3, .6, .3, 1);
}

/* THE DIE SUMMONED AGAIN (founder, 2026-09-27): the die swells a little and
   goes up in smoke (the rig's poof puffs out from its centre); its rays and
   aura go with it. The entrance that follows brings the new die. */
body.beast-royal_baboon .beast-rig-live.is-die-vanishing #dieTurnly {
  animation: dieVanish 520ms cubic-bezier(.5, 0, .75, .45) forwards;
}
@keyframes dieVanish {
  0%   { opacity: 1; transform: scale(1); filter: blur(0); }
  28%  { opacity: 1; transform: scale(1.1); filter: blur(0); }
  100% { opacity: 0; transform: scale(.35); filter: blur(3px); }
}
body.beast-royal_baboon .beast-rig-live.is-die-vanishing #dieMagicRays {
  animation: none; opacity: 0; transition: opacity .45s ease-in;
}

/* the poof is drawn over the vanishing die, not behind it (2026-09-27) */
body.beast-royal_baboon .beast-rig-live.is-die-vanishing #dieSmokeField { z-index: 4; }

/* THE DIE BREAKS (2026-09-27): while the film cracks the die's picture, its
   glow, aura and rays go out, so no halo lingers round an empty palm. */
body.beast-royal_baboon .beast-rig-live.is-die-breaking .die-glow-rig,
body.beast-royal_baboon .beast-rig-live.is-die-breaking .die-aura,
body.beast-royal_baboon .beast-rig-live.is-die-breaking #dieMagicRays {
  animation: none; opacity: 0; transition: opacity .3s ease-in;
}

/* THE HEAP (founder, 2026-09-27): the landing's dust gathers on the ground
   at his foot. Its canvas spans his width; its foot is the ground line, 93%
   down his picture, and it is tall enough for the heap at its largest (a
   tenth of his width) and the glints above it. In front of him, behind
   the die. */
#dieDustHeap {
  position: absolute; left: 0; bottom: 7%;
  width: 100%; height: 26%;
  z-index: 2; pointer-events: none;
}

/* THE DIE UNDER THE HAND (founder, 2026-09-27): the idle die takes the
   pointer -- its own box, and a little more over the height it floats
   through, so a resting mouse does not slip on and off it as it bobs; the
   fields of light and smoke around it stay out of the way. Held under a
   mouse, it stops leaning where it is (the rig stops its turn) and only
   floats. */
body.beast-royal_baboon .beast-rig-live[data-magic-phase="idle"] #dieLev {
  pointer-events: auto; cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
body.beast-royal_baboon .beast-rig-live[data-magic-phase="idle"] #dieLev > * { pointer-events: none; }
body.beast-royal_baboon .beast-rig-live[data-magic-phase="idle"] #dieLev::before {
  content: ""; position: absolute; left: -15%; right: -15%; top: -45%; bottom: -35%;
}
body.beast-royal_baboon .beast-rig-live.is-die-held #dieTilt { animation-play-state: paused; }

/* THE LIDS TREMBLE, SIDEWAYS ONLY (founder, 2026-09-27: "a lot more subtle
   and just on the lateral axis, no up-down"; then "direction is perfect.
   Its just too subtle now. Make it a tiny bit more apparent. And maybe a
   bit longer. A bit"): the shut-lid layer shivers left and right on an
   uneven beat through the last 720 ms before his eyes open (beast-rig
   tremorLids). Per cent of the layer, which is his plate: 0.26% is about
   1 px at phone width (the approved-direction cut peaked at 0.16%, 520 ms;
   the beat is kept at ~55 ms a swing). No vertical movement, no frame
   change -- the lids never part. */
body.beast-royal_baboon .beast-rig-live.is-lids-trembling #beastFaceBlinkClosed {
  animation: lidQuiver 720ms linear both;
}
@keyframes lidQuiver {
  0%   { transform: translate3d(0, 0, 0); }
  8%   { transform: translate3d(-.18%, 0, 0); }
  15%  { transform: translate3d(.12%, 0, 0); }
  23%  { transform: translate3d(-.1%, 0, 0); }
  31%  { transform: translate3d(.22%, 0, 0); }
  38%  { transform: translate3d(-.16%, 0, 0); }
  46%  { transform: translate3d(.08%, 0, 0); }
  54%  { transform: translate3d(-.2%, 0, 0); }
  62%  { transform: translate3d(.26%, 0, 0); }
  69%  { transform: translate3d(-.14%, 0, 0); }
  77%  { transform: translate3d(.1%, 0, 0); }
  85%  { transform: translate3d(-.12%, 0, 0); }
  92%  { transform: translate3d(.06%, 0, 0); }
  100% { transform: translate3d(0, 0, 0); }
}

/* THE DIE AS A CLICK TOY (founder, 2026-09-27): taps come fast, so the
   phone's double-tap zoom is kept off the die. */
body.beast-royal_baboon .beast-rig-live[data-magic-phase="idle"] #dieLev { touch-action: manipulation; }

/* BUBBLES LIKE CO2 IN WATER (founder, 2026-09-27): the click toy's bubbles.
   Small and round, drawn as the page draws a pearl -- a sepia line, a faint
   wash of the die's own glow, one point of white lead -- they rise from the
   die in little chains, wobbling a hair from side to side, speeding up and
   swelling as they go, and pop at the top (beast-rig hitBubbles). */
.die-fizz-bubble {
  position: absolute; left: var(--fizz-x, 50%); top: var(--fizz-y, 40%);
  width: var(--fizz-size, 3%); aspect-ratio: 1; border-radius: 50%;
  box-sizing: border-box;
  border: 1px solid rgba(62, 42, 20, .55);
  background: rgba(var(--die-glow-hue, 255, 190, 74), .12);
  box-shadow: inset 0 0 0 1px rgba(255, 252, 240, .32);
  opacity: 0; pointer-events: none;
  transform: translate(-50%, -50%);
}
.die-fizz-bubble::before {
  content: ""; position: absolute; left: 20%; top: 16%; width: 32%; height: 32%;
  border-radius: 50%; background: rgba(255, 253, 246, .94);
}
.die-fizz-bubble.is-active.is-fizz {
  animation: fizzRise var(--effect-duration, 900ms) linear both;
}
@keyframes fizzRise {
  0%   { opacity: 0; left: var(--fizz-x); top: var(--fizz-y);
         transform: translate(-50%, -50%) scale(.45); }
  8%   { opacity: var(--fizz-alpha, .9); }
  20%  { left: calc(var(--fizz-x) + var(--fizz-wobble)); top: calc(var(--fizz-y) - var(--fizz-rise) * .06);
         transform: translate(-50%, -50%) scale(.6); }
  40%  { left: calc(var(--fizz-x) - var(--fizz-wobble) * .8); top: calc(var(--fizz-y) - var(--fizz-rise) * .2);
         transform: translate(-50%, -50%) scale(.74); }
  60%  { left: calc(var(--fizz-x) + var(--fizz-wobble) * .7); top: calc(var(--fizz-y) - var(--fizz-rise) * .42);
         transform: translate(-50%, -50%) scale(.86); }
  80%  { left: calc(var(--fizz-x) - var(--fizz-wobble) * .5); top: calc(var(--fizz-y) - var(--fizz-rise) * .7);
         transform: translate(-50%, -50%) scale(.97); opacity: var(--fizz-alpha, .9); }
  94%  { left: calc(var(--fizz-x) + var(--fizz-wobble) * .3); top: calc(var(--fizz-y) - var(--fizz-rise) * .94);
         transform: translate(-50%, -50%) scale(1.06); opacity: var(--fizz-alpha, .9); }
  100% { left: calc(var(--fizz-x) + var(--fizz-wobble) * .3); top: calc(var(--fizz-y) - var(--fizz-rise));
         transform: translate(-50%, -50%) scale(1.5); opacity: 0; }
}

/* THE HALO UNDER THE HAND (founder, 2026-09-27: "when clicking and hovering,
   maybe the halo can intensify"). One registered number, the halo's boost:
   0 at rest, so the halo at rest is exactly as it was; 0.55 while a mouse
   rests on the die, eased in and out; and each hit flares it higher for a
   moment (beast-rig flareHalo). The rig's washes and rays grow by up to a
   fifth and brighten with it; the aura behind brightens (its own pulse owns
   its size). */
@property --die-halo-boost {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}
/* set on the glow rig and the aura themselves, not inherited from the stage
   (2026-09-28): changing a number every element under the stage inherits
   restyled all of them, every frame of a hover or a flare */
body.beast-royal_baboon .beast-rig-live .die-glow-rig,
body.beast-royal_baboon .beast-rig-live .die-aura { transition: --die-halo-boost .35s ease; }
body.beast-royal_baboon .beast-rig-live.is-die-held .die-glow-rig,
body.beast-royal_baboon .beast-rig-live.is-die-held .die-aura { --die-halo-boost: .55; }
body.beast-royal_baboon .beast-rig-live .die-glow-rig {
  transform: translate(-50%, -50%) scale(calc(var(--die-glow-radius-scale) * (1 + var(--die-halo-boost, 0) * .22)));
  filter: brightness(calc(var(--die-glow-brightness) * (1 + var(--die-halo-boost, 0) * .7)));
}
body.beast-royal_baboon .beast-rig-live.is-engaged .die-glow-rig {
  filter: brightness(calc(var(--die-glow-brightness) * (1 + var(--die-halo-boost, 0) * .7))) saturate(1.14);
}
body.beast-royal_baboon .beast-rig-live .die-aura {
  filter: brightness(calc(1 + var(--die-halo-boost, 0) * .5));
}
body.beast-royal_baboon .beast-rig-live.is-engaged .die-aura {
  filter: saturate(1.25) brightness(calc(1.18 * (1 + var(--die-halo-boost, 0) * .5)));
}

/* THE DIE LEADS THE WAY (founder, 2026-09-27): the landing's die flying to
   the middle over rising parchment, between the landing and the menu. The
   page sets the die's box (the middle of the screen) and animates it. Its
   light is the preloader's breathing light, in the die's own glow colour. */
#dieWay { position: fixed; inset: 0; z-index: 9001; display: none; pointer-events: auto; }
#dieWay.on { display: block; }
#dieWay .dw-veil { position: absolute; inset: 0; opacity: 0; }
#dieWay .dw-die { position: absolute; opacity: 0; will-change: transform, opacity; }
#dieWay .dw-die::before {
  content: ""; position: absolute; inset: -34%; border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--dw-glow, 255, 214, 120), .46) 0%,
    rgba(var(--dw-glow, 255, 214, 120), .16) 38%, rgba(var(--dw-glow, 255, 214, 120), 0) 68%);
  animation: plGlow 2.6s ease-in-out infinite alternate;
}
#dieWay .dw-die canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
/* THE HOME ARRIVES LIKE A SCROLL (founder, 2026-09-28: "Menus that arrive
   like a scroll"; of Card Crawl 2, "the way the menus drag onto the screen,
   like a scroll"). One movement, for the whole home at once -- one trigger,
   one effect: it is drawn down onto the page from above and settles, as a
   hanging scroll let down. A transform only, so it rides the compositor. */
#s10.arriving .home { animation: homeArrive .5s cubic-bezier(.22, 1.18, .36, 1) both; }
@keyframes homeArrive { from { transform: translateY(-46px); } to { transform: none; } }
@media (prefers-reduced-motion: reduce) { #s10.arriving .home { animation: none; } }

/* IDLE PIECES ARE NOT DRAWN (2026-09-28): the pools of smoke, sparks, stars
   and bubbles -- some 290 pieces, each with a drop-shadow filter -- sat on
   the page invisible but laid out and filtered. A piece is drawn only while
   it plays; the cue that plays it forces a layout before it starts, so its
   animation begins from its first frame as before. */
.beast-rig-live .die-smoke-actor:not(.is-active),
.beast-rig-live .die-spark-actor:not(.is-active),
.beast-rig-live .die-fizz-bubble:not(.is-active),
.beast-rig-live .baboon-bubble-star:not(.is-active),
.beast-rig-live .baboon-large-star:not(.is-active) { display: none; }

/* ==========================================================================
   A COOLER TABLE (2026-09-28, the landing's heat audit carried to the table:
   "I left the landing on for a while on my phone. It got hot").

   THE BRIBE'S PULSE, LIT FROM BEHIND. The bribe button and a bribed column
   breathed their glow by animating box-shadow, which the browser repaints
   on the main thread every frame: measured at phone speed, the idle table
   was 13% busy and 2.6% without it. The same two glows are now laid on a
   layer of their own (::before) whose OPACITY breathes -- work the
   compositor does alone. At rest each carries the pulse's low glow; the
   layer brings its high glow in and out on the same beat.

   THE GHOST DIE'S TILT. Its breath animated a transform built from a
   variable (rotate(var(--tilt)) scale(...)); the tilt now stands on the
   separate `rotate` property and only `scale` and opacity breathe.
   ========================================================================== */
#bribeBtn {
  position: relative;
  box-shadow: 0 0 0 2px rgba(255,214,10,.70), 0 0 16px rgba(255,214,10,.35);
  animation: none;
}
#bribeBtn::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 0 2px rgba(255,236,120,1), 0 0 26px rgba(255,214,10,.62);
  opacity: 0; animation: bribeglow 1.4s ease-in-out infinite;
}
.col.bribed {
  box-shadow: 0 0 0 2px rgba(255,214,10,.70), 0 0 16px rgba(255,214,10,.35);
  animation: none;
}
.col.bribed::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 0 2px rgba(255,236,120,1), 0 0 26px rgba(255,214,10,.62);
  opacity: 0; animation: bribeglow 2.2s ease-in-out infinite;
}
.col.bribed.unnamed::before { animation-duration: 1.1s; }   /* waiting for the figure: the quicker beat, as before */
@keyframes bribeglow {
  0%, 100% { opacity: 0; }
  50%      { opacity: 1; }
}
#offerArea .ghost-die { rotate: var(--tilt); animation-name: ghost-breathe-still; }
@keyframes ghost-breathe-still {
  0%, 100% { opacity: .45; scale: .95; }
  50%      { opacity: .95; scale: 1.05; }
}
@media (prefers-reduced-motion: reduce) {
  #bribeBtn::before, .col.bribed::before { animation: none; opacity: .5; }
  #offerArea .ghost-die { animation: none; }
}

/* ==========================================================================
   ONE TRIGGER, ONE EFFECT, AT THE TABLE (founder, 2026-09-28: "Nothing should
   trigger more than 1 animation at any one time"), and the filters that
   breathed on the main thread.

   THE BRIBE BUTTON breathes only while it is a hand to play (.live); idle,
   it stands still like every other idle hand on the bar. And while the
   bribe's own fuse burns, the fuse is the one moving thing: the bribed
   column's ring stands lit rather than breathing beside it.

   A PAIR WAITING TO BE CHOSEN keeps its gold glow hugging the dice, held
   rather than breathing: the breath was a drop-shadow filter animated over
   the dice's canvas, redrawn by the browser every frame (measured: half the
   work of a table waiting on a choice). The nudge's wiggle no longer
   restarts it.

   THE ARMED REROLL AND CONCEDE still ask, but their breath is a layer's
   opacity, which the compositor does alone, not a filter.

   ONE CALL TO ACT. Before a hand cast, the plaque's hand beckoned and the
   ghost dice breathed in the middle, both saying "press"; the ghost dice,
   where the eye already is, carry it, and the hand points without moving.
   With auto on, the die on the plaque and the sign in the middle both
   turned; the sign turns, and the plaque's die stands.

   THE NAMES glide by script now, as plain pixels (see rollNames).

   A ROPE FROZEN BY THE PLACING TAP stands exactly as a held rope does, its
   end fade included, until the die is down and it goes (freezeFuses).
   ========================================================================== */
#bribeBtn:not(.live)::before { animation: none; opacity: 0; }
#s2.simony-lit .col.bribed::before { animation: none; opacity: .6; }
#offerArea.undecided { animation: none; filter: drop-shadow(0 0 6px rgba(201, 162, 39, .5)); }
#offerArea.shake { animation: none; }
#reroll.armed, #concede.armed { animation: none; position: relative; }
#reroll.armed::before, #concede.armed::before {
  content: ""; position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  box-shadow: 0 0 8px 2px rgba(201, 162, 39, .55); opacity: 0;
  animation: bribeglow 1.5s ease-in-out infinite;
}
#concede.armed::before { box-shadow: 0 0 8px 2px rgba(200, 80, 40, .55); }
#s2 #bar > #cast .hand { animation: none; }
#s2 #bar > #cast.auto .cube { animation: none; }
#s2 .total .pf-scroll.rolling .who { animation: none; will-change: auto; }
#s2 .board-frame .fuse.lit.frozen, #s2 .board-frame .fuse.lit.frozen .rope, #s2 .board-frame .fuse.lit.frozen .spark { animation-play-state: paused !important; }

/* ==========================================================================
   THE VERDICT, INKED (step 4, 2026-09-28; staging -- the page sets .ink on
   #s3 only there until the founder's word, and lays the sheet inside the
   table, under the drawer and the bar).

   THE VEIL is light and still: the table stays lit, a touch brighter on the
   winner's half, no black-out and no blur. THE WORDS are ink on parchment --
   the winner's in vermilion rubric, the loser's and a draw in iron gall --
   not gold: gold laid on parchment is the founder's "never" for text. A word
   that stands on leather takes the leather's own light ink. THE CARD is one
   parchment slip in the lane between the masks: the score, what moved, the
   purse, the hands; chunky figures, nothing italic, nothing small.
   THE SHEET ARRIVES as one fade (opacity only); nothing inside it moves.
   ========================================================================== */
#s3.ink {
  z-index: 45; inset: 0 0 calc(66px + env(safe-area-inset-bottom, 0px)) 0;
  padding: 30px 18px 22px; pointer-events: none;
  background: rgba(43, 32, 24, .16); backdrop-filter: none;
}
#s3.ink[data-won="near"] { background: linear-gradient(to bottom, rgba(43, 32, 24, .22) 0 42%, rgba(43, 32, 24, .10) 58% 100%); }
#s3.ink[data-won="far"]  { background: linear-gradient(to bottom, rgba(43, 32, 24, .10) 0 42%, rgba(43, 32, 24, .22) 58% 100%); }
#s3.ink.inking { animation: ink-in 520ms ease-out both; }
@keyframes ink-in { from { opacity: 0; } to { opacity: 1; } }
#s3.ink.on { display: block; }                    /* its pieces are seated by measure (seatVerdict) */
#s3.ink.measuring { display: block; visibility: hidden; }
#s3.ink .verdict { position: absolute; left: 0; right: 0; margin: 0; }
#s3.ink .verdict.tight { text-shadow: 0 0 4px rgba(239, 229, 207, .95), 0 0 10px rgba(239, 229, 207, .9), 0 1px 0 rgba(255, 248, 230, .75); }
#s3.ink .verdict, #s3.ink .verdict.gilt {
  font: 700 44px/1.05 var(--serif); letter-spacing: .03em;
  background: none; -webkit-background-clip: border-box; background-clip: border-box;
  filter: none; color: var(--ink); -webkit-text-fill-color: currentColor;
  text-shadow: 0 1px 0 rgba(255, 248, 230, .75);
}
#s3.ink .verdict.win { color: var(--vermilion); }
#s3.ink .verdict.benched { display: none; }
#s3.ink .card-word { font: 700 36px/1.05 var(--serif); letter-spacing: .03em; color: var(--ink); text-shadow: 0 1px 0 rgba(255, 248, 230, .75); margin-bottom: 4px; }
#s3.ink .card-word.win { color: var(--vermilion); }
#s3.ink .verdict.on-leather { color: #f6ead0; text-shadow: 0 1px 2px rgba(0, 0, 0, .75); }
#s3.ink .verdict.win.on-leather { color: #ffd9a8; }
#s3.ink #verdictCard {
  position: absolute; left: 50%; top: var(--card-top, 38%); transform: translateX(-50%);
  pointer-events: auto; box-sizing: border-box;
  width: var(--card-w, 250px); padding: 12px 14px 10px; text-align: center;
}
#s3.ink #verdictCard > .scroll {
  border: 0; border-image: none; background: none; box-shadow: none; padding: 0; margin: 0;
}
#s3.ink #verdictCard .scroll > .legend { display: none; }
#s3.ink .totals { font: 700 28px/1.1 var(--serif); color: var(--ink); }
#s3.ink .ledger-moved { margin-top: 6px; font: 700 15px/1.25 var(--serif); color: var(--ink); }
#s3.ink .ledger-moved b { font-size: 26px; }
#s3.ink .ledger-note { margin-top: 2px; font: 600 15px/1.25 var(--serif); color: var(--ink-soft); }
#s3.ink .ledger-purse, #s3.ink .ledger-rating { margin-top: 4px; font: 600 15px/1.25 var(--serif); color: var(--ink-soft); }
#s3.ink .ledger-purse b, #s3.ink .ledger-rating b { font-size: 19px; font-weight: 700; color: var(--ink); }
#s3.ink #verdictCard .row { margin-top: 10px; }
#s3.ink #verdictCard .row button { padding: 10px 8px; min-height: 44px; }
@media (prefers-reduced-motion: reduce) { #s3.ink.inking { animation-duration: 200ms; } }

/* THE POT ON THE WINNER'S FLAP: "pot:" over the figure over its currency,
   in the flap's own lettering and ink */
#s2 .board-frame .flap .flap-unit { display: none; }
#s2 .board-frame .flap.money .flap-label { top: 19%; font-size: calc(var(--flap-h) * 0.18); }
#s2 .board-frame .flap.money .flap-n { top: 50%; font-size: calc(var(--flap-h) * 0.36); }
#s2 .board-frame .flap.money .flap-n[data-len="4"] { font-size: calc(var(--flap-h) * 0.30); }
#s2 .board-frame .flap.money .flap-n[data-len="5"], #s2 .board-frame .flap.money .flap-n[data-len="6"] { font-size: calc(var(--flap-h) * 0.25); }
#s2 .board-frame .flap.money .flap-unit {
  display: block; position: absolute; left: 0; right: 0; top: 80%; transform: translateY(-50%);
  text-align: center; font: 700 calc(var(--flap-h) * 0.15)/1 var(--serif); font-style: normal; white-space: nowrap;
}
#s2 .board-frame:has(.theirs) .flap .flap-unit { color: var(--ink-far, #f1e4c4); text-shadow: var(--ink-far-shadow, 0 1px 1px rgba(0, 0, 0, .6)); }
#s2 .board-frame:has(.mine) .flap .flap-unit { color: var(--ink-near, #f1e4c4); text-shadow: var(--ink-near-shadow, 0 1px 1px rgba(0, 0, 0, .6)); }

/* DURER'S SQUARE, IN PIXIE DUST (founder, 2026-10-02): the pixie, her trail and
   the figures are all drawn on one small canvas over the boards (index.html,
   DURER'S SQUARE), there only while the square plays */
.durer-dust { position: fixed; z-index: 50; pointer-events: none; }

/* a finished table: nothing loops under the verdict */
#s2[data-over="1"] #bar > #cast.auto .cube, #s2[data-over="1"] #offerStage #stageAuto.on svg { animation: none; }

/* THE ROPE BURNS ON THE COMPOSITOR (2026-09-28, measured at phone speed: a
   burning rope made the table four times busier than at rest -- its burn
   animated `width` and its spark `left`, a layout and a repaint of the
   masked cord every frame, for the last ten to twenty-five seconds of every
   round). The same rope, the same burn, moved by transforms alone:

   the ROPE is a window onto the cord. The window slides out toward the
   burnt end while the cord inside slides back by exactly as much, so the
   cord stands still on the leather and only the window's edge -- the
   burning end -- moves along it; the ash, hung on that edge, rides with it.
   The buried end's fade is the cord's own (its mask), so it stays where it
   was drawn.

   the SPARK rides a track as wide as the rope, which slides the same way:
   the spark sits on the track's end, so it is carried from one end of the
   rope to the other. Its flicker is its own. The ride keeps its name
   (fuse-ride), because its end is what sets off the burst.

   Paused, held and frozen ropes stand exactly as before: every part of the
   burn is listed where the pauses are. */
#s2 .board-frame .fuse .rope, #simonyFuse .rope {
  overflow: hidden; background-image: none; -webkit-mask-image: none; mask-image: none;
}
#s2 .board-frame .fuse .rope::before, #simonyFuse .rope::before {
  content: ""; position: absolute; inset: 0;
  background-image: url("/static/art/corners/fuse_rope.webp");
  background-repeat: repeat-x; background-size: auto 100%;
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 20px);
  mask-image: linear-gradient(to right, transparent 0, #000 20px);
}
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse .rope::before {
  -webkit-mask-image: linear-gradient(to left, transparent 0, #000 20px);
  mask-image: linear-gradient(to left, transparent 0, #000 20px);
}
#s2 .board-frame .fuse .ash, #simonyFuse .ash { z-index: 1; }
#s2 .board-frame .fuse .spark-track, #simonyFuse .spark-track { position: absolute; inset: 0; pointer-events: none; }
#s2 .board-frame .fuse.lit .rope         { animation: fuse-window var(--fuse-ms, 10000ms) linear var(--fuse-in, 0ms) forwards; }
#s2 .board-frame .fuse.lit .rope::before { animation: fuse-still  var(--fuse-ms, 10000ms) linear var(--fuse-in, 0ms) forwards; }
#s2 .board-frame .fuse.lit .spark-track  { animation: fuse-ride   var(--fuse-ms, 10000ms) linear var(--fuse-in, 0ms) forwards; }
#s2 .board-frame .fuse.lit .spark        { animation: fuse-flicker 240ms steps(3) infinite; }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse.lit .rope         { animation-name: fuse-window-r; }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse.lit .rope::before { animation-name: fuse-still-r; }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse.lit .spark-track  { animation-name: fuse-ride-r; }
#s2[data-rolls="opposed"] .board-frame:has(.mine) .fuse.lit .spark        { animation: fuse-flicker 240ms steps(3) infinite; }
#simonyFuse.lit .rope         { animation: fuse-window var(--fuse-ms, 20000ms) linear forwards; }
#simonyFuse.lit .rope::before { animation: fuse-still  var(--fuse-ms, 20000ms) linear forwards; }
#simonyFuse.lit .spark-track  { animation: fuse-ride   var(--fuse-ms, 20000ms) linear forwards; }
#simonyFuse.lit .spark        { animation: fuse-flicker 240ms steps(3) infinite; }
#s2 .board-frame .fuse.lit.out .rope::before, #s2 .board-frame .fuse.lit.out .spark-track,
#s2 .board-frame .fuse.lit.held .rope::before, #s2 .board-frame .fuse.lit.held .spark-track,
#s2 .board-frame .fuse.lit.frozen .rope::before, #s2 .board-frame .fuse.lit.frozen .spark-track { animation-play-state: paused !important; }
@keyframes fuse-window   { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@keyframes fuse-still    { from { transform: translateX(0); } to { transform: translateX(100%); } }
@keyframes fuse-window-r { from { transform: translateX(0); } to { transform: translateX(100%); } }
@keyframes fuse-still-r  { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@keyframes fuse-ride     { from { transform: translateX(0); } to { transform: translateX(-100%); } }
@keyframes fuse-ride-r   { from { transform: translateX(0); } to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) {
  #s2 .board-frame .fuse.lit .spark, #simonyFuse.lit .spark { animation: none; }
}
@media (prefers-reduced-motion: reduce) {
  #reroll.armed::before, #concede.armed::before { animation: none; opacity: .6; }
}

/* ==========================================================================
   THE HOME (founder, 2026-09-28) -- #s10 rebuilt as the closed leather case.
   The strip (both currencies), the case (the player's own board, shut, the
   bocca in its bezel and a brass plate on the lid), one big PLAY, a row of
   four small doors. Sized off one knob, --case, the case's width: the
   column's width on a phone, capped so the whole home stands in one screen
   on a short phone and does not balloon on a desktop. The lid's furniture
   is placed in the lid's own proportions, measured on the plates: the gilt
   frame's inner panel runs 9%-89.5% across the lid and 12.7%-84.3% down it.
   ========================================================================== */
#s10 { gap: 0; padding: calc(8px + env(safe-area-inset-top, 0px)) 4px calc(10px + env(safe-area-inset-bottom, 0px)); }
#s10 .home {
  --case: min(100%, 400px, 47dvh);
  display: flex; flex-direction: column; align-items: center;
  gap: 12px; flex: 1; width: 100%;
}

/* the strip: the purse, loud (a standing ruling: money is loud, and both
   currencies show wherever money shows). Chunky figures with an ink
   outline, as the founder's model sets them (Clash Royale); no whispered
   caps (DESIGN_CUES, 2026-09-28: no skinny fonts). It stands at the top of
   the page, between the frame's two top sprays (they leave ~220 px of
   clear parchment at a phone's width; the purse is ~200); the case, PLAY
   and the doors are centred in what is left, the strip's auto margin and
   the foot's sharing the spare height. */
/* THE HEADER (founder, 2026-09-30: "the perfect place for the pfp is like
   most games have it, in the header, with the account stats"): the
   portrait and the name on the left, the purse on the right, as a game's
   top bar reads. It stands just under the frame's top sprays, the width of
   the column. */
#s10 .home-strip {
  width: min(100%, 400px); display: flex; align-items: center; gap: 8px; margin-bottom: auto;
  margin-top: 64px;
}
#s10 .home-medal { width: 44px; height: 44px; flex: none; border-radius: 50%; box-shadow: 0 2px 5px rgba(0, 0, 0, .35); }
#s10 .home-name {
  flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
  font: 800 17px/1.1 var(--serif); color: #2a1705; text-shadow: 0 1px 0 rgba(255, 246, 222, .7);
}
#s10 .home-strip .home-purse { flex: none; }
/* a narrow phone keeps the name whole: the pills close up, nothing under 14 px */
@media (max-width: 400px) {
  #s10 .home-strip { gap: 6px; }
  #s10 .home-medal { width: 38px; height: 38px; }
  #s10 .home-name { font-size: 16px; }
  #s10 .home-purse { gap: 4px; }
  #s10 .home-purse .coin { padding: 5px 8px 6px; gap: 3px; }
  #s10 .home-purse b { font-size: 17px; }
  #s10 .home-purse i { font-size: 14px; }
}
#s10 .hub-foot { margin-top: auto; }
#s10 .hub-foot button.quiet, #s1 .hub-foot button.quiet { font: italic 700 14px/1 var(--serif); letter-spacing: .02em; text-transform: none; color: #7a5a12; }
#s10 .home-purse { display: flex; gap: 6px; }
#s10 .home-purse .coin {
  display: inline-flex; align-items: baseline; gap: 4px;
  padding: 6px 10px 7px; border-radius: 999px;
  background: rgba(40, 26, 12, .82);
  box-shadow: inset 0 0 0 1.5px #c9a227, 0 2px 4px rgba(0, 0, 0, .35);
}
#s10 .home-purse b {
  font: 800 19px/1 var(--serif); letter-spacing: .01em; color: #fff3d2;
  font-variant-numeric: lining-nums tabular-nums;
  text-shadow: 0 1px 0 #170d03, 0 -1px 0 #170d03, 1px 0 0 #170d03, -1px 0 0 #170d03, 0 2px 3px rgba(0, 0, 0, .55);
}
#s10 .home-purse i { font: 700 14px/1 var(--serif); font-style: normal; color: #e9c45a; }

/* THE CLOSED BOARD. The case is the board plate's box (roll + board); closed,
   only the cover shows -- the board's width -- so the box is drawn half a
   roll to the left, and the cover stands centred. */
/* THE SCENE (founder, 2026-09-30: "the book/board should be at an angle,
   like the view should be a bit top-down"): the board lies on the table
   and is seen a little from above -- tipped back on its near edge, so its
   far edge recedes -- while the bocca stands upright on it, facing us, and
   the dice lie in front. Only the board tilts; the objects on it do not.
   The case is drawn up into the room the tilt frees above it. It tilts
   FORWARD ONLY (founder, 2026-10-02: "only tilt it forward ... keep the OX
   level"): no turn about the upright, so its sides stand level and the
   cover, centred on the lens, recedes evenly to both. */
#s10 .home-scene { position: relative; width: var(--case); flex: none; margin-top: calc(var(--case) * -0.12); }
#s10 .home-case {
  position: relative; width: 100%;
  aspect-ratio: var(--case-aspect, 1199 / 1193);
  transform-origin: 50% 100%;
  transform: perspective(1000px) translateX(calc(var(--lid-x, 14.2%) / -2)) rotateX(26deg);
  transform-style: preserve-3d;
  /* THE THICKNESS, in the case's own pixels: the board a slab, the cover a
     thick sheet on it (founder, 2026-09-30: "a thick, premium feel") */
  --slab: calc(var(--case-px, 380px) * .036);
  --cover-t: calc(var(--case-px, 380px) * .02);
}
/* the edges: drawn as the illuminator draws, in flat bands of the leather's
   own colour -- a lit top line where the edge turns to the light, a gilt
   tooling line on the cover's edge, the slab's lower band in shade -- not a
   renderer's ramp */
#s10 .home-case :is(.slab-front, .cover-front) { position: absolute; display: block; }
#s10 .home-case .slab-front {
  left: calc(var(--lid-x, 14.2%) + 1.2%); right: 1.2%; top: 96.1%; height: var(--slab);
  transform-origin: 50% 0; transform: rotateX(-90deg);
  background: linear-gradient(180deg, rgba(255, 240, 200, .35) 0 1px, transparent 1px 45%, rgba(0, 0, 0, .38) 45% 100%), var(--leather-tint, #3a2410);
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .6);
}
#s10 .home-case .cover-front {
  left: calc(var(--lid-x, 14.2%) + 1.2%); right: 1.2%; top: 96.1%; height: var(--cover-t);
  transform-origin: 0 0; transform: translateZ(var(--cover-t)) rotateX(-90deg);
  background: linear-gradient(180deg, rgba(255, 244, 210, .45) 0 1px, transparent 1px 38%, #c9a227 38% 52%, transparent 52% 100%), var(--leather-tint, #3a2410);
  box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .55);
}
#s10 .home-case.open .cover-front { display: none; }
/* its shadow on the table, beneath the slab, thrown down and to the right
   (the page's light comes from the upper left) */
#s10 .home-case .case-shadow {
  position: absolute; display: block; left: calc(var(--lid-x, 14.2%) + 2%); right: -1%; top: 6%; bottom: 1%;
  border-radius: 4%;
  background: rgba(30, 16, 4, .42);
  box-shadow: 0 0 26px 14px rgba(30, 16, 4, .34);
  transform: translate3d(8px, 12px, calc(var(--slab) * -1));
}
#s10 .home-case.open .case-shadow { left: 1%; }
/* the base: the board plate, its roll hidden while the cover lies unrolled
   over the board (the roll IS the cover, rolled back) */
#s10 .home-case .case-base { position: absolute; inset: 0; background: center / 100% 100% no-repeat;
  clip-path: inset(3% 0 3.9% var(--lid-x, 14.2%)); }
#s10 .home-case.open .case-base { clip-path: none; }
/* the cover, flat over the board: a window, so that rolling it up is a
   window drawn leftward over a still picture -- transforms only */
#s10 .home-case .case-lid {
  position: absolute; top: 0; bottom: 0; right: 0; left: var(--lid-x, 14.2%); overflow: hidden;
  transform: translateZ(var(--cover-t));        /* the cover lies ON the board, its thickness above it */
}
/* clipped to the board's own top and bottom edges (3.0% and 3.9% of every
   plate's height): the cover's left strip still carried the roll's shoulder
   above the board and its curl below, cut from the plate with the cover */
#s10 .home-case .lid-face { position: absolute; inset: 0; background: var(--lid-img, none) center / 100% 100% no-repeat;
  clip-path: inset(3% 0 3.9% 0); }
/* the roll that travels as the cover rolls up: the plate's own painted roll,
   shown only while it rolls; it lands exactly on the plate's roll */
#s10 .home-case .case-roll {
  position: absolute; top: 0; bottom: 0; left: 0; width: var(--lid-x, 14.2%); display: none;
  background: var(--plate-img, none) 0 0 / var(--roll-size, 705% 100%) no-repeat;
  transform: translateZ(var(--cover-t));
}
#s10 .home-case.rolling .case-roll { display: block; }
/* THE CLASP: a strap cut from the cover's own stitched band, wrapped over
   the right edge and fastened with the gilt quatrefoil. Its strap reads
   the cover picture's top band (dressCase sets the size and the offset). */
#s10 .home-case .case-clasp {
  position: absolute; right: -1.2%; top: 50%; width: 16%; height: 7.5%;
  transform: translateY(-50%) translateZ(calc(var(--cover-t) + 1px));
}
#s10 .home-case .clasp-strap {
  position: absolute; inset: 0; display: block; border-radius: 4px 7px 7px 4px;
  background-image: var(--lid-img, none); background-repeat: no-repeat;
  background-size: var(--strap-size, 1000% auto); background-position: var(--strap-pos, 0 0);
  box-shadow: 0 3px 6px rgba(20, 10, 2, .5), inset 0 0 0 1px rgba(20, 10, 2, .55);
}
#s10 .home-case .clasp-catch {
  position: absolute; left: 0; top: 50%; height: 230%; width: auto;
  transform: translate(-42%, -50%);
  filter: drop-shadow(0 3px 4px rgba(20, 10, 2, .5));
}
/* a case whose plates did not arrive stands as a quiet frame, not a hole */
#s10 .home-case.bare { background: rgba(60, 40, 20, .18); border-radius: 18px; transform: none; }
#s10 .home-case.bare .case-lid, #s10 .home-case.bare .case-clasp { display: none; }

/* the bocca in its bezel: the bezel is the level's crest (identity notes,
   2026-09-03: "the bezel can be level"), shown in its first state, the
   ivory ring, until the founder rules the level. The stone sits behind the
   ring, turned as it is at the table. */
/* standing on the cover's middle -- which the tilt carries to ~59% of the
   scene's height -- its foot a little in front of it, so it reads planted */
#s10 .lid-crest {
  position: absolute; left: 50%; top: 63%; width: 44%;
  aspect-ratio: 1107 / 1124; transform: translate(-50%, -100%);
}
/* the dice: the table's own 3D stage, borrowed while the home is up (as the
   profile borrows it), lying on the cover's near half */
#s10 .home-dice { position: absolute; left: 50%; top: 81%; transform: translate(-50%, -50%) scale(var(--dice-k, 1.45));
  pointer-events: none; }
/* the dice's touch pad (the toy, 2026-09-30): over the pair and a finger's
   reach around them, centred where the stage is; a drag on it rolls a die
   and never scrolls the page. Which die, and whether a touch reached one at
   all, dice.js answers (dieAt). */
#s10 .home-dice-pad {
  position: absolute; left: 50%; top: 81%; transform: translate(-50%, -50%);
  width: calc(150px * var(--dice-k, 1.45)); height: calc(84px * var(--dice-k, 1.45));
  touch-action: none; -webkit-tap-highlight-color: transparent; -webkit-user-select: none; user-select: none;
}
#s10 .home-case.bare ~ .lid-crest, #s10 .home-case.bare ~ .home-dice, #s10 .home-case.bare ~ .home-dice-pad { display: none; }
#s10 .lid-crest img { position: absolute; display: block; pointer-events: none; }
#s10 .lid-crest .lid-mouth { left: 50%; top: 50%; height: 88%; width: auto; transform: translate(-50%, -50%);
  filter: drop-shadow(0 3px 5px rgba(0, 0, 0, .45)); }
#s10 .lid-crest .lid-bezel { inset: 0; width: 100%; height: 100%; }
#s10 .lid-crest img:not([src]) { visibility: hidden; }

/* PLAY: the landing's painted plaque at the menu's size */
#s10 .home-play { margin-top: 2px; }

/* the small doors: four in a row, the plaque over its one word */
#s10 .home-doors { width: var(--case); display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
#s10 .home-door {
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  min-height: 0; min-width: 0; margin: 0; padding: 0;
  background: none; border: 0; border-radius: 0; box-shadow: none; transition: none;
  color: var(--ink); -webkit-tap-highlight-color: transparent;
}
#s10 .home-door:hover, #s10 .home-door:active, #s10 .home-door:focus-visible { transform: none; box-shadow: none; }
#s10 .home-door .glyph { display: block; width: 100%; aspect-ratio: 320 / 185; background: center / contain no-repeat; }
#s10 .home-door:active .glyph { transform: scale(.95); }
#s10 .home-door:focus-visible .glyph { outline: 2px solid rgba(201, 162, 39, .7); outline-offset: 2px; border-radius: 12px; }
#s10 .home-door .glyph[data-glyph="crown"] { background-image: url("/static/art/door_crown.webp"); }
#s10 .home-door .glyph[data-glyph="gear"]  { background-image: url("/static/art/door_gear.webp"); }
#s10 .home-door .glyph[data-glyph="book"]  { background-image: url("/static/art/door_book.webp"); }
#s10 .home-door .glyph[data-glyph="lion"]  { background-image: url("/static/art/door_lion.webp"); }
#s10 .home-door .word {
  font: 800 14px/1 var(--serif); letter-spacing: .03em; color: #3a2410;
  text-shadow: 0 1px 0 rgba(255, 246, 222, .7);
}

/* THE FIRST-RUN DEMO'S VERDICT (menus, 2026-09-28): the story ends at the
   home, so its verdict has one door, HOME. The real verdict's doors --
   REMATCH, NEW SETUP, the receipt, the claim -- stand down: there is no
   match to run back, no dice drawn to account for, no game to keep. */
/* THE HAND (founder, 2026-09-29: "a hand to point at exactly where and what
   to do. And also a textbox with commentary attached to that hand ... the
   hand should jump from one row to the next in a loop, also highlighting
   the row"). The first-run demo's teacher, over the table and its bar (the
   bar stands at 60 in #s2's stacking), taking no taps. Its three parts are
   placed by coachShow in page pixels, by transform (the compositor's), and
   only when a figure changes. One effect at a time: while the hand loops
   over the places it only moves; resting on one place, it taps. */
#coach { position: fixed; inset: 0; z-index: 70; pointer-events: none; }
#coach[hidden] { display: none; }
#coach .coach-lit {
  position: absolute; left: 0; top: 0; border-radius: 10px;
  border: 3px solid #e8b93f;
  background: rgba(255, 232, 150, .20);
  box-shadow: 0 0 0 2px rgba(42, 27, 12, .55), 0 0 14px rgba(255, 214, 120, .55);
}
#coach .coach-lit[hidden] { display: none; }
/* the hand is a point -- its fingertip -- with the glyph hung from it: the
   manicule points right, and is turned to point up at a place from below or
   down at CAST from above */
#coach .coach-hand { position: absolute; left: 0; top: 0; width: 0; height: 0; transition: transform .28s cubic-bezier(.3, .7, .3, 1); }
/* the solid manicule (U+261B), big and inked, edged in parchment so it
   reads over dark leather and pale cells alike -- the outline one drew at
   ~20 px of hairline and read as a doodle (seen 2026-09-29) */
#coach .coach-hand i {
  position: absolute; display: block; font-style: normal;
  font: 84px/1 "Segoe UI Symbol", "Apple Symbols", "Noto Sans Symbols 2", "DejaVu Sans", var(--serif);
  color: #2e1c09;
  text-shadow: 0 0 1px #f5ecd6, 0 0 2px #f5ecd6, 0 0 3px #f5ecd6, 0 0 5px #f5ecd6, 0 0 10px rgba(245, 236, 214, .85), 0 4px 8px rgba(0, 0, 0, .35);
  width: 1em; height: 1em; text-align: center;
}
/* pointing up: the glyph below the point, turned a quarter back */
#coach .coach-hand[data-way="up"] i { left: -.5em; top: .02em; transform: rotate(-90deg); }
/* pointing down: the glyph above the point, turned a quarter on */
#coach .coach-hand[data-way="down"] i { left: -.5em; top: -1.02em; transform: rotate(90deg); }
/* resting on a place, it taps (the loop only moves) */
#coach .coach-hand[data-rest="1"][data-way="up"] i { animation: coachTapUp .9s ease-in-out infinite alternate; }
#coach .coach-hand[data-rest="1"][data-way="down"] i { animation: coachTapDown .9s ease-in-out infinite alternate; }
@keyframes coachTapUp   { from { transform: rotate(-90deg) translateX(0); } to { transform: rotate(-90deg) translateX(-12px); } }
@keyframes coachTapDown { from { transform: rotate(90deg) translateX(0); }  to { transform: rotate(90deg) translateX(-12px); } }
@media (prefers-reduced-motion: reduce) { #coach .coach-hand i { animation: none !important; } #coach .coach-hand { transition: none; } }
/* the words: a parchment slip beside the hand, sturdy and upright (no skinny
   fonts in new UI; nothing readable under ~14 px) */
#coach .coach-box {
  position: absolute; left: 0; top: 0; width: max-content; max-width: min(76vw, 280px);
  padding: 9px 13px 10px; border-radius: 9px;
  background: #f5ecd6; border: 1.5px solid #8a6a2c;
  box-shadow: 0 3px 10px rgba(40, 26, 8, .38), inset 0 0 0 1px rgba(255, 250, 235, .7);
  font: 600 15.5px / 1.32 var(--serif); color: #2a1b0c; text-align: center;
  letter-spacing: 0; text-transform: none;
}
#coach .coach-box[hidden] { display: none; }

/* RULES' demo door stands down at a table, the story's own included: it
   would walk a player away from a live game (menus, 2026-09-28) */
body:has(#s2.on) #rulesDemo { display: none; }
#s3 #demoHome { display: none; }
#s3.demo #demoHome { display: inline-flex; }
#s3.demo #rematch, #s3.demo #newSetup, #s3.demo #showReceipt, #s3.demo #claimRow, #s3.demo #claimNote { display: none; }

/* A MIRRORED FAR MOUTH SPEAKS INTO THE ARENA (menus, 2026-09-28, found
   while giving the demo its voice). A champion's face is mirrored when it
   looks the wrong way for its side (dressMouth: --mouth-flip, and the
   total's data-flipped), and everything inside the mouth mirrors with it:
   his bubble, told to open leftward, opened off the right edge of the
   screen with its words reading backwards. Unseen until now because the
   mask is silent by default. For a mirrored far mouth the bubble is placed
   on the side that mirrors to the left, and turned back round on its own
   middle with the `scale` property, which composes with the pop's own
   `transform` instead of fighting it; its tail, turned back with it, is
   drawn on its right, toward the face. */
#s2[data-cast="angled"] .board-frame:has(.theirs) .total[data-flipped="1"]:not([data-turned]) .mouth .speech {
  left: 94%; right: auto; scale: -1 1; transform-origin: 50% 18px;
}
#s2[data-cast="angled"] .board-frame:has(.theirs) .total[data-flipped="1"]:not([data-turned]) .mouth .speech::before {
  right: -7px; left: auto;
  border-left: none; border-right: 1.5px solid #8a6a2c; border-bottom: 1.5px solid #8a6a2c;
  transform: rotate(-45deg);
}
/* the first-run story's lessons are read by someone new to the table: an
   upright, sturdier face than the table's italic aside (no skinny fonts in
   new UI, DESIGN_CUES 2026-09-28; nothing readable under ~14 px) */
#s2[data-demo] .mouth .speech { font: 600 14.5px / 1.3 var(--serif); max-width: min(62vw, 250px); }
/* ...and it stands a little higher than his usual aside, on the foot of his
   own board, because the lesson is spoken once the die has landed and the
   die lands in the gap his bubble would otherwise cover; the tail drops to
   the bubble's foot, toward the face below it */
#s2[data-demo] .board-frame:has(.theirs) .total .mouth .speech { top: auto; bottom: 88%; }
#s2[data-demo] .board-frame:has(.theirs) .total .mouth .speech::before { top: auto; bottom: 7px; }
