/* ================= Texas Bones — styles =================

   ---------- the widths, and the one rule about them ----------

   **A rule written for a wide screen must carry `min-width`.** That is not
   style advice, it is the bug from v1.49.0 written down: a desktop bone size
   was added without one, it sat later in the file than the phone sizes, it beat
   them at equal specificity, and a 390px screen drew 100px bones with the
   fourth one off the edge and the scorepad gone. Phone-first rules are written
   `max-width`; anything that widens them is written `min-width`; and no two
   rules for the same property are ever written at the same pixel.

   The widths in use, and what each is actually for:

     380 400 420 430 460 480 520 560 620   phones and large phones
     700                                    small tablet
     899 / 900                              the desktop boundary, complementary
     1240 / 1241, 1500                      wide desktop

   The complementary pairs — 560/561, 899/900, 1240/1241 — are correct and
   deliberate: they cannot both match at once. **The nine in the phone range are
   not.** They accumulated one bug report at a time, no two of them mean
   anything different from each other, and which one wins between 380 and 620
   depends on the order they happen to sit in this file. Consolidating them onto
   three is `BACKLOG.md` §4.6 and is deliberately not being done in the same
   change as everything else — moving a rule from 380 to 430 changes what real
   devices get, and that wants its own release and its own look.

   `fitcheck.js` measures 430, 412, 390 and 360 in ordinary and Big print, which
   is every width where these rules straddle a real device.
   ========================================================= */
* { margin: 0; padding: 0; box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
:root {
  --felt: #1d5c3a;
  --felt-dark: #14432a;
  --wood: #6b3f22;
  --wood-light: #8a5430;
  --cream: #f5efe0;
  --ink: #23180f;
  --gold: #e0aa3e;
  --red: #c0392b;
  --shadow: 0 2px 8px rgba(0,0,0,.35);
  /* theme system.
     Standard is felt and brass: the green gets a napped, slightly fuzzy
     surface from two fine cross-hatches and a speckle, the way real billiard
     felt catches light in two directions at once. Nothing here is an image —
     it all has to survive being inlined into a single HTML file. */
  --table-bg:
    repeating-linear-gradient(27deg, rgba(255,255,255,.030) 0 1px, rgba(0,0,0,0) 1px 3px),
    repeating-linear-gradient(-64deg, rgba(0,0,0,.055) 0 1px, rgba(0,0,0,0) 1px 3px),
    radial-gradient(circle at 26% 30%, rgba(255,255,255,.05) .6px, rgba(0,0,0,0) 1.3px),
    radial-gradient(circle at 72% 68%, rgba(0,0,0,.08) .6px, rgba(0,0,0,0) 1.3px),
    radial-gradient(ellipse at 50% 22%, #236943 0%, #1d5c3a 34%, #14432a 78%, #0e3120 100%);
  --table-bg-size: auto, auto, 5px 5px, 7px 7px, 100% 100%;
  --table-text: #f5efe0;
  --status-text: #ffe9b8;
  --btn-text: #23180f;
  --pill-hi: #e0aa3e;
  --marker: rgba(224,170,62,.9);
  --card-a: #8a5430;
  --card-b: #6b3f22;
  --link-brd: rgba(245,239,224,.4);
  --heading: #e0aa3e;      /* readable on panels; --gold is the button FILL */
  /* Emphasis on a dark panel — bold runs, game titles, glossary terms. Was
     `#ffe9b8` hard-coded in four rules until 21 Aug, which is why no theme
     could correct it and Slate could not be made to stop looking yellow.
     BACKLOG 1L.10. */
  --emph: #ffe9b8;
  --brand: #e0aa3e;        /* the wordmark and stars, which sit on the tabletop */
  /* **The brand's typeface is TeX Gyre Bonum Bold** — `brand/README.md`,
     convention 6, and every canon mark is drawn from it. Bonum is GUST's libre
     cut of URW Bookman: an old-style serif, large x-height, bracketed serifs.

     This stack used to lead with Rockwell under a comment saying headings take
     "the nearest slab serif the platform already has". **Bonum is not a slab.**
     Rockwell is a geometric slab and a different genus altogether, so every
     heading and button on a Windows machine was rendering in the wrong family
     directly beside a wordmark drawn in the right one — since the marks first
     shipped. The comment was the bug: nothing in this file could contradict it,
     so it was trusted. BACKLOG 1Y.1.

     Bonum first, so that embedding it later (1Y.3) needs no change here. Then
     the Bookmans the platforms actually carry — Bookman Old Style ships with
     Windows and Office, which is why the desktop lands close to brand for
     nothing. Georgia last: it is a Scotch rather than a Bookman, but it is
     everywhere, and it is the same face the body text uses.

     Still nothing downloaded. 1Y.3 is where that gets decided, with a measured
     file size rather than a guess. */
  --brand-font: 'TeX Gyre Bonum', 'Bookman Old Style', 'URW Bookman',
                'URW Bookman L', Bookman, 'Bitstream Charter', Georgia, serif;
}
/* Northgate — worn bar wood, carved names, maroon */
body.theme-northgate {
  --table-bg:
    repeating-linear-gradient(92deg, rgba(50,27,10,.25) 0 3px, rgba(0,0,0,0) 3px 9px, rgba(38,20,8,.18) 9px 14px, rgba(0,0,0,0) 14px 27px),
    repeating-linear-gradient(88deg, rgba(255,214,150,.05) 0 2px, rgba(0,0,0,0) 2px 19px),
    radial-gradient(ellipse at 50% 30%, #7c5026 0%, #57351a 65%, #3e250f 100%);
  --table-bg-size: auto, auto, 100% 100%;
  --gold: #7a1414;            /* maroon buttons/badges */
  --btn-text: #ffe6c4;
  --pill-hi: #ffcf7d;
  --status-text: #ffe1a8;
  --marker: rgba(255,215,134,.95);
  --card-a: #6e4520;
  --card-b: #4c2d12;
  --heading: #ffcf7d;       /* maroon is the button fill; headings stay light */
  --brand: #ffcf7d;
}
/* The Hall — folding table, fluorescent light, red cups */
body.theme-kc {
  --table-bg:
    radial-gradient(circle at 30% 30%, rgba(60,50,30,.09) 1px, rgba(0,0,0,0) 1.7px),
    radial-gradient(circle at 70% 65%, rgba(40,32,18,.08) 1px, rgba(0,0,0,0) 1.7px),
    radial-gradient(ellipse at 50% 25%, #dcd5c0 0%, #c6bda2 65%, #ac9f82 100%);
  --table-bg-size: 7px 7px, 11px 11px, 100% 100%;
  --table-text: #2b2114;
  --status-text: #7c1f16;
  --gold: #26364F;            /* deep navy — the baskets are red, so the
                                 buttons can't be, or they vanish into them */
  --btn-text: #fdf6e8;
  /* White, not gold. The chips this outlines sit on The Hall's dark panels,
     and gold on parchment was the one value in this file that belonged to no
     theme at all. BACKLOG 1K.14. */
  --pill-hi: #ffffff;
  --marker: rgba(150,40,28,.95);
  --card-a: #37517d;          /* legion navy */
  --card-b: #22314c;
  --link-brd: rgba(43,33,20,.45);
  /* Light was the intention and the comment said so; pink was a leftover
     from the red baskets `--brand` is drawn from, and it read as a colour
     nobody had chosen. BACKLOG 1K.14. */
  --heading: #ffffff;       /* panels are dark, so this stays light */
  --emph: #ffffff;
  --brand: #b3231a;         /* the table is light — the wordmark goes proper red */
}
/* Slate & chalk — black slate tabletop, score chalked on it */
body.theme-slate {
  /* Slate isn't a smooth gradient — it's a stone that split along a bedding
     plane, so it has irregular blotching, faint cleavage streaks running one
     way, and a fine grain over all of it. Six layers: two grains, three
     blotches at different scales, and the base. */
  --table-bg:
    radial-gradient(circle at 20% 30%, rgba(255,255,255,.028) .7px, rgba(0,0,0,0) 1.4px),
    radial-gradient(circle at 65% 70%, rgba(0,0,0,.14) .7px, rgba(0,0,0,0) 1.4px),
    repeating-linear-gradient(103deg,
      rgba(255,255,255,.016) 0 2px, rgba(0,0,0,0) 2px 9px,
      rgba(0,0,0,.05) 9px 12px, rgba(0,0,0,0) 12px 31px),
    radial-gradient(ellipse 60% 40% at 18% 22%, rgba(120,132,142,.16), rgba(0,0,0,0) 70%),
    radial-gradient(ellipse 50% 55% at 82% 68%, rgba(96,108,118,.13), rgba(0,0,0,0) 72%),
    radial-gradient(ellipse 70% 50% at 55% 92%, rgba(0,0,0,.28), rgba(0,0,0,0) 70%),
    linear-gradient(158deg, #363b40 0%, #262b2f 40%, #1a1e21 72%, #131618 100%);
  --table-bg-size: 7px 7px, 11px 11px, auto, 100% 100%, 100% 100%, 100% 100%, 100% 100%;
  --slate-grain:
    radial-gradient(circle at 22% 34%, rgba(255,255,255,.026) .7px, rgba(0,0,0,0) 1.4px),
    radial-gradient(circle at 68% 66%, rgba(0,0,0,.16) .7px, rgba(0,0,0,0) 1.4px),
    repeating-linear-gradient(103deg,
      rgba(255,255,255,.014) 0 2px, rgba(0,0,0,0) 2px 9px,
      rgba(0,0,0,.05) 9px 12px, rgba(0,0,0,0) 12px 31px);
  --table-text: #e9ece8;
  --status-text: #cfe6ff;
  --gold: #7fb4d8;
  --btn-text: #11171b;
  --pill-hi: #a8d8f5;
  --marker: rgba(190,225,250,.95);
  --card-a: #3b4247;
  --card-b: #262b2f;
  --link-brd: rgba(233,236,232,.38);
  --heading: #a8d8f5;
  --emph: #dbeeff;          /* near-white with the same blue cast */
  --brand: #a8d8f5;
}
/* Chalk on slate. Three things make it read as chalk rather than grey text:
   it's dusty (a soft halo around every stroke), it's granular (the stick skips,
   so the stroke is never solid), and it's old (somebody's sleeve has been
   through it). The grain and the smear are both masks — one speckled, one a
   sweep — multiplied together, so the same marks look worn without drawing two
   copies of anything. */
.chalk {
  position: absolute; white-space: nowrap;
  font-family: 'Segoe Script', 'Bradley Hand', 'Comic Sans MS', cursive;
  color: rgba(238,244,249,.34);
  text-shadow:
    0 0 1px rgba(226,238,248,.5),
    0 0 7px rgba(210,232,250,.18),
    0 0 16px rgba(190,220,245,.09);
  letter-spacing: 2px;
  -webkit-mask-image:
    repeating-linear-gradient(58deg, #000 0 1px, rgba(0,0,0,.35) 1px 2px, #000 2px 4px),
    repeating-linear-gradient(-31deg, #000 0 2px, rgba(0,0,0,.5) 2px 3px, #000 3px 6px),
    linear-gradient(102deg, #000 0 18%, rgba(0,0,0,.18) 34%, #000 52%,
                    rgba(0,0,0,.08) 71%, rgba(0,0,0,.6) 88%, #000 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    repeating-linear-gradient(58deg, #000 0 1px, rgba(0,0,0,.35) 1px 2px, #000 2px 4px),
    repeating-linear-gradient(-31deg, #000 0 2px, rgba(0,0,0,.5) 2px 3px, #000 3px 6px),
    linear-gradient(102deg, #000 0 18%, rgba(0,0,0,.18) 34%, #000 52%,
                    rgba(0,0,0,.08) 71%, rgba(0,0,0,.6) 88%, #000 100%);
  mask-composite: intersect;
}
/* the running score has been up there longest and is half gone */
.chalk.chalk-old {
  color: rgba(238,244,249,.2);
  -webkit-mask-image:
    repeating-linear-gradient(58deg, #000 0 1px, rgba(0,0,0,.3) 1px 2px, #000 2px 4px),
    radial-gradient(ellipse 55% 120% at 34% 45%, rgba(0,0,0,.04) 0%, rgba(0,0,0,.35) 45%, #000 85%),
    linear-gradient(96deg, rgba(0,0,0,.5) 0 12%, rgba(0,0,0,.06) 30%, rgba(0,0,0,.55) 55%,
                    rgba(0,0,0,.1) 78%, rgba(0,0,0,.45) 100%);
  -webkit-mask-composite: source-in;
  mask-image:
    repeating-linear-gradient(58deg, #000 0 1px, rgba(0,0,0,.3) 1px 2px, #000 2px 4px),
    radial-gradient(ellipse 55% 120% at 34% 45%, rgba(0,0,0,.04) 0%, rgba(0,0,0,.35) 45%, #000 85%),
    linear-gradient(96deg, rgba(0,0,0,.5) 0 12%, rgba(0,0,0,.06) 30%, rgba(0,0,0,.55) 55%,
                    rgba(0,0,0,.1) 78%, rgba(0,0,0,.45) 100%);
  mask-composite: intersect;
}

html, body { height: 100%; }
body {
  font-family: Georgia, 'Times New Roman', serif;
  background-image: var(--table-bg);
  background-size: var(--table-bg-size);
  background-attachment: fixed;
  color: var(--table-text);
  overflow-x: hidden;
  /* `dvh` where it exists, `vh` where it does not (§1AE.58, 7 Sep). On iOS
     Safari `100vh` is the LARGEST viewport — the height with the address bar
     hidden — so while the bar shows, a screen sized to it is taller than the
     glass by the bar's height and the dock sits below the fold: *"you need
     to scroll in game to get the right view, especially on iPhone."* The
     dynamic unit follows the bar. Two declarations, the old one first, so a
     browser without `dvh` keeps what it had. */
  min-height: 100vh; min-height: 100dvh;
}
/* decorations (per theme, injected by JS) */
#decor-layer { position: fixed; inset: 0; pointer-events: none; z-index: 0; overflow: hidden; }
.screen { position: relative; z-index: 1; }
/* Carved into the wood: the cut is dark, and the raised lip on the lower edge
   catches the light from above. Three "hands" so it looks like different
   people over different decades. */
.carve {
  position: absolute; white-space: nowrap;
  color: rgba(30,14,3,.46);
  letter-spacing: 1.2px;
  -webkit-text-stroke: .35px rgba(18,7,1,.34);
  text-shadow:
    0 -1px 0 rgba(0,0,0,.42),
    0 1px 0 rgba(255,219,158,.34),
    0 2px 1px rgba(255,214,150,.14),
    0 0 3px rgba(0,0,0,.18);
}
.carve.h1 { font-family: 'Segoe Script', 'Bradley Hand', 'Snell Roundhand', cursive; font-weight: 700; }
.carve.h2 { font-family: 'Impact', 'Haettenschweiler', 'Arial Narrow', sans-serif;
  letter-spacing: 2.4px; transform-origin: center; }
.carve.h3 { font-family: 'Rockwell', 'Courier New', Courier, monospace; font-weight: 700;
  letter-spacing: 1.8px; }
.decor-svg { position: absolute; transform-origin: center;
             filter: drop-shadow(0 4px 6px rgba(0,0,0,.35)); }
.flyer {
  position: absolute; background: #f8f4e9; color: #3a3428; width: 150px;
  padding: 18px 22px; font-family: 'Courier New', monospace; font-size: 21px;
  line-height: 1.55; transform: rotate(2.5deg); box-shadow: 0 5px 14px rgba(0,0,0,.34);
  border-top: 26px solid rgba(215,205,175,.9);
}
button { font-family: inherit; cursor: pointer; }
.screen { display: none; min-height: 100vh; min-height: 100dvh; }
.screen.active { display: flex; flex-direction: column; }

/* ---------- Home ---------- */
#screen-home { align-items: center; justify-content: center; text-align: center; padding: 24px; }
/* Two lines' worth of room, always.

   Each table writes its own tagline, and only the Club's fits one line on a
   phone: at 412px the other three wrap, which pushed everything below them
   down by exactly one line and made the page 19px taller. Swapping tables
   moved the whole home screen, and scrolling a little put it back — reported
   19 Aug, and the vertical twin of §1B.12.

   Reserved rather than shortening the three long ones, because the taglines
   are meant to differ and the furniture is not — and a future tagline that
   wraps would bring the fault straight back. The Club pays a blank line for
   it. */
.tagline {
  opacity: .85; margin: 6px 0 34px; font-style: italic;
  line-height: 1.2; min-height: 2.4em;
}
/* Cards stretch to a common height and the badge is pushed to the bottom of
   each one, so the three buttons sit on one line however long the blurbs run.
   Without that they float at different heights and pull the eye around. */
.game-cards {
  display: flex; gap: 18px; flex-wrap: wrap; justify-content: center;
  align-items: stretch; margin-bottom: 26px;
}
.game-card {
  background: linear-gradient(160deg, var(--card-a), var(--card-b));
  border: 2px solid rgba(0,0,0,.35);
  border-radius: 14px;
  padding: 22px 24px;
  width: 250px;
  color: var(--cream);
  box-shadow: var(--shadow);
  transition: transform .12s ease;
  text-align: center;
  display: flex; flex-direction: column;
}
.game-card:hover { transform: translateY(-3px); }
/* Standard theme only — the other tables have their own surface already.
   Pebbled leather grain, a saddle-stitch run just inside the edge, and a
   highlight along the top so the panel reads as a thing with thickness. */
body:not(.theme-northgate):not(.theme-kc):not(.theme-slate) .game-card {
  position: relative;
  background-image:
    radial-gradient(circle at 22% 26%, rgba(255,225,190,.055) .8px, rgba(0,0,0,0) 1.7px),
    radial-gradient(circle at 68% 62%, rgba(0,0,0,.10) .9px, rgba(0,0,0,0) 1.9px),
    radial-gradient(circle at 45% 82%, rgba(255,220,180,.04) .7px, rgba(0,0,0,0) 1.5px),
    linear-gradient(160deg, var(--card-a), var(--card-b));
  background-size: 9px 9px, 13px 13px, 17px 17px, 100% 100%;
  border-color: rgba(0,0,0,.5);
  box-shadow:
    inset 0 1px 0 rgba(255,214,164,.22),
    inset 0 -2px 5px rgba(0,0,0,.32),
    0 4px 12px rgba(0,0,0,.45);
}
body:not(.theme-northgate):not(.theme-kc):not(.theme-slate) .game-card::before {
  content: ''; position: absolute; inset: 7px; border-radius: 9px;
  border: 1.5px dashed rgba(247,222,178,.42);
  pointer-events: none;
}
.game-card h2 { font-size: 26px; margin-bottom: 6px; color: var(--emph); }
.game-card p { font-size: 14px; line-height: 1.45; opacity: .9; }
.game-card .badge {
  align-self: center; margin-top: auto; padding-top: 0;
  background: var(--gold); color: var(--btn-text);
  font-size: 12px; font-weight: bold; border-radius: 20px; padding: 5px 14px;
}
/* the gap above the badge lives here, not in the badge's own margin, so
   `margin-top: auto` can do its job */
.game-card p { margin-bottom: 14px; }
.game-card .card-kicker {
  font-size: 11px; letter-spacing: 1.4px; text-transform: uppercase;
  opacity: .6; margin-bottom: 2px;
}
/* Positioned and clipped sideways for the Install button's bubble (§1AF.45):
   the bubble spans this row rather than hanging off its button, the way the
   theme row's does under 520px, and `overflow-x: clip` means nothing inside
   the row can ever widen the page — §1B.10's Android zoom-out came from an
   invisible bubble doing exactly that. `clip`, not `hidden`, so the bubble
   is free to rise above the row. */
.home-links { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; position: relative; overflow-x: clip; }
/* Two classes deep on purpose: `.ts-tip` is written further down the file
   and would beat a bare `.install-tip` on source order (csslint's case). The
   button itself is NOT positioned — the row is the bubble's ancestor, so
   left and right pin it to the row, not to the button. */
.home-links .install-btn { position: relative; }
.home-links .install-tip {
  /* Off the button: under it, its right edge on the button's right edge, so
     on a desktop it sits at the right end of the row where the button is
     (Mike, 11 Sep — the first cut spanned the row and landed two lines
     away from the button, over the tip jar). Opaque, so the footer's
     Privacy and Game stats do not show through it. */
  left: auto; right: 0; width: max-content; max-width: min(340px, 90vw);
  top: calc(100% + 6px); bottom: auto; transform: translateY(-4px);
  /* Opaque AND above the footer: the footer comes later in the document
     and painted over the solid bubble on the first try. */
  background: #120e0a; z-index: 20;
}
.home-links .install-btn:hover .install-tip, .home-links .install-btn:focus-visible .install-tip {
  opacity: 1; visibility: visible; transform: translateY(0);
}
/* A phone's row is narrow and the button may sit anywhere on its line, so
   the bubble spans the row instead: the row becomes the positioned ancestor
   again (the button goes static) and both edges pin to it. */
@media (max-width: 520px) {
  .home-links .install-btn { position: static; }
  .home-links .install-tip { left: 0; right: 0; margin: 0 auto; width: auto; max-width: none; }
}
.link-btn {
  background: none; border: 1px solid var(--link-brd); color: var(--table-text);
  border-radius: 24px; padding: 11px 22px; font-size: 16px;
}
.link-btn:hover { border-color: var(--pill-hi); }
.proto-note { margin-top: 26px; font-size: 13px; opacity: .6; max-width: 430px; }
.site-footer { margin-top: 16px; font-size: 14px; opacity: .75; }
/* The notice under the links: a legal line, not navigation. Smaller and
   quieter than the links, in em so Big print scales it with them. */
.site-footer .legal { margin: 6px 0 0; font-size: .82em; opacity: .6; letter-spacing: .02em; }
.site-footer a { color: var(--table-text); text-decoration: none; margin: 0 4px;
  border-bottom: 1px solid var(--link-brd); padding-bottom: 1px; }
.site-footer a:hover { color: var(--pill-hi); border-color: var(--pill-hi); }

/* ---------- House rules ---------- */
#screen-rules { padding: 18px; align-items: center; }
.rules-panel {
  /* Declared rather than merely applied, so `.rb-bar` can cancel exactly this
     padding instead of guessing at it. See the note on `.rb-bar`. */
  --panel-pad-x: 20px; --panel-pad-t: 20px;
  width: 100%; max-width: 620px; background: rgba(16,10,5,.72);
  border: 1px solid rgba(224,170,62,.35); border-radius: 14px;
  padding: var(--panel-pad-t) var(--panel-pad-x);
  color: var(--cream);
}
.rules-panel h2 { color: var(--heading); margin-bottom: 2px; font-size: 25px; }
.rules-sub { font-size: 14px; opacity: .85; margin-bottom: 16px; }
.rule-row { display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 11px 0; border-bottom: 1px solid rgba(255,255,255,.08); flex-wrap: wrap; }
.rule-row:last-of-type { border-bottom: none; }
.rule-name { font-size: 16px; }
.rule-name small { display: block; opacity: .65; font-size: 13px; margin-top: 2px; }
.seg { display: flex; background: rgba(0,0,0,.35); border-radius: 20px; padding: 3px; }
/* ---- a settings row that cannot fit its options wraps them (§4.18) ----

   The Table theme row is four word labels — The Club, Northgate, The Hall,
   Slate — and it did not fit: 352px of buttons in a 282px row. At ordinary
   print it stuck out of the row and happened to land inside the glass; in Big
   print it pushed 32px past the edge of a 360px phone and cut Slate off, and
   the whole page scrolled sideways.

   **It is not a Big print bug**, which is what decides where the fix goes:
   the control overflows its row at ordinary print too, and the only Big print
   rule on .seg is a min-height and a font size — no padding, no width. So the
   fix is here, in the default layout, and Big print inherits it. No second
   number to go stale.

   The wrap is the half that cannot go stale: it does nothing at any width
   where a row already fits, so it cannot regress one, and it fits any set of
   labels at either print size — including a fifth theme, which would have
   broken this again. The narrower padding below is cosmetic: it keeps the
   theme row on one line at 360 and 390, and on two rather than three at 320.
   Measured past the page in Big print, before and after:

     as it ships   320: 72px   360: 32px   390: 2px   430: 0
     with both     320: 0      360: 0      390: 0     430: 0                 */
#screen-rules .rule-row > .seg { flex-wrap: wrap; gap: 2px; }
/* ---------- filled means do it, outlined means chosen ---------- §1G.5

   These two were the same declaration:

       .big-btn       { background: var(--gold); color: var(--btn-text); }
       .seg button.on { background: var(--gold); color: var(--btn-text); }

   — so the table's colour meant *press this* and *this is the one you picked*
   at the same time, and the profile carried twenty-five of the second kind
   against one of the first. Counted 20 Aug; the home screen had none at all.

   A chosen chip is outlined now and an action stays filled. The same idea in
   every room, because both come out of the theme's own variables rather than a
   colour written down here.

   **Outlined in `--pill-hi`, not `--gold`.** `--gold` is the table's *fill*
   colour and it is navy in the Hall and maroon on Northgate — a navy line round
   a chip on a dark strip is not a line anybody can see. `--pill-hi` is the
   bright accent and is bright in all four.

   The border is on every chip and merely transparent when off, so choosing one
   does not move the row a pixel. */
.seg button {
  background: none; color: var(--cream); padding: 7px 15px;
  border: 2px solid transparent;
  border-radius: 16px; font-size: 15px; opacity: .75;
}
.seg button.on {
  background: none; border-color: var(--pill-hi); color: var(--pill-hi);
  font-weight: bold; opacity: 1;
}
.seg button:disabled { opacity: .3; cursor: default; }
/* "to be added later", not "later", and at the size of the text beside it
   rather than 12px. A bare "later" reads as a state the *setting* is in; the
   tag is a promise about the app and should say so. BACKLOG 1X.3. */
.stats-link { text-decoration: none; }
.stats-link:hover, .stats-link:focus-visible { background: rgba(255,255,255,.3); }
.soon-tag { font-size: inherit; background: rgba(255,255,255,.18); color: var(--cream);
  border-radius: 12px; padding: 4px 11px; white-space: nowrap; }
.agree-strip { margin-top: 18px; background: rgba(0,0,0,.3); border-radius: 10px; padding: 12px; color: var(--cream); }
.agree-strip h3 { font-size: 15px; color: var(--heading); margin-bottom: 10px; }
.agree-seats { display: flex; gap: 10px; justify-content: space-between; flex-wrap: wrap; }
.agree-seat { text-align: center; font-size: 13px; flex: 1; min-width: 90px; }
.agree-seat .face { font-size: 26px; }
.agree-seat .tick { color: #7ee6a2; font-weight: bold; visibility: hidden; }
.agree-seat.agreed .tick { visibility: visible; }
.big-btn {
  display: block; width: 100%; margin-top: 16px; background: var(--gold); color: var(--btn-text);
  font-size: 19px; font-weight: bold; border: none; border-radius: 10px; padding: 17px;
  box-shadow: var(--shadow);
}
.big-btn:disabled { opacity: .5; }
.ghost-btn { display:block; width:100%; margin-top: 10px; background: none; border: 1px solid rgba(245,239,224,.35);
  color: var(--cream); border-radius: 10px; padding: 14px; font-size: 16px; }
/* Quieter than its neighbours without leaving the stack. "Save these as my
   go-to rules" is the only way from "I have just built the table I like" to
   "keep it", and it is offered at the one moment somebody has an opinion about
   their rules — the profile screen is where you are when you are *not*
   thinking about them. But it was drawn at the same weight as "Open a table
   others can join", which is a thing you do now, and it sat two rows under
   Shake 'em!. Raised 16 Aug as a candidate for deletion; demoted instead.

   The padding is deliberately left alone. Dropping it would take the button
   under the touch target `a11ytest.js` insists on, and a control that is
   harder to hit is not the same thing as a control that is quieter. */
/* The border was 18% cream, which is a rounded rectangle you have to already
   know is there. Quiet is the intention and invisible is not: it keeps the
   smaller type and takes a border somebody can find. §1G.5. */
.ghost-btn.quiet { font-size: 14.5px; border-color: rgba(245,239,224,.42); opacity: .85; }
.ghost-btn.quiet:hover { opacity: 1; }

/* The one destructive control in the app. Marked, because "put them out" and
   "never mind" sitting in the same stack of quiet grey buttons is how somebody
   removes a friend by accident. */
.ghost-btn.danger { color: #ff9d92; border-color: rgba(192,57,43,.55); }
.ghost-btn.danger:hover { border-color: var(--red); background: rgba(192,57,43,.16); }
/* Two seconds of having worked. The button says it rather than a toast, because
   a toast can land behind a share sheet or off the side of a narrow screen —
   and a copy button with no visible result reads as a broken one. */
.ghost-btn.is-copied { color: var(--gold); border-color: rgba(224,170,62,.6); }

.panel-h3, .rules-panel h3 { color: var(--heading); }

/* sticky header so you can leave the rule book without scrolling to the end */
/* The bar bleeds to the edges of the panel it sits in, which it does by
   cancelling that panel's padding with a negative margin. The number used to
   be written here as a hard −20px, which is right for `.rules-panel` and wrong
   for every other host: `.lobby-wrap` pads 16px, so the bar hung 4px proud at
   each end — less the panel's 1px border, a 3px tab of darker bar sticking out
   past the corner. Reported 15 Aug off a screenshot of the lobby, and visible
   at every width rather than only on a laptop.

   So the padding is a variable the host declares and the bar reads. A new host
   with different padding now gets a bar that fits it, instead of one that fits
   the panel this was first written for. The radius is 13 rather than 14
   because the bar sits inside the border, and 14 would bleed a pixel of bar
   past the curve at each top corner — the same class of mistake one layer
   further in. */
.rb-bar {
  position: sticky; top: 0; z-index: 5;
  margin: calc(-1 * var(--panel-pad-t, 20px)) calc(-1 * var(--panel-pad-x, 20px)) 12px;
  padding: 12px 16px; display: flex; align-items: center; gap: 10px;
  background: rgba(20,14,8,.94); border-bottom: 1px solid rgba(255,255,255,.12);
  border-radius: 13px 13px 0 0; backdrop-filter: blur(4px);
}
.rb-bar .rb-close {
  background: none; border: 1px solid var(--link-brd); color: var(--cream);
  border-radius: 20px; padding: 8px 16px; font-size: 15px; white-space: nowrap;
}
.rb-bar .rb-title { font-weight: bold; color: var(--heading); font-size: 17px; }
.rb-bar .seg { margin-left: auto; }

/* ---------- The training hand — BACKLOG 3.6 ----------

   The note sits over the felt rather than over the hand, because the hand is
   the thing being talked about and covering it would be the one unforgivable
   layout here. It is pinned to the top of the table screen and the felt is
   tall enough on every phone measured to take it. */
.tutor-cta {
  display: block; width: 100%; margin: 6px 0 16px;
  background: var(--gold); color: var(--btn-text); border: none;
  border-radius: 12px; padding: 14px; font-family: inherit;
  font-size: 16px; font-weight: bold; cursor: pointer;
}
.tutor-cta:hover { filter: brightness(1.06); }
/* Anchored to the bottom, just above the dock, since 19 Aug.

   It was pinned to the top, and the comment below claimed that kept Next
   under the thumb. It did not: the panel's *top* was fixed and its height
   changed with the prose, so the button row moved down the screen when a note
   was showing and up when it shrank to a bar — the length of the phone, on the
   screen a beginner presses forty times. Reported 19 Aug.

   Anchoring the bottom edge fixes both halves of that: the panel sits near the
   hand it is talking about rather than at the far end of the phone, and the
   row — now the last element in both states — stays put while the prose grows
   upward. */
.tutor-note {
  position: absolute; left: 50%; transform: translateX(-50%);
  top: auto; bottom: calc(var(--dock-h, 210px) + 10px);
  width: min(440px, calc(100% - 16px)); z-index: 45;
  background: linear-gradient(170deg, #2a2018, #1c150e);
  border: 1px solid rgba(224,170,62,.55); border-radius: 14px;
  padding: 14px 16px; color: var(--cream);
  box-shadow: 0 10px 30px rgba(0,0,0,.45);
  /* Never under the header. The panel hangs from just above the dock and
     grows upward to its cap, and at the end of the hand the dock is
     collapsed under the review (`body.reviewing .my-dock`), so the last
     note — the longest — grew 52vh up from a low anchor and its top went
     under the sticky header bar (Mike's screenshot, 5 Sep). The second
     term is the room there actually is: the window less the dock, the
     anchor's 10px and the header with a margin. */
  max-height: min(52vh, calc(100vh - var(--dock-h, 210px) - 10px - 120px));
  max-height: min(52dvh, calc(100dvh - var(--dock-h, 210px) - 10px - 120px));
  display: flex; flex-direction: column;
}
.tutor-note .tn-body { overflow-y: auto; min-height: 0; flex: 1 1 auto; }
/* Between notes the panel shrinks to a bar and stops covering the table. The
   reader is watching rather than reading at that moment, and the two were
   fighting for the same screen. Only its size and its weight change: it is
   anchored by its bottom edge, so Next stays exactly where it was. */
.tutor-note.quiet {
  width: auto; max-width: calc(100% - 16px);
  background: rgba(28, 21, 14, .82);
  border-color: rgba(224, 170, 62, .3);
  /* Bottom padding matches the full panel's 14px. The panel is anchored by its
     bottom edge, so any difference here moves the button row by exactly that
     much — which is what was left after the heights were matched. */
  padding: 8px 12px 14px; max-height: none;
}
.tn-watch {
  font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
  opacity: .55; text-align: center; margin-bottom: 6px;
}
.tutor-note.quiet .tn-row { margin-top: 0; }
/* Same height as in the full panel, so Back and Next land on exactly the same
   pixels in both states rather than six off — and 44px is the tap target these
   should have had anyway. The bar gives up six pixels for it. */
.tutor-note.quiet .tn-row button { min-height: 44px; font-size: 14px; }
.tutor-note.quiet .tn-quit { display: none; }
.tutor-note h4 { color: var(--heading); font-size: 17px; margin-bottom: 6px; }
.tutor-note p { font-size: 14.5px; line-height: 1.6; opacity: .95; }
.tutor-note b { color: var(--pill-hi); }
.tn-row { display: flex; gap: 8px; margin-top: 12px; flex: none; }
.tn-row button {
  flex: 1 1 0; min-height: 44px; border-radius: 10px; font-family: inherit;
  font-size: 15px; font-weight: bold; cursor: pointer; border: none;
}
.tn-back { background: rgba(255,255,255,.12); color: var(--cream); }
.tn-back:disabled { opacity: .35; cursor: default; }
.tn-next { background: var(--gold); color: var(--btn-text); }
.tn-quit {
  margin-top: 8px; background: none; border: none; color: var(--cream);
  opacity: .6; font-family: inherit; font-size: 13px; cursor: pointer;
  text-decoration: underline; flex: none; min-height: 32px;
}
/* Nothing on the table is tappable while the hand is being explained. The
   reader advances it with Next and never plays a bone — that is the whole
   "show me, not let me try" decision, and this is where it is enforced rather
   than merely intended. */
body.tutoring #my-hand .domino,
body.tutoring #tk-my-hand .pcard,
body.tutoring .end-marker { pointer-events: none; }

/* The way back, pinned where a thumb is.

   On a phone the reachable part of the screen is the bottom, and every option
   screen was putting its way out at the top, the bottom of a scroll, or both.
   Reported 18 Aug: *"I'd kind of like there to be a back button frozen in a row
   at the bottom of the screen that my thumb can naturally go to."*

   Bottom-left rather than centred, because the middle of that row is where a
   thumb lands by accident and Back is a thing you should mean.

   Three things this has to get right, and the third is the one that bites:
     - `env(safe-area-inset-bottom)` keeps it above the iPhone's home
       indicator, the same way `.my-dock` already does.
     - It replaces the screens' own Back buttons rather than joining them, so
       below 900px those are hidden. Above 900px there is no reach problem and
       a floating button over a wide screen is clutter, so the originals stay
       and this one does not appear.
     - A fixed bar covers whatever the page ends with, so the option screens
       get bottom padding equal to its height. That is the mistake
       `.hand-review` made in another form: something floating over content
       that the content did not know about. */
/* A bar, since 2 Sep, and the button rides on it. The floating chip was the
   right reach and the wrong object: *"I don't really like the floating back
   button. would like it to be a floating footer like 'Texas 42' header up
   top, but with the back button on the footer."* So this is `.rb-bar` turned
   over — the same dark glass, the same hairline, fixed to the foot of the
   glass instead of the head — and the Back chip sits at its left end where
   the thumb already knew to find it. Every game's rules screen, because there
   is one rules screen. §1AC.5. */
.back-foot {
  display: none;
  position: fixed; z-index: 55; bottom: 0;
  /* The panel's width, not the glass's — *"the same width as the dark green
     box. See the floating header that says Texas 42? Want it to be the same
     but on the bottom."* (§1AC.10). The header it mirrors is sticky INSIDE
     the panel and so takes the panel's width and its top radius for free;
     this bar lives outside every screen (see `template.html`), so it is
     sized from the same numbers the panel is built from — `.rulebook`'s
     620px cap and `#screen-rules`'s 18px of padding a side — and finishes
     the panel's foot with its bottom radius and its border. If the panel's
     width ever changes, this changes with it; that is the cost of a copied
     constant and it is written here so the copy is findable. */
  left: 50%; transform: translateX(-50%);
  width: min(100% - 36px, 620px);
  padding: 10px 14px calc(10px + env(safe-area-inset-bottom));
  background: rgba(20, 14, 8, .94);
  border: 1px solid rgba(224,170,62,.5); border-top: 1px solid rgba(255,255,255,.12);
  border-radius: 0 0 13px 13px;
  backdrop-filter: blur(4px);
  box-shadow: 0 -6px 18px rgba(0, 0, 0, .35);
}
.back-sticky {
  background: none; color: var(--cream);
  border: 1px solid var(--pill-hi); border-radius: 24px;
  padding: 10px 22px; min-height: 44px;
  font-family: inherit; font-size: 16px; font-weight: bold;
}
@media (max-width: 899px) {
  .back-foot:not([hidden]) { display: flex; align-items: center; }
  /* A full-width bar covers whatever is scrolled to the foot of the glass —
     including a control that was only just scrolled *into* view, which lands
     exactly there. Caught the night the bar went in: a script's click on
     Shake 'em! reported "`#back-foot` intercepts pointer events". The
     floating chip never had this because it was narrow and left. The
     padding below keeps the last row clear when you scroll to the end;
     `scroll-padding` keeps it clear when something scrolls you there. */
  html { scroll-padding-bottom: calc(80px + env(safe-area-inset-bottom)); }
  /* One way back, not three. */
  #screen-rules .rb-close,
  #screen-rules #btn-back,
  #screen-rules #rb-back,
  #screen-rules #prof-back-foot { display: none; }
  /* Room for it, so the last row of the screen is never underneath it — and
     no more than room: the bar's own measured height (`--foot-h`, set by a
     ResizeObserver in `wireStickyBack`) less 12px, so the pane's rounded foot
     tucks under the bar drawn to finish it instead of stopping a strip of
     felt short of it. 74px was a copied constant for a bar measuring 66, and
     the gap it left was in two of Mike's screenshots on 5 Sep; `pagecheck`
     holds the foot within the bar on every footer screen, both prints.
     The fallback is the old number, for the instant before the observer has
     spoken. §1AE.35. */
  #screen-rules { padding-bottom: calc(var(--foot-h, calc(74px + env(safe-area-inset-bottom))) - 12px); }}

/* ---------- Rule book ---------- */
.rulebook { max-width: 620px; }
.rb-switch { width: fit-content; margin: 0 auto 14px; }

/* The bar is one row of three things — the way back, the title, and the four
   games — and on a phone the four games do not fit beside the other two. They
   ran off the right, so Taroky was unreachable and Moon was half a word; and
   the title, squeezed by what was left, wrapped "How to play" onto three
   lines, which is what made the bar look broken rather than merely tight.

   Reported 16 Aug. Two rows on a narrow screen, and the games become 2x2 —
   which is the same answer the rules presets need at the same width, and they
   are written the same way on purpose. 560px is where it actually stops
   fitting: the four buttons are about 306px, the way back about 90 and the
   title about 110, and with the gaps and the bar's own padding that is 558. */
@media (max-width: 560px) {
  .rb-bar { flex-wrap: wrap; }
  /* Nothing else on the first row now, so the title has all the room it needs
     — but say so, because a three-line title is the symptom somebody reported
     and it should not be able to come back by accident. */
  .rb-bar .rb-title { white-space: nowrap; }
  /* Beats `.rb-bar .seg` above on specificity rather than on source order,
     which is the mistake `csslint.js` exists to catch. */
  .rb-bar .seg.rb-switch {
    margin-left: 0; width: 100%;
    display: grid; grid-template-columns: 1fr 1fr; gap: 3px;
  }
}
.rb-sec { margin: 20px 0; }
.rb-sec h3 {
  color: var(--heading); font-size: 18px; margin-bottom: 8px;
  display: flex; align-items: center; gap: 9px;
}
.rb-num {
  background: var(--gold); color: var(--btn-text); font-size: 13px; font-weight: bold;
  width: 22px; height: 22px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center; flex: none;
}
.rb-sec p { font-size: 14.5px; line-height: 1.72; margin-bottom: 10px; color: var(--cream); opacity: .92; }
.rb-sec b { color: var(--emph); }
.rb-sec .domino { vertical-align: middle; margin: 0 2px; }
.rb-gloss {
  margin-top: 22px; background: rgba(0,0,0,.25); border-radius: 10px; padding: 14px;
}
.rb-gloss h3 { color: var(--heading); font-size: 16px; margin-bottom: 8px; }
.rb-gloss p { font-size: 13px; line-height: 1.9; color: var(--cream); opacity: .88; }
.rb-gloss b { color: var(--emph); }

/* ======================================================================
   THE SEAT CARD - Mike's layout, texas-bones-seat-cards.html, 27 Aug
   ======================================================================

   One component, two orientations. BACKLOG 1Z.

   **Vertical** (`.tok`) for opponents, who have height down the sides of the
   table. **Horizontal** (`.tok-h`) for your partner across the top and for your
   own seat in the dock, where a landscape screen has no height to give.

   ---- the governing rule ----

   **The name, the back and the count stack into a column that is never taller
   than the circle with both its banners.** On a desktop that is 76px. That one
   constraint keeps the partner's seat short - it is what buys back the vertical
   room above the play area - and it is what decides how big the back may get.

   Everything below derives from it. Do not add a number here that could be
   worked out from `--sc-stack` and `--sc-name`; that is how the pile it
   replaced came to have a step, a width and a height that disagreed.

   ---- the sizes ----

   Four layout modes, one formula. `--sc-back` is NOT chosen: it falls out of
   the stack minus the name line minus 8, exactly as the spec says, so a future
   mode needs no new rule.

       mode              disc  stack  name   back ceiling
       desktop normal     52     76     21    84 x 47
       desktop big text   60     86     25    94 x 53
       mobile normal      44     66     19    69 x 39
       mobile big text    50     73     22    77 x 43

   ---- what carries meaning ----

   The **orientation is the team signal**: in 42 the partner is the only seat
   lying on its side and it is the one across from you. No badge, no colour, no
   legend - which is why the old "partner" tag is gone rather than enlarged.

   Row heights never change, so a seat that gains a banner cannot shove its
   neighbours. That is 1R.8's rule, kept. */
.tok {
  --sc-disc: calc(44px * var(--sc-scale));      /* mobile normal is the base; desktop raises it */
  --sc-stack: calc(66px * var(--sc-scale));
  --sc-name: calc(19px * var(--sc-scale));
  /* The CEILING is `stack - name - 8` — 47px on a desktop. The specimens are
     drawn at the SUGGESTED size instead, which is what makes the vertical
     seat's third row 40px and the whole seat 156 rather than 197. Mike:
     *"the specimens on this page are drawn at 64px — the suggested desktop
     size, not the ceiling. Push to 84 if you want the backs louder; the seat
     won't grow, the name just gets tighter company."*

     So: suggested by default, and the ceiling is written beside it because
     raising this is a one-line decision he has already described. */
  --sc-back-ceiling: calc(var(--sc-stack) - var(--sc-name) - 8px);
  --sc-back: calc(30px * var(--sc-scale));
  --sc-chip: calc(10px * var(--sc-scale));
  --sc-chip-max: calc(124px * var(--sc-scale));   /* the phone's; the larger set raises it. 116 until
                              4 Sep: `168 · BLANKS` needs 120 at the phone's 12px */
  --sc-count: calc(18px * var(--sc-scale));

  /* ---- the larger set, named once (Track A, C3) ----
     What a seat is when it has room. The window decides when that is, for
     now — see the note above .seat-w about why it cannot be the column yet —
     and these are the numbers it switches to, defined here and nowhere
     else. */
  --sc-lg-disc: calc(52px * var(--sc-scale));
  --sc-lg-stack: calc(76px * var(--sc-scale));
  --sc-lg-name: calc(21px * var(--sc-scale));
  --sc-lg-back: calc(56px * var(--sc-scale));
  --sc-lg-chip: calc(10.5px * var(--sc-scale));
  --sc-lg-chip-max: calc(176px * var(--sc-scale));
  --sc-lg-count: calc(21px * var(--sc-scale));

  /* ---- and the rest of what the unit owns, promoted 18 Sep (Track A, C2) ----

     These were raw numbers inside the rules below. Same values, one home
     each: the point of the slice is that a dimension has ONE definition, and
     a number typed into a rule is a definition nothing can find. Colours are
     deliberately not here — they belong to the palette, not to this unit's
     geometry, and mixing the two would make the build gate in C6 unable to
     say what it is checking. */
  --sc-pad-ring: 4px;      /* the ring's breath around the disc */
  --sc-slot: 4px;          /* what a row reserves for a banner's overhang */
  --sc-gap-row: 7px;       /* between the circle, the name and the back */
  --sc-gap-name: 4px;      /* name to whatever rides beside it */
  --sc-gap-tag: 5px;       /* except HOST, which sat a pixel further out */
  --sc-gap-hand: 11px;     /* the back to the count */
  --sc-gap-info: 8px;      /* the lying-down card's name over its hand */
  --sc-gap-h: 30px;        /* and its circle to that column — clears the shield */
  /* The three caps SCALE, and the reason is a bug this slice shipped for
     about an hour: a cap that bounds text must grow with the text. Big print
     made the bid shield's type 1.15x and left this at a raw 126px, so
     "168 · BLANKS" wanted 124px of a 122px box and pagecheck refused the
     build — the same shape of fault as the sixty-five numbers, made by the
     fix for them. Anything that bounds something scaled, scales. */
  --sc-card-max: calc(170px * var(--sc-scale));    /* a standing card never grows past this */
  --sc-name-max-h: calc(190px * var(--sc-scale));  /* the lying-down card's name, before the ellipsis */
  --sc-chip-max-h: calc(126px * var(--sc-scale));  /* and its chip, narrower than the standing one */
  --sc-ring-w: 2px;        /* the ring itself */
  --sc-ring-halo: 4px;     /* the turn's halo, and the bidder's */
  --sc-ring-halo-2: 7px;   /* the outer one, when a player is both */
  --sc-pulse-out: 6px;     /* how far the one animation on the felt reaches */
  --sc-disc-w: 1px;        /* the disc's own edge */
  --sc-chip-pad: 3px 10px;
  --sc-chip-w: 2px;        /* a banner's border, which is the felt showing through */
  --sc-chip-radius: 3px;
  --sc-chip-track: .14em;
  --sc-chip-out: 58%;      /* how far a banner hangs off the circle */
  --sc-face: .52;          /* the face, as a share of the disc */
  --sc-count-sub: .71;     /* the hand size beside the count */
  --sc-count-word: .6;     /* and Moon's word after it */
  --sc-host-ratio: .5;     /* HOST, as a share of the name */
  --sc-host-min: 9px;      /* but never smaller than this */
  --sc-host-pad: 3px 5px 2px;
  --sc-host-track: .12em;
  --sc-hair: 1px;          /* the away banner's inset edge */
  --sc-gap-hair: 2px;      /* either side of the count's divider */
  --sc-gap-hand-h: 10px;   /* the lying-down card's back to its count */
  --sc-gap-hand-tight: 3px;/* stacked on a phone, where the column is narrow */
  --sc-gap-hand-col: 8px;  /* and between the back's column and the count's */
  --sc-gap-line: 8px;      /* Fives' Focus line, where the card is a row */
  --sc-gap-line-wide: 10px;
  --sc-chip-bid: calc(12px * var(--sc-scale));   /* the phone's bid shield, which
                              says the longest thing any shield says */
  --sc-move: 180ms ease;   /* the only thing here that is a duration */

  display: grid;
  /* 80 / 22 / 40 on a desktop, and every one of them derived: the circle with
     its banner overhangs, the name line, and the back's row. Row heights never
     change, so a seat that gains a banner cannot shove its neighbours. */
  grid-template-rows: calc(var(--sc-stack) + var(--sc-slot))
                      var(--sc-name)
                      calc(var(--sc-back) + var(--sc-slot));
  justify-items: center; align-items: center; row-gap: var(--sc-gap-row);
  width: 100%; max-width: var(--sc-card-max);
}
/* 56 on a desktop, from 36 — the sixth ask (§1AC.31): *"the other players'
   dominoes are still not big enough for what I want to see on desktop."*
   Past the ceiling the card's arithmetic gave (47, which kept the seat's
   height), on Mike's own note in §1Z: *"push to 84 if you want the backs
   louder; the seat won't grow, the name just gets tighter company."* The
   vertical card's third row is `auto` on a desktop below so it takes the
   extra 20px; the side seats have three hundred pixels of felt under them.
   The chip budget goes to 176 with it: `BID 30 · BLANKS` and `SHOOT THE
   MOON` did not fit 150 (§1AC.32). `fitcheck` holds 56 as a floor. */
/* A card that cannot be asked about its room still reads the window: the
   partner lying across the top, your own plate in the dock and the lane, and
   every seat in Fives, Moon and Taroky, whose columns are sized by their
   contents (see .seat-w above).

   42's two side seats are no longer among them — see the container query
   below, which is what §4.20 was for. */
@media (min-width: 900px) {
  .tok { --sc-disc: var(--sc-lg-disc); --sc-stack: var(--sc-lg-stack);
         --sc-name: var(--sc-lg-name); --sc-back: var(--sc-lg-back);
         --sc-chip: var(--sc-lg-chip); --sc-chip-max: var(--sc-lg-chip-max);
         --sc-count: var(--sc-lg-count); }}
/* ---- why the seat still does NOT ask its column (§4.20, 19 Sep) ----

   The column has a width of its own now and is contained above, so the
   query became possible — and then the measuring said not to ship it.

   A query on the column reads one dimension. The seat needs two. Sizing
   the card by the column alone gives the larger seat to every band where
   the column is 120px or wider, which includes 520 to 700 (the column is
   166 to 212 there, wider than a desktop's 168) — and the larger card is
   38px taller. Measured at those widths with four bones on the table: the
   page scrolled vertically by 38px at 600, 86 in Big print, and 136 and
   325 at 520. The height the larger seat needs is 760 in ordinary print
   and 820 to 960 in Big print, which those windows do not have.

   Gating it on a height as well would leave it firing at exactly one
   width (700), because from 701 the column is 118 and below the line. So
   it buys nothing today and is not here.

   What IS here is the containment, which costs nothing and is what the
   next component needs: the column can be asked, as soon as there is a
   rule worth asking it. The rule wants the height too, and a container
   query cannot ask for height without containing it — which would need
   the column's height to come from outside it as well. That is the next
   piece of this, and it is filed rather than guessed at. */

/* ---- Big print is a scale, not a second stylesheet (Track A, C4) ----

   It used to be eleven hand-typed numbers in four blocks, and the state they
   were in is the argument for this change: the four they redefined scaled by
   four different ratios — the disc by 1.14, the stack by 1.11, the name by
   1.16, the back by 1.17 — and the three they did not redefine at all (the
   chip, its cap, and the count on a phone) silently scaled by 1.0. Nobody
   decided that. It is what a second set of values does when one of them is
   edited and its twin is not, which is the fault CLAUDE.md has a whole
   section about.

   So: one number. Every size the unit owns is the ordinary size times
   --sc-scale, which is 1 in ordinary print and 1.15 in Big print — Mike's
   choice, 18 Sep, as the closest single value to what was already there.
   The rule this project already states, finally applied to the place it was
   written for: give the outside world a scale to multiply, never a value to
   replace. */
:root { --sc-scale: 1; }
html.big-print { --sc-scale: 1.15; }
/* Both on the root, and that is not an accident: the first draft put the 1 on
   .tok itself, where a card's own declaration beats the value it would have
   inherited — so Big print quietly drew SMALLER seats than ordinary print.
   Caught by the measurement, in the one direction fitcheck also refuses. */


/* ---- the circle: disc, ring, and both banner slots ---- */
.tok .ring {
  display: inline-flex; position: relative; padding: var(--sc-pad-ring); border-radius: 50%;
  border: var(--sc-ring-w) solid rgba(245,239,224,.16);
  transition: border-color var(--sc-move), box-shadow var(--sc-move);
}
.tok .disc {
  position: relative; border-radius: 50%;
  width: var(--sc-disc); height: var(--sc-disc);
  font-size: calc(var(--sc-disc) * var(--sc-face)); line-height: 1;
  background: rgba(0,0,0,.34); border: var(--sc-disc-w) solid rgba(245,239,224,.16);
  display: flex; align-items: center; justify-content: center;
}
/* Their turn takes the ring; the bidder keeps a gold one. When a player is
   both - the only case where two treatments collide - red takes the ring and
   gold survives as a halo just outside it. Mike's spec. */
.tok.is-turn .ring { border-color: var(--red); box-shadow: 0 0 0 var(--sc-ring-halo) rgba(192,57,43,.28); }
.tok.is-bidder .ring { border-color: var(--gold); box-shadow: 0 0 0 var(--sc-ring-halo) rgba(224,170,62,.26); }
.tok.is-bidder.is-turn .ring {
  border-color: var(--red);
  box-shadow: 0 0 0 var(--sc-ring-halo) rgba(192,57,43,.28), 0 0 0 var(--sc-ring-halo-2) rgba(224,170,62,.26);
}
/* The pulse is the ONLY animation on the felt, so motion always means "act
   here". One seat at a time, because only one seat is ever on turn. */
.tok.is-turn .ring::after {
  content: ""; position: absolute; inset: calc(var(--sc-pulse-out) * -1); border-radius: 50%;
  border: var(--sc-ring-w) solid var(--red); opacity: .55;
  animation: sc-pulse 2.4s ease-in-out infinite;
}
@keyframes sc-pulse {
  0%   { transform: scale(.94); opacity: .55; }
  70%  { transform: scale(1.18); opacity: 0; }
  100% { transform: scale(1.18); opacity: 0; }}
@media (prefers-reduced-motion: reduce) {
  .tok.is-turn .ring::after { animation: none; opacity: .4; }}

/* ---- the banners, welded to the circle ---- */
.tok .chip {
  position: absolute; left: 50%; z-index: 3;
  font-family: var(--brand-font);
  font-size: var(--sc-chip); font-weight: bold; letter-spacing: var(--sc-chip-track);
  text-transform: uppercase; white-space: nowrap; line-height: 1;
  padding: var(--sc-chip-pad); border-radius: var(--sc-chip-radius);
  border: var(--sc-chip-w) solid var(--felt-dark);
  /* A number, never a percentage.

     The chip is absolutely positioned inside `.disc`, so `100%` resolves
     against the DISC — 44px on a phone — and every banner truncated to "S…"
     and "B…". Percentages are relative to the containing block, and the
     containing block here is deliberately tiny.

     So the budget is a variable the card sets per mode: 150 on a desktop,
     which is what the spec allows for 42's longest bid, and less where the
     seat itself is narrower. BACKLOG 1Z. */
  max-width: var(--sc-chip-max); overflow: hidden; text-overflow: ellipsis;
}
.tok .chip-top { top: 0; transform: translate(-50%, calc(var(--sc-chip-out) * -1)); }
.tok .chip-bot { bottom: 0; transform: translate(-50%, var(--sc-chip-out)); }
.tok .chip.shaker { background: var(--cream); color: var(--ink); }
.tok .chip.bid { background: var(--gold); color: #2c2005; }
.tok .chip.away {
  background: #0d3a28; color: rgba(245,239,224,.58);
  box-shadow: inset 0 0 0 var(--sc-hair) rgba(245,239,224,.16);
}
/* Away is a state you need to act on, so it takes the whole seat down a key
   rather than only adding a word. */
.tok.is-away .disc { filter: grayscale(1); opacity: .45; }
.tok.is-away .pname { color: rgba(245,239,224,.38); }
.tok.is-away .count b { color: rgba(245,239,224,.58); }

/* ---- the name ---- */
.tok .namerow {
  display: flex; align-items: baseline; justify-content: center;
  gap: var(--sc-gap-name); min-width: 0; max-width: 100%;
}
.tok.tok-h .namerow { justify-content: flex-start; }
.tok .pname {
  font-weight: bold; font-size: var(--sc-name); line-height: 1.4;
  color: var(--cream); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}

/* ---- the hand: one back, and the count ---- */
.tok .hand { display: flex; align-items: center; justify-content: center; gap: var(--sc-gap-hand); }
/* The back is the player's own art and is always lying down. Its height comes
   from the governing rule and its width from the bone's own aspect, so it is
   as large as it can be in every mode without making the seat taller. */
.tok .dom-back .domino.back.small.h {
  height: var(--sc-back); width: calc(var(--sc-back) * 2);
}
.tok .dom-back .domino.back.small .half {
  width: var(--sc-back); height: var(--sc-back);
}
.tok .count {
  display: flex; align-items: baseline;
  font-variant-numeric: tabular-nums; line-height: 1;
  font-family: var(--brand-font);
}
.tok .count b { font-size: var(--sc-count); font-weight: bold; color: var(--cream); }
.tok .count s,
.tok .count u {
  font-size: calc(var(--sc-count) * var(--sc-count-sub)); font-weight: 600;
  text-decoration: none; color: rgba(245,239,224,.58);
}
.tok .count s { color: rgba(245,239,224,.38); margin: 0 var(--sc-gap-hair); }
/* A word after the number — Moon's "3 tricks" (§1AE.39) — set like the hand
   size was, small and quiet, so the number stays the thing read. */
.tok .count u.cw { margin-left: var(--sc-gap-name); font-size: calc(var(--sc-count) * var(--sc-count-word)); letter-spacing: .02em; }

/* ---- horizontal: your partner, and your own seat ---- */
.tok.tok-h {
  grid-template-rows: auto;
  grid-template-columns: auto minmax(0, 1fr);
  column-gap: var(--sc-gap-h);      /* clears the bid shield's overhang */
  align-items: center; justify-items: start;
  width: auto; max-width: none;
  height: calc(var(--sc-stack) + var(--sc-slot));
}
.tok.tok-h .info {
  display: flex; flex-direction: column; align-items: flex-start;
  gap: var(--sc-gap-info); min-width: 0;
}
.tok.tok-h .pname { text-align: left; max-width: var(--sc-name-max-h); }
.tok.tok-h .info > .namerow { width: 100%; }
.tok.tok-h .hand { justify-content: flex-start; gap: var(--sc-gap-hand-h); }
.tok.tok-h .chip { max-width: min(var(--sc-chip-max), var(--sc-chip-max-h)); }
/* The lying-down card's 118 was the phone partner's budget; a desktop's own
   plate wears `SHOOT THE MOON` and gets the full one. §1AC.32. */
@media (min-width: 900px) { .tok.tok-h .chip { max-width: var(--sc-chip-max); } }
/* The phone's shield drops the word BID and some of its tracking — see
   `seatUnitHTML` for why the word is the part that can go. §1AA.7. */
/* The shield's two forms — see `bidChipShort42` in ui.js. */
.tok .chip .chip-short { display: none; }
@media (max-width: 699px) {
  .tok .chip .chip-long { display: none; }
  .tok .chip .chip-short { display: inline; }
  .tok .chip.bid .chip-word { display: none; }
  .tok .chip { letter-spacing: .08em; }
  /* The short form can afford to be bigger than the phrase could. With BID
     gone the shield is a number (`5`) or a number and a suit (`30 · ACES`),
     and 10px — the size that fitted `BID 30 · ACES` across 116px — read as
     a speck on Moon (§1AC.19). 13 here, 14 in Big print; `30 · BLANKS`, the
     longest short form, still fits the budget at 360. */
  /* 12 and 13, from 13 and 14 (4 Sep): the short form `168 · BLANKS` is
     the longest a phone shows and it needs the twelve, measured by
     `pagecheck` against every bid and trump on every seat. */
  .tok .chip.bid { font-size: var(--sc-chip-bid); letter-spacing: .03em; }
  /* The Big print twin here used to be a hand-typed 13px — a sixty-sixth
     value, outside the token block, which C4's scale missed and the gate
     below found. It is the same 12 times the scale now, and the tracking is
     all that differs. */
  html.big-print .tok .chip.bid { letter-spacing: .02em; }
}
/* The opponent's card as a column on a phone: face, name, back, count, each
   centred on the one above. Mike, 2 Sep, off a 430 shot where Ed's name sat
   left of his back and the count beside it: *"let's put the domino counts for
   opponents BELOW the domino picture since we have additional vertical space
   below it."* Phone only, by his call — the desktop keeps the row he drew in
   §1Z. The vertical card's third row was a fixed height derived from the
   back; with the count under the back it holds two things, so it is `auto`
   here and the seat grows by one count line. The back's own size is
   untouched — `--sc-back` still derives it, and nothing here states a width
   (CLAUDE.md, "a derived number cannot be overridden"). Every game's side
   seats are vertical cards, so Moon's tricks-taken and Taroky's card come
   down with it. §1AC.2. */
@media (max-width: 899px) {
  .tok:not(.tok-h) { grid-template-rows: calc(var(--sc-stack) + var(--sc-slot)) var(--sc-name) auto; }
  .tok:not(.tok-h) .hand { flex-direction: column; gap: var(--sc-gap-hand-tight); }
}
/* And on a desktop the back is centred ALONE, the count off to its right.
   The row centred the pair, so the back sat left of the name by half the
   count's width — *"the centring should be on the domino with the number off
   to the right and not affecting the centring"* (§1AC.34, 3 Sep, and for
   the whole card: *"the icon above should be centred on the domino"*). A
   three-track grid, the back in the middle track and the count at the start
   of the third, keeps the back on the card's centre line whatever the count
   says or whether it is there. The third row is `auto` here too, for the
   56px back (§1AC.31). */
@media (min-width: 900px) {
  .tok:not(.tok-h) { grid-template-rows: calc(var(--sc-stack) + var(--sc-slot)) var(--sc-name) auto; }
  .tok:not(.tok-h) .hand {
    /* `minmax(0, 1fr)`, not `1fr`: a bare `1fr` cannot shrink below its
       content, and `7 / 7` is wider than the free space beside a 112px back
       in a 168px card — the count's track held its width, the empty one
       went to nothing, and the back sat 12px left of the card's centre.
       Zero-floored, the two tracks stay equal and the count overflows its
       own track to the right, which is exactly where he wants it. */
    display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center;
    justify-items: start; column-gap: var(--sc-gap-hand-col); width: 100%;
  }
  .tok:not(.tok-h) .hand .count { white-space: nowrap; }
  .tok:not(.tok-h) .hand .dom-back { grid-column: 2; }
  .tok:not(.tok-h) .hand .count { grid-column: 3; }
}

/* The rename pencil rides beside the name and costs the row no height - the
   name line is a fixed size either way. */
.tok .seat-edit { flex: none; margin-left: var(--sc-gap-name); }
/* HOST beside the name at an online table (§1AG.15) — the bid shield's gold
   and letter-spacing, at half the name's size, so it reads as a label on the
   seat rather than a second name. `flex: none` so it is the name that
   shortens when a card is narrow, never the word. */
.tok .host-tag {
  flex: none; align-self: center; margin-left: var(--sc-gap-tag);
  font-family: var(--brand-font); font-weight: bold; text-transform: uppercase;
  font-size: max(var(--sc-host-min), calc(var(--sc-name) * var(--sc-host-ratio)));
  letter-spacing: var(--sc-host-track); line-height: 1;
  padding: var(--sc-host-pad); border-radius: var(--sc-chip-radius);
  background: var(--gold); color: #2c2005;
}
/* The banner copy, for every seat on a phone (see `seatUnitHTML`): gold like
   the bid shield. Every plate, not only the standing ones: your own plate
   lies in the lane beside the trick’s south bone, and HOST beside the name
   ran into the bone at 390. */
.tok .chip.host { background: var(--gold); color: #2c2005; display: none; }
@media (max-width: 899px) {
  .tok .chip.host { display: block; }
  .tok .host-tag { display: none; }
}

/* The order of play used to be written across the top as a strip of four
   names with arrows between them — `.turn-strip` and its `.ts-*` parts, gone
   16 Aug along with `turnOrderHTML` in `ui.js`.

   It went from wide screens in v1.55.0, where the argument was that the
   travelling token and your own breathing plate already answer "whose turn?",
   and it was kept on a phone on the grounds that the token is smaller there.
   Reported from a phone on 16 Aug, by the person who made that call: the order
   is clockwise in 42 and counterclockwise in Fives and everybody knows it, so
   the strip was four names telling you a rule rather than a state.

   Cut from the markup rather than hidden, because the reason it was kept in it
   was the phone, and the phone is what was just removed. The breathing plate
   that pays for it — `.seat-unit.su-me.is-turn` above — is deliberately not
   width-guarded, so it was already doing this job on a phone before the strip
   came off. */
.status-msg { display: block; }
/* "I misclicked." Only on screen while the table could actually honour it —
   your own bone, nothing played since — so it is a moment rather than a
   feature you can lean on. Quiet: it is an admission, not an action, and a
   loud button here would invite a different game than the one being played. */
.undo-btn {
  display: inline-block; margin-top: 6px;
  background: rgba(0,0,0,.32); color: var(--cream);
  border: 1px solid rgba(255,255,255,.22); border-radius: 16px;
  padding: 5px 14px; font-size: 13px; font-family: inherit;
}
.undo-btn:hover { border-color: var(--gold); }
/* The take-back offer itself, and the line that replaces it while the table
   decides. §1AG.6, 16 Sep: *"that button should probably be higher"* — it
   already sat at the top of the felt, a dark 13px pill that read as part of
   the table's furniture, and at a real table nobody found it in the
   seconds it is there. So it is still an admission rather than an action,
   and no longer a whisper: a gold edge and the words saying what it does.
   The status line takes its place, the same shape, so ask → waiting → how
   it ended all happen in the one spot. The seconds are tabular so the line
   does not twitch as it counts. */
.undo-btn.undo-ask {
  border-color: rgba(224,170,62,.75); font-size: 14px; padding: 6px 16px;
  background: rgba(0,0,0,.45);
}
.undo-state {
  display: inline-block; margin-top: 6px;
  background: rgba(0,0,0,.45); color: var(--cream);
  border: 1px solid var(--gold); border-radius: 16px;
  padding: 6px 16px; font-size: 14px; font-family: var(--brand-font); font-weight: bold;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
html.big-print .undo-btn.undo-ask, html.big-print .undo-state { font-size: 16px; }
/* Somebody said something (§1AG.13): a gold glow that rises and goes, once,
   on the talk box that was already open. Motion on the felt otherwise means
   "act here" (the turn ring), so this is a glow rather than a pulse, and it
   does not repeat. Without motion it is a gold edge for the same moment. */
.chat-corner.heard { animation: talk-heard 1.6s ease-out; }
@keyframes talk-heard {
  0%   { box-shadow: 0 0 0 0 rgba(224,170,62,0); border-color: rgba(224,170,62,.4); }
  20%  { box-shadow: 0 0 0 3px rgba(224,170,62,.75), 0 0 22px rgba(224,170,62,.45); border-color: var(--gold); }
  100% { box-shadow: 0 0 0 0 rgba(224,170,62,0); }}
@media (prefers-reduced-motion: reduce) {
  .chat-corner.heard { animation: none; box-shadow: 0 0 0 2px var(--gold); }}
.undo-count { font-variant-numeric: tabular-nums; color: var(--gold); }
/* The capture button for §1A.9. Red-edged because it is a fault report rather
   than an offer, and it only ever appears in a state the game cannot deal. */
.odd-btn { border-color: rgba(192,57,43,.8); color: #ffd9d2; }

/* The way off the rail and into a chair. Same shape as the misclick button —
   both are quiet offers that appear only in the moment they mean something —
   but gold-edged, because this one is an invitation rather than an admission. */
.take-chair { border-color: rgba(224,170,62,.55); color: var(--pill-hi); font-weight: bold; }
.undo-row { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }


/* ---------- Shared table chrome ---------- */
/* Three columns, and the middle one is the wordmark. It was a flex row with
   `space-between`, which centres the middle child only while the two outside
   it happen to be the same width — so adding the table's name to the left made
   the wordmark drift right, which is exactly what was reported on 15 Aug. Equal
   `1fr` tracks either side pin it to the centre of the screen whatever is
   beside it, and every table screen has the same three children, so they all
   get it.

   `minmax(0, 1fr)` rather than `1fr`: a long table name would otherwise widen
   its own track and push the wordmark off centre again by another route. */
.table-top {
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  justify-content: space-between; align-items: center;
  /* The bar's height is pinned, so the wordmark can be given more width
     without the green strip growing to match it. */
  min-height: 58px; box-sizing: border-box;
  padding: 4px 14px; background: rgba(0,0,0,.35); font-size: 15px; gap: 9px; flex-wrap: wrap;
  color: var(--cream);
}
.score-pill { background: rgba(0,0,0,.4); border-radius: 20px; padding: 7px 16px; font-size: 15px; }
/* The two outside columns. The wordmark is centred on the screen; these hold
   whatever sits either side of it. */
/* Grid children stretch to fill their track unless told not to, and that is
   how "Leave" became 557px wide on the Fives table against 123px on the 42
   one: 42 wraps its buttons in `.tt-side`, and Fives, Moon and Taroky put them
   straight into the grid, where each filled a whole `1fr` column. Reported
   15 Aug as the buttons being "way too big" — which they were, by a factor of
   four and a half, and not because of any font size.

   So the default is that a bar child takes its own width and sits where it
   belongs, and `.tt-side` opts back into filling — it has to, because the
   table's name centres inside it. */
.table-top > * { justify-self: center; }
.table-top > :first-child { justify-self: start; }
.table-top > :last-child { justify-self: end; }
.table-top > .tt-side { justify-self: stretch; }
.tt-side { display: flex; align-items: center; gap: 10px; min-width: 0; }
.tt-right { justify-content: flex-end; }
/* The table's name sits centred in the gap between "← Leave" and the wordmark,
   rather than tucked against the button — Mike, 15 Aug. `flex: 1` takes the
   rest of the track and centres inside it. */
.tt-side .table-called-top {
  /* Takes the rest of its track and centres in it, which is what puts the name
     midway between "← Leave" and the wordmark rather than tucked against the
     button. `max-width: none` overrides the cap set with the rest of its
     styling: a capped box cannot fill the track, so it sat at the start of it
     and measured 196px off centre. The track is `minmax(0, 1fr)` and the text
     ellipsises, so a long name still cannot push the wordmark. */
  flex: 1 1 auto; text-align: center; max-width: none;
}

.score-pill b { color: var(--pill-hi); }
/* The version tag, bottom-right of every screen — see the boot in ui.js and
   BACKLOG §1AA.4. Faint on purpose: it is for a screenshot, not a reader, and
   it must never win a glance from the table. It sits inside the safe-area
   inset and above the dock's pill row on a phone, where the row's right end
   comes within 35px of the edge at 430 and 9px at 360. */
.build-tag {
  position: fixed; right: 5px; bottom: calc(3px + env(safe-area-inset-bottom));
  z-index: 40; pointer-events: none; user-select: none;
  font-family: var(--brand-font); font-size: 10px; letter-spacing: .04em;
  line-height: 1; color: rgba(245,239,224,.42);
  text-shadow: 0 1px 2px rgba(0,0,0,.6);
}
/* Off the home screen (§1AC.3): the footer there already prints the version
   and the build in the middle of the page, and a second copy in the corner
   was reported as noise. `showScreen` sets the class. */
body.on-home .build-tag { display: none; }
@media (max-width: 699px) {
  /* In the corner. This sat 46px up to stay off the dock's pill row —
     §1AA.4's "over no control" — which put it level with the HAND chip, and
     on Mike's phone, where the type runs wider than Chromium's, on top of its
     last digit (§1AC.3, screenshot at 430). His answer: *"needs to be all the
     way at the bottom and in corner … behind the chip if it overlaps."* So it
     goes to the true corner, which is UNDER the chip row rather than beside
     it: the dock's bottom padding is 10px plus the inset on a phone and the
     chips end there, so a 9px tag standing 1px off the floor touches them by
     a pixel at most. It cannot go behind them — the screen is its own
     stacking layer (`.screen { z-index: 1 }`) and this is fixed to the body,
     so nothing inside a screen can paint over it without moving it into the
     screen, which the renderers would then be free to wipe. Placement does
     the job instead; `pagecheck` asserts it is within 8px of both edges and
     over no interactive control (a span reading HAND is not one). */
  .build-tag { bottom: calc(1px + env(safe-area-inset-bottom)); right: 3px; font-size: 9px; }
}
.status-line { text-align: center; padding: 8px 10px; font-size: 17px; min-height: 36px; color: var(--status-text); font-weight: bold; }

/* ---------- the table's commentary, in the middle of the table ---------- §1K.6

   Two channels wrote this and neither knew about the other, so a standing
   message and a passing one arrived on the same pixels. One stack now, in the
   green between the partner's bones and the played ones, which is the space
   1K.3 freed by moving his name down.

   Click-through, always. It sits over the felt and there are bones under it. */
.status-stack {
  /* Anchored below the seats rather than to a share of the felt. `top: 27%`
     was chosen against a 932px felt and lands squarely on the seat faces on a
     taller phone — the screenshots caught the sentence reading as one line
     with an emoji at each end. The seats have a known height, so this is a
     number against them rather than a proportion of a box that changes.
     BACKLOG 1M.6. */
  /* The middle of the table, sitting directly on top of "trick N of 7".

     It used to hang from the felt's top edge by a typed offset — 150px, then
     240 on Moon — and every time the seats changed height that number went
     stale and the sentence landed on somebody's chair. Asked for plainly:
     *"move that to the middle right above 'trick 5 of 7' and make them centered
     vertically and horizontally on the table."*

     `top: 50%` with the box pulled fully above its own anchor puts its foot on
     the felt's centre line, which is exactly where `.tp-c` sits — the two read
     as one stack. It is anchored to the middle of the felt rather than to
     anything that can change size, so there is no number left to go stale.
     BACKLOG 1U.3. */
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 4; pointer-events: none; display: flex; flex-direction: column;
  align-items: center; gap: 5px; width: max-content; max-width: calc(100% - 24px);
}
/* And it may not be wider than the gap it sits in.

   Photographed on an Android phone, 26 Aug: *"Waiting on Bobo..."* and
   *"Bobo names treys"* stacked across the middle of the table with a face at
   each end of them - the sentence reading as though it belonged to the two
   side players rather than to the table. The cause is one number: the stack is
   allowed `100% - 24px` of the felt, and at 360 the gap between the two side
   seats is 150px of it. The rest of the sentence was over their bones.

   The seats take 27% a side below 700px (see `.seats-mid > .seat`), so the
   middle is 46% and this is what is left after a margin. It wraps instead of
   spilling, which is the right trade: a wrapped sentence is still the
   table's, and a spilled one has visibly become somebody's seat. */
@media (max-width: 700px) {
  .status-stack { max-width: 42%; }
}
/* This is the sentence the table speaks, in the middle of the table, and it
   was 17px on a 1900px screen — *"'your play' is TOO SMALL."*

   The desktop bump that should have caught it was written against
   `.status-msg`, which is the copy in the live region ABOVE the felt that
   nobody sees. Two elements carry one sentence — see `syncStatusStack` in
   ui.js — and for two releases the size was applied to the invisible one. A
   rule that styles the wrong twin fails silently and forever. BACKLOG 1U.3. */
.st-standing {
  font-size: 17px; font-weight: bold; color: var(--status-text);
  text-shadow: 0 2px 6px rgba(0,0,0,.55); text-align: center;
}
@media (min-width: 900px) { .st-standing { font-size: 26px; }}
/* Passing messages get the dark pill the toast had, because they are events
   rather than the state of the table and should read as arriving. */
.st-passing {
  /* Table text, not a toast. The dark pill read as a popup arriving over the
     game — Mike, pic 6, 23 Aug: "should just be the text that appears on the
     table". Same voice as the standing line, slightly smaller, still fading. */
  font-size: 15px; font-weight: bold; color: var(--status-text);
  text-shadow: 0 2px 6px rgba(0,0,0,.55);
  text-align: center; opacity: 1; transition: opacity .3s ease;
}
.st-passing.going { opacity: 0; }
@media (prefers-reduced-motion: reduce) { .st-passing { transition: none; }}

/* The old lane keeps the sentence for a screen reader and stops drawing it.
   It cannot simply be hidden: the undo offer shares this element and is a
   control somebody has a few seconds to press. So the message is taken out of
   sight and the line keeps whatever else is in it. */
#screen-42 .status-line .status-msg,
#screen-moon .status-line .status-msg {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
#screen-42 .status-line, #screen-moon .status-line { min-height: 0; padding: 0 10px; }
.exit-btn { background: none; border: 1px solid rgba(245,239,224,.35); color: var(--cream);
  border-radius: 16px; padding: 7px 15px; font-size: 14px; }
/* Leave and Rules, in the table's own colour and bigger. Asked for 20 Aug, and
   asked for filled after the caution that it makes the two permanent controls
   the brightest things on the felt: *"full fill, as asked."*

   Only in the table's top bar. `.exit-btn` is also the record sheet's Close,
   which is a way out of a sheet rather than a fixture of the table and has its
   own treatment on a phone. */
.table-top .exit-btn {
  background: var(--gold); color: var(--btn-text); border-color: transparent;
  font-size: 15px; padding: 0 18px; font-weight: bold;
  /* One height for the pair, whatever the words do. TABLE RULES wraps to two
     lines on a phone and the chip grew with it, so the bar's two ends did not
     match — *"keep LEAVE chip size as-is, just remove extra vertical yellow
     space in rules chip."* The box is fixed at what LEAVE measured (15px
     type, 9px above and below, the border) and the padding comes out of the
     vertical, so two lines at a tight leading sit inside the same 38px as
     one. All four tables draw this pair. §1AC.1. */
  display: inline-flex; align-items: center; justify-content: center;
  height: 38px; line-height: 1.05; text-align: center;
}

/* ---------- Dominoes ---------- */
.domino {
  display: inline-flex; background: var(--cream); border-radius: 6px;
  box-shadow: 0 2px 4px rgba(0,0,0,.45), inset 0 0 0 1px rgba(0,0,0,.15);
  position: relative; flex: none;
}
.domino.h { flex-direction: row; width: 88px; height: 44px; }
.domino.v { flex-direction: column; width: 44px; height: 88px; }
.domino .half { position: relative; width: 44px; height: 44px; }
.domino.h .half:first-child { border-right: 2px solid #b7ac93; }
.domino.v .half:first-child { border-bottom: 2px solid #b7ac93; }
/* The pips, as geometry. See `halfHTML` for why they are not boxes any more:
   short version, a rounded box can only be rasterised on whole pixels and the
   Fives board scales itself to fit, so every pip size on it was fractional and
   came out as an egg. A circle in an SVG stays a circle at any scale.

   Sized by the half it sits in rather than by a pixel count per context, which
   is what keeps it round everywhere and also what stops the same bone looking
   like a different bone at a different size. */
.pips {
  position: absolute; inset: 0; width: 100%; height: 100%;
  display: block; fill: var(--ink); pointer-events: none;
}
/* ---------- a pile of bones, face down ---------- §1N.1, §1M.7

   Mike, 22 Aug: *"have it so it's a 'pile' of dominoes, where you see the faint
   edge of the ones the highest one is on top of — the stack shows the actual
   number of dominoes but is almost the footprint of only one domino."*

   Which is better than the multiplier that was proposed, and worth saying why:
   a back with **x7** written on it is a label, and a pile of seven with six
   edges showing is the thing itself. It counts without anything being read,
   and it is what a hand laid face down on a table actually looks like.

   The step is small on purpose — seven bones come to one bone plus 24px rather
   than to seven bones — so the pile grows in *depth* and the seat does not
   change width as the hand goes down. The count is the pile: the renderer
   draws one back per bone held, so nothing here is a constant that could
   disagree with the hand.

   `--pile-bone` is whatever a back is in this context; the default is the
   `.small` size directly below, and 42 overrides it with its own seat bone.
   One bone has no edges and is simply a back, which is the case to check
   first. */
/* `.hand-fan.pile` and `--pile-bone` stood here — the pile as three seats'
   worth of hand-tuned numbers. Both are gone into `.seat-unit`, where the
   step is the only number and the box is derived from it. BACKLOG 1R.8. */
.domino.small.h { width: 56px; height: 28px; }
.domino.small.v { width: 28px; height: 56px; }
.domino.small .half { width: 28px; height: 28px; }
/* Backs: a cream tile with a mark. Only ever seen by the player who picked it. */
.domino.back { background: #f2ece0; }
/* The far seats are where your back design actually gets seen — give it room. */
.domino.back .half { background: transparent; border: none !important; }
.domino.back .back-art {
  position: absolute; inset: 8%; width: 84%; height: 84%;
  pointer-events: none;
}
/* A flag is printed across the back, not floated in the middle of it, so the
   wide marks get almost the whole tile and a hairline to sit them on. */
.domino.back .back-art.wide {
  inset: 6% 4%; width: 92%; height: 88%;
  filter: drop-shadow(0 0 0.6px rgba(0,0,0,.45));
}

/* the picker in Profile */
.back-picker {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; margin: 6px 0 14px;
}
.back-opt {
  background: rgba(0,0,0,.3); border: 2px solid transparent; border-radius: 11px;
  padding: 10px 6px 8px; text-align: center; color: var(--cream);
  font-family: inherit; min-width: 0;
}
.back-opt.on { border-color: var(--pill-hi); background: rgba(224,170,62,.16); }
/* The placeholder. It holds its square in the grid and says why it is empty,
   which is the whole job — so it is drawn as an outline of a domino rather
   than as a back you could pick by mistake. */
.back-opt.back-empty { border-style: dashed; border-color: rgba(245,239,224,.22); }
.back-opt.back-empty .back-name { opacity: .62; }
.domino.back.back-blank {
  background: rgba(0,0,0,.16);
  box-shadow: inset 0 0 0 1px rgba(245,239,224,.16);
}
.domino.back.back-blank .half { background: none; box-shadow: none; }
.back-preview { display: block; margin-bottom: 7px; }
.back-preview .domino { position: relative; }
.back-name { display: block; font-size: 12px; font-weight: bold; line-height: 1.25; }
.domino.selectable { cursor: pointer; }
.domino.selectable:hover { transform: translateY(-4px); }
.domino.dim { opacity: .38; }
.domino.picked { outline: 3px solid var(--gold); transform: translateY(-6px); }
/* ---------- The rings: one mechanism, three meanings ----------
   Gold and solid says count. Red and dashed says trump. A bone that is both
   gets a red dashed ring over a solid gold one, so gold shows through the gaps
   and it reads as alternating without needing a second geometry.

   Line style carries the meaning; colour only reinforces it. Red and gold are
   the one pair that must never be relied on alone — red-green deficiency
   collapses them toward the same muddy yellow-brown, and that is roughly 8% of
   men, which is most of the people who play this game. Solid versus dashed
   survives that completely and costs nothing.

   Both colours are literals, not `var(--gold)` / `var(--red)`, because the
   themes repaint those — one room's "gold" is a deep navy, another's is a
   maroon — and a hint that changes colour by room is not a hint. The bone face
   is `var(--cream)` in every theme, so these two always sit on ivory.

   A pseudo-element rather than the box-shadow this used to be, for two
   reasons: a box-shadow cannot be dashed, and the shadow ring was fighting
   both the bone's own drop shadow and `body.hand-lit`, which quietly erased
   the count hint whenever the hand was lit. Outline is not used either — that
   channel already belongs to `.picked` and to big print's legal-play ring. */
.domino.ring-count::after,
.domino.ring-trump::after,
.domino.ring-both::after,
.domino.ring-both::before {
  content: ''; position: absolute; inset: -4px;
  border-radius: 8px; pointer-events: none;
}
/* 4px, from 3 — *"I need the border for the count and the trump to be
   thicker so that it's easy to identify what is what"* (§1AC.11, 2 Sep).
   The inset moves out with it so the ring still sits on the bone's edge
   rather than a pixel inside it. Big print takes 5 below. */
.domino.ring-count::after, .domino.ring-spinner::after { border: 4px solid rgba(224,170,62,.9); }
.domino.ring-trump::after { border: 4px dashed rgba(192,57,43,1); }
.domino.ring-both::before { border: 3px solid rgba(224,170,62,.85); }
.domino.ring-both::after  { border: 3px dashed rgba(192,57,43,.95); }

/* (A key for the rings — `.hand-key` — lived here for v1.80.0 and was
   removed in v1.82.0 at Mike's word: "we had decided not to do that."
   §1AC.22, §1AC.29.) */

/* ---------- the info strip: everything you glance at, by your hand ----------
   One line across the top of the dark area: who you are on the left, the game
   / hand / bid score to the right of it. Bones sit under that line, scorepad
   below them — the same order you'd find on a real table. */
.dock-head {
  display: flex; align-items: center; gap: 10px; padding: 6px 8px 8px;
  border-bottom: 1px solid rgba(255,255,255,.1); margin-bottom: 9px;
}
.table-info {
  flex: 1 1 auto; min-width: 0;
  display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center;
  gap: 8px; color: var(--cream);
}
.ti-item {
  background: rgba(0,0,0,.4); border-radius: 20px; padding: 6px 14px;
  font-size: 15px; white-space: nowrap;
}
.ti-item b { color: var(--pill-hi); font-size: 17px; }
.ti-item small { opacity: .6; font-size: 12px; margin-left: 3px; }
.ti-lbl {
  text-transform: uppercase; font-size: 10.5px; letter-spacing: 1px;
  opacity: .6; margin-right: 6px;
}
.ti-bid { background: rgba(224,170,62,.18); border: 1px solid rgba(224,170,62,.4); }
/* The bid sits with the shaker and down badges now, on the row above the
   bones. `.dock-bid` is the slot it lives in; empty until there is a bid to
   show, and it takes no room when it is. */
/* ---- everything in a dock head row is one chip ----

   The row carries your name plate, the GAME and BOARD chips and, on Fives, the
   Board view switch. Measured at 1913 on 27 August, they were **55, 33, 35 and
   44 pixels tall, sitting at four different tops, in two type sizes** — asked
   about with some exasperation: *"they're all different heights. Would like
   them all to be the exact same heights... And I would like all their text to
   be the same font size. Like, everything is just janky because everything is
   different sizes. Why is it like this? And how many times do I need to go
   over that?"*

   **Three times, and the third is this one, because the first two fixed
   individual chips.** v1.76.0's attempt is the instructive failure: it set
   `min-height` on `.dock-bid`, which is the *container* — and the container
   dutifully became 55px while `.ti-item` inside it stayed 35, so the player
   still saw a 55px plate beside a 35px pill. **Size the thing that is painted,
   and verify by measuring the element that carries the background.**

   So the rule is about the ROW, and every chip in it reads the same two
   numbers. A fifth chip added later cannot become a fifth size, which is the
   only way this stops recurring. BACKLOG 1V.2, 1W.4. */
:root { --dock-chip-h: 44px; --dock-chip-text: 16px; }
/* The container still has to be as tall as what it holds, or the row's items
   align to a box shorter than its own contents. */
.dock-bid { flex: 0 0 auto; display: flex; align-items: center; }
.dock-bid:empty { display: none; }
/* The score band under the hand. On a laptop it is a plain strip; on a phone
   the game score is a button that swaps the band for the scorepad. */
.dock-info { display: flex; justify-content: center; margin-top: 8px; }
.ti-game {
  font: inherit; color: inherit; border: none; cursor: pointer;
  display: inline-flex; align-items: center;
}
/* Why the score is wrapped in a span of its own, and why removing it as
   redundant markup would put the bug back.

   The Game score and the Hand score are written identically — label, "Us",
   the number, "· Them", the number — and on screen the Game one read "Us0 ·
   Them0" while the Hand one read "Us 0 · Them 6". Reported 16 Aug as the two
   being spaced differently, which they were.

   The Game score is a button, because on a phone it swaps the band for the
   scorepad; the Hand score is a plain span. A button here is `inline-flex`,
   and **flex layout throws away the whitespace-only text nodes between
   elements** — so the spaces around `<b>` survived in the span and vanished
   in the button. Nothing was wrong with either rule; the two lines were in
   different layout modes.

   Wrapping the score run in one span makes it a single flex item with
   ordinary inline layout inside it, so the spaces come back and the two pills
   are the same by construction rather than by a hand-tuned `gap` that would
   drift the moment either line changed. */
.ti-score { white-space: nowrap; }
.ti-swap { display: none; }
/* Only the phone layout hides the score behind the pad, so only the phone
   layout needs a way back. */
.pad-close { display: none; }

/* ---------- the table's name, and what a wide screen can do without ----------

   All of this is Mike's, 15 August, off a screenshot of a 1900px table, and
   the theme running through it is that a laptop was being given a phone's
   furniture. Four things were saying whose turn it was — the travelling token,
   the lit plate, a strip of four names across the top, and the words "Your
   play." — on the one screen with enough felt to make the token obvious. */

/* The name in the bar, not over the table. Phone keeps its own line under the
   bar; see `.table-called-top` in `render42`, which writes both. */
.table-called-top { display: none; }
@media (min-width: 900px) {
  .table-called-top {
    display: inline-block; color: var(--pill-hi); font-size: 19px;
    text-transform: uppercase; letter-spacing: 1.5px; font-weight: bold;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 22ch;
  }
  .table-called-top:empty { display: none; }
  .table-called { display: none; }

  /* And the words, but only the ones that are merely repeating the token.
     `own-turn` is set only when the table is waiting on *you* — to bid, to
     name trumps, or to play — so "Amos is thinking…" and everything at the end
     of a hand still speak up. When it is your turn there is already a token
     beside your seat, your own plate is lit and breathing, and in the bidding
     and trump cases a panel is open in the middle of the screen with your name
     on it. A line of text at the top saying so as well is the fourth thing.
     Widened from play-only to any turn on 15 Aug: "Your bid." was still
     appearing above an open bid panel. */
  .status-line.own-turn .status-msg { visibility: hidden; }

  /* Bigger targets on the one screen with room for them. Asked for twice —
     9px/16px was still small against a 1900px table. */
  .exit-btn { padding: 12px 26px; font-size: 18px; border-radius: 22px; }

  /* Your partner reads as one row: who they are, then what they are holding.
     The unit is that row now — `.seat-unit` puts the identity and the pile
     side by side on a desktop of its own accord, so the seat's only job here
     is to hold one and centre it. */
  /* The reserved fan width stood here — "seven backs at 68px with 4px between
     them", with a note saying that if either number moved this had to move
     with it. Neither number exists any more: the pile reserves its own box
     from one step, and the two cannot drift apart. BACKLOG 1R.1, 1R.8. */
}
.ti-trump {
  /* No left margin on a phone: the chip is a flex row with its own gap there,
     and the two together read as a shove. Kept for the desktop, where the chip
     is inline and there is nothing else separating them. §1F.2. */
  margin-left: 8px; background: var(--gold); color: var(--btn-text);
  border-radius: 12px; padding: 2px 10px; font-weight: bold; font-size: inherit;
}

/* ---------- trick record: what's been played ---------- */
/* The record sits under the left-hand player's bones, in the corner of the
   table where you'd actually stack your won tricks.

   This used to go on: *"the 300px reservation is that player's header plus a
   full seven-bone column — the record never climbs into it, it scrolls
   instead."* **There is no 300px reservation, and there never was one in a
   shipped release.** It was `.seats-mid { min-height: 300px }`, and
   `.seats-mid { min-height: 0 }` beat it at every width from the day the
   felt went back to being only the table. The sentence described an
   intention, two thousand lines away from the rule it was describing, where
   nothing could contradict it — which is the second half of CLAUDE.md's
   comment rule, and why it survived being read for weeks.

   What holds the record off the seat today is its own box, not a reservation
   above it. Whether the reservation was wanted is a real question and it is
   filed, not answered here: §4.22, and §8J for the pass that found it. */
/* Three declarations, and it used to be eleven. The other eight — `left`,
   `bottom`, `top`, the background, the border, the radius, the padding and
   `max-width: 244px` — were all restated at `.trick-record` further down,
   with no media query on either, so the ones here had never applied in any
   release. Found 19 Sep by the slice-two enumeration (§8J), not by reading:
   the two blocks are 2,500 lines apart and each reads perfectly sensibly on
   its own. Removing them changed nothing anywhere, which was the point and
   was measured rather than assumed. §4.22 has the other fifty-four. */
.trick-record {
  position: absolute; z-index: 3;
  backdrop-filter: blur(2px);
}
/* both side seats ride up so the corner is free and the table stays symmetric */
.seat-w, .seat-e { justify-content: flex-start; padding-top: 10px; }
/* phone: a chip you tap, so the record never sits on top of the far seat */
.trick-record.tr-chip { top: 8px; bottom: auto; max-height: none; padding: 0; background: none; border: none; backdrop-filter: none; }
/* Drawn as something you press. It was a dark pill with gold type, which is
   how this app draws *headings* — so the one control on the band that opens
   something read as a label for the bones underneath it. Reported 16 Aug as
   wanting to be "a more obvious button". The gold-tinted fill and full-strength
   gold border are the same treatment `.ti-bid` uses for a live thing, so this
   is the app being consistent with itself rather than a new style. */
.tr-chip-btn {
  display: flex; align-items: center; gap: 7px;
  background: rgba(224,170,62,.16); border: 1px solid var(--pill-hi);
  border-radius: 20px; padding: 6px 13px; color: var(--cream);
  font-family: inherit;
}
/* The label is small caps and quiet; the count is the number somebody is
   actually reading, so it is bigger than the label rather than a dimmed
   afterthought inside it. `.tr-head` carries a bottom margin for its use in
   the full record panel, which in a centred flex row here just pushes it off
   centre. */
.tr-chip-btn .tr-head { margin-bottom: 0; }
.tr-chip-count { font-size: 15px; font-weight: bold; color: var(--cream); }
.tr-chip-arrow { color: var(--pill-hi); font-size: 12px; }
.tr-panel { max-height: 60vh; overflow-y: auto; margin: 4px 0 14px; }
/* The phone's What's-been-played sheet: bigger bones, and seven tricks
   without a scroll. Two tricks were drawn at the default 56px with two-thirds
   of every row empty to the right of them (§1AC.4), so the bones grow
   sideways into that room and the row does not get taller for it — a row's
   height is its tallest child, the count bone stood on end, which is exactly
   `--sheet-bone`. Then the arithmetic that has to hold: seven rows at that
   height plus six gaps must fit under the panel's ceiling, and the panel
   itself is capped at 86vh with a heading and a button inside it, so this
   box gets 86vh less the 150px those take — and a row is its bone plus 6px
   of its own padding, which the first arithmetic left out and `pagecheck`
   found as 10px of scroll at 360 with Big print on. 360x780 ordinary: 61px
   bones, 499 of 521. 360x780 Big print: 63px bones, 513 of 521. 430x932
   Big print: 75px bones, 597 of 651. `pagecheck` measures both widths in
   both print sizes with seven tricks on the sheet. Big print gets its own
   value and the two are read together — the §1 lesson. */
@media (max-width: 899px) {
  .overlay .tr-panel { max-height: calc(86vh - 150px); --sheet-bone: clamp(56px, 17vw, 76px); }
  html.big-print .overlay .tr-panel { --sheet-bone: clamp(58px, 17.5vw, 82px); }
  .overlay .tr-panel .domino.small.h { width: var(--sheet-bone); height: calc(var(--sheet-bone) / 2); }
  .overlay .tr-panel .domino.small.v { width: calc(var(--sheet-bone) / 2); height: var(--sheet-bone); }
  .overlay .tr-panel .domino.small .half { width: calc(var(--sheet-bone) / 2); height: calc(var(--sheet-bone) / 2); }
}
.tr-head {
  font-size: 12px; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--pill-hi); margin-bottom: 7px; font-weight: bold;
}
.tr-head small { opacity: .6; font-weight: normal; letter-spacing: 0; margin-left: 5px; }
.tr-note { font-size: 11px; opacity: .75; font-style: italic; margin-bottom: 6px; color: var(--cream); }
.tr-rows { display: flex; flex-direction: column; gap: 5px; }
.tr-row {
  display: flex; align-items: center; gap: 6px;
  padding: 3px 5px; border-radius: 7px; border-left: 3px solid transparent;
}
/* Who took the trick. Reported 17 Aug as not obviously different — gold at 13%
   on a dark panel is close to nothing, so the record read as "some rows look
   slightly off" rather than as a fact about the hand. Both tints are up, and
   the left border keeps doing its share because it is the part that survives
   if the fills are ever restyled. The words in `.hr-key` are what makes this
   safe rather than the colour; see §3.4. */
.tr-row.ours { background: rgba(224,170,62,.24); border-left-color: var(--pill-hi); }
.tr-row.theirs { background: rgba(255,255,255,.09); border-left-color: rgba(255,255,255,.36); }
.tr-num { font-size: 11px; opacity: .55; width: 11px; flex: none; color: var(--cream); }
.tr-bones { display: flex; align-items: center; gap: 3px; }
.tr-pts { font-size: 12px; color: var(--pill-hi); font-weight: bold; margin-left: auto; }
/* a count bone gets turned sideways so it sticks out of the row */
.tr-bone { display: inline-flex; align-items: center; }

/* Two flat bones to a column, stacked. A column holding only one hangs from
   the bottom of the row rather than sitting in the middle of a space meant for
   two — which is what a lone heavy bone looks like when somebody drops it onto
   the end of a pile they have already squared up. */
.tr-stack { display: flex; flex-direction: column; justify-content: flex-start; }
.tr-stack.tr-hang { justify-content: flex-end; }

/* The record scrolls, and for a long time nothing on the screen said so — the
   scrollbar was turned off on purpose to keep the box clean, which it did, at
   the cost of hiding four of the seven tricks from anybody who didn't think to
   try dragging it. A thin one in the table's own gold is quiet enough to leave
   on and is the only thing that says "there is more of this".

   Sideways stays hidden: the rows are built to fit the width, so a horizontal
   bar would only ever mean something is wrong. */
.trick-record, .trick-record .tr-rows, .tr-panel {
  overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--pill-hi, #e0aa3e) rgba(0, 0, 0, .25);
}
.trick-record::-webkit-scrollbar, .trick-record .tr-rows::-webkit-scrollbar,
.tr-panel::-webkit-scrollbar { width: 7px; height: 0; }
.trick-record::-webkit-scrollbar-track, .trick-record .tr-rows::-webkit-scrollbar-track,
.tr-panel::-webkit-scrollbar-track {
  background: rgba(0, 0, 0, .25); border-radius: 4px;
}
.trick-record::-webkit-scrollbar-thumb, .trick-record .tr-rows::-webkit-scrollbar-thumb,
.tr-panel::-webkit-scrollbar-thumb {
  background: var(--pill-hi, #e0aa3e); opacity: .8; border-radius: 4px;
}

/* ---------- the hand, read afterwards ----------
   Pinned over the open left half of the felt while the score panel is up. The
   panel is centred, so this stops at the middle and the two never fight for
   the same pixels. Top clears the seat across from you. */
.hand-review {
  /* Placed by measurement against the felt — see showHandReview. */
  position: fixed; left: 0; top: 0; width: 0; height: 0;
  display: none; overflow: hidden;
  /* Above the score panel's scrim, not under it. The scrim is a black wash
     over everything behind the panel — which is exactly right for the table
     and exactly wrong for this, the one thing on screen you are meant to be
     reading at that moment. Under it the bones came out grey.

     `pointer-events: none` is what makes sitting above the scrim safe: taps go
     straight through to the panel and to Shake 'em!. And it stops at the
     middle of the table, so it never covers the panel it sits beside. */
  /* It takes its own pointer events, so it can be scrolled. It used to refuse
     them — the note here said that was what made sitting above the scrim safe,
     taps passing through to the panel and to Shake 'em! — and the cost was
     that the panel could not be scrolled at all. It draws a gold scrollbar and
     ignored the wheel, which is worse than not offering one. Reported 15 Aug.

     Safe because of where it is rather than because it is inert: it is pinned
     to the left third of the felt and the result panel is centred, so it never
     covers the panel or its buttons. What it now intercepts is taps on the
     empty left third, which went to a scrim that does nothing. */
  z-index: 60; pointer-events: auto;
  /* Drawn as a panel lying on the table rather than as loose rows floating
     over it. A hand can end with bones still in people's hands — a set stops
     it dead — so the seats are not always empty underneath, and rows tangled
     up with somebody's face-down bones read as a rendering fault. A surface
     under it settles that, and gives the bones something to sit on other than
     green. */
  background: rgba(12, 27, 20, .94);
  border: 1px solid rgba(224, 170, 62, .22);
  border-radius: 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .45);
  /* No backdrop-filter here, however well it suits a panel over a table. It
     promotes this to its own composited layer, and the browser then rasterises
     everything inside at its unscaled size and stretches the result — so the
     bones came out soft and washed grey against the dark panel, which read as
     the record being greyed out rather than enlarged. The whole point of this
     panel is that the bones are easier to see. */
}
.hand-review.on { display: block; }
/* Grows down from the top corner. Anchored at the bottom it grew upward, so
   whatever the scale got slightly wrong came off the first trick — the one
   nobody can reconstruct from what's left. Overflow scrolls rather than clips,
   with the same gold bar as the corner record, so a hand that doesn't quite
   fit is still all there. */
.hand-review {
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--pill-hi, #e0aa3e) rgba(0, 0, 0, .25);
}
.hand-review::-webkit-scrollbar { width: 7px; }
.hand-review::-webkit-scrollbar-track { background: rgba(0,0,0,.25); border-radius: 4px; }
.hand-review::-webkit-scrollbar-thumb { background: var(--pill-hi, #e0aa3e); border-radius: 4px; }
.hr-inner {
  transform-origin: top left;
  position: absolute; left: 10px; top: 8px;
  transition: transform .3s ease;
}
.hr-head {
  font-size: 12px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--heading, #e0aa3e); opacity: .85; margin-bottom: 5px;
}
/* Inside the review the record is a panel in its own right, not a box tucked
   into a corner, so it drops the corner box's chrome and lets the scaling do
   the work. */
.hand-review .tr-rows { overflow: visible; }
/* The key under the heading. Small, quiet, and read once. */
/* The key sits under the rows now, because it travels with them — see
   `trickKeyHTML`. It used to be built above them by the corner record alone,
   which is why the phone sheet had none (§1G.4). */
.hr-key {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--cream); opacity: .75; margin: 6px 0 0;
}
/* In the corner record, height is the binding constraint: §3.19a got seven
   tricks to fit with three pixels to spare, and moving the key in cost 17. It
   is one line either way, so it gives them back by being a tighter one. */
.hand-review .hr-key { font-size: 10.5px; gap: 4px; margin: 2px 0 0; }
.hand-review .hr-sw { width: 13px; height: 8px; }
/* And the heading gives up a few of its own. Seven tricks are the reason this
   panel exists; the words above them are not. */
.hand-review .hr-head { font-size: 11px; margin-bottom: 1px; }
/* Three pixels off the corner record's bone, which sits on its floor of 56 at
   the widths that bind. Seven rows of it filled 418 of the 422 the panel has,
   leaving nothing for the heading and the key — and the key only moved in here
   on 20 Aug (§1G.4), so this is paying for that. Three pixels a bone is
   twenty-one down the column, which covers it.

   The phone sheet is untouched: it has its own size and no height to fight
   over. §3.19a, §1G.3. */
/* Sized explicitly, because `--rec-bone` never reached here: those rules are
   written against `.trick-record`, and the review panel's inner element is
   `.hr-inner`. Both records have been falling through to the default
   `.domino.small` all along — which is also why setting the variable did
   nothing when it was tried. */
.hand-review { --review-bone: 53px; }
.hand-review .domino.small.h { width: var(--review-bone); height: calc(var(--review-bone) / 2); }
.hand-review .domino.small.v { width: calc(var(--review-bone) / 2); height: var(--review-bone); }
.hand-review .domino.small .half { width: calc(var(--review-bone) / 2); height: calc(var(--review-bone) / 2); }
.hr-sw {
  width: 16px; height: 10px; border-radius: 3px; display: inline-block;
  border-left: 3px solid transparent;
}
.hr-sw-them { margin-left: 10px; }
.hr-sw-us { background: rgba(224,170,62,.24); border-left-color: var(--pill-hi); }
.hr-sw-them { background: rgba(255,255,255,.09); border-left-color: rgba(255,255,255,.36); }
/* One record at a time. While *The hand just played* is up it is showing the
   same seven tricks the corner box is showing, larger — and the corner box was
   sitting underneath it saying them again. Reported 17 Aug. Scoped to the
   width where the review exists at all; below 900px it is `display: none` and
   the corner record is the only one there is. */
@media (min-width: 900px) {
  body.reviewing .trick-record { display: none !important; }

/* ---- the end of a hand is four things ----

   *"the only things that should show up is the header with the leave button,
   Texas bones, and table rules, the panel that's the end of the hand, and then
   the hand that was just played with the dominoes bigger so that I don't have
   to scroll... And then, of course, the scorepad."*

   His reasoning is right and worth keeping: **the result panel already carries
   everything the table was still showing** — who bid what, what the bidders
   took, what the defenders took, the running score. The seats behind it repeat
   nothing anybody needs at that moment, and every one of them is height the
   played hand could be using.

   `body.reviewing` is already set while the review is up and already hides the
   corner record, so the rest of the table joins it here. Two things it buys
   beyond tidiness: the felt is empty, so the review is laid over nothing; and
   the dock collapses to its scorepad, which hands the felt real height — which
   is what makes the bones bigger without any scaling work. BACKLOG 1V.1. */
body.reviewing .seats-mid,
body.reviewing .seat,
body.reviewing .fives-seats,
body.reviewing #mn-seats,
body.reviewing .status-stack,
body.reviewing .me-plate,
body.reviewing .dock-head,
body.reviewing .my-hand,
body.reviewing .dock-info,
body.reviewing .board-wrap,
body.reviewing .dock-side { display: none !important; }
/* Seven tricks in two columns, not one.

   The review scales its contents to fit its box, and a single column of seven
   is tall and narrow — so it was fitting to the height and throwing the width
   away, exactly the fault the Fives board has (1W.1). The bones came out at
   27x55 in a box 623 wide.

   Four tricks and three is half the height for twice the width, so the same
   box scales it far larger. The complaint was that the played hand is too
   small to read; this is where the size comes from, rather than from any
   scaling change. BACKLOG 1V.1. */
body.reviewing #hr-inner .tr-rows {
  display: grid; grid-template-columns: 1fr 1fr;
  grid-auto-flow: column; grid-template-rows: repeat(4, auto);
  align-content: start;
}
/* Moon's review is one column. *"In Moon I would like the after-hand played
   pane to be one column"* (4 Sep). Moon's felt has the whole left side free
   at the end of a hand — its record already runs the full column there —
   and three bones a trick is narrow enough that seven rows read as a list.
   The scaling pass in `showHandReview` fits the column to the felt's height
   as it does the grid. */
body.reviewing .hand-review.hr-moon #hr-inner .tr-rows {
  grid-template-columns: 1fr; grid-template-rows: repeat(7, auto);
}
/* The scorepad stays, and takes the middle now that nothing is beside it. */
body.reviewing .dock-pad { display: flex !important; justify-content: center; }
/* Moon's pad, which has no named area in Moon's dock grid and was being
   auto-placed: with the record hidden for the review the first free cell
   was the 248px side column, the flex row inside shrank the pad to its
   content — 88px, names spilling out of it — and it sat at the far left,
   *"really weird scorepad size and it moved to the left"* (4 Sep). It spans
   the dock now and keeps a width three names fit in. */
body.reviewing #screen-moon .dock-pad {
  grid-column: 1 / -1; grid-row: auto; justify-self: stretch; width: auto;
  justify-content: center; padding-left: 0;
}
body.reviewing #screen-moon .score-pad.mn-pad { width: min(100%, 380px); }
/* The dock keeps only what is left in it, so the felt gets the difference. */
body.reviewing .my-dock { min-height: 0; padding-top: 8px; }
}
@media (max-width: 899px) { .hand-review { display: none !important; }}
@media (prefers-reduced-motion: reduce) { .hr-inner { transition: none; }}

/* ---------- 42 table ---------- */
.table-42 { flex: 1; display: flex; flex-direction: column; position: relative; overflow-x: hidden; }

/* ---------- handwritten scorepads, sat in front of you ---------- */
.my-dock { display: flex; flex-direction: column; overflow: hidden; }
.dock-name { flex: none; }
/* `min-width: 0` because this is a grid item, and a grid item's floor is the
   intrinsic width of what is inside it unless it is told otherwise. The pad
   inside is a fixed 5-marks-across drawing — `xMarksSVG` builds a constant
   width whatever the score, so the pad is exactly as wide empty as it is at
   150 — and it simply does not fit the column it is given.

   Measured on Fives, 21 Aug, at the start of a hand: the pad overran its own
   column by 50px at 1900 and 254px at 1400, and at 1200 and below it ran off
   the right of the screen by 172px and 251px. It was reported as the pad
   sitting on top of the bones, which is what the first 50px of it does.
   BACKLOG 1L.1. */
/* Measured 22 Aug on 42, Fives and Moon at 1917 and 1400: **no geometric
   overlap at all** between the pad and the hand — 0px in all six. What the
   screenshots show is the pad sitting immediately against the bones with a
   rotation and a drop shadow reaching over them, on the brightest object on a
   dark table. So this is clearance and weight rather than a collision, and it
   is treated as such. BACKLOG 1N.8, 1P.7. */
.dock-pad { display: flex; justify-content: center; margin-top: 10px; min-width: 0; padding-left: 20px; padding-bottom: 16px; }
.dock-pad .score-pad { max-width: 100%; box-sizing: border-box; }
.fives-pad .pad-col, .fives-pad .pad-cols { min-width: 0; }
.score-pad {
  background: #fdfaf0; color: #3a3428; border-radius: 3px;
  /* A note on the table, not a lamp on it. The shadow was reaching a good way
     over whatever sat beside it. */
  padding: 8px 12px 7px; box-shadow: 0 2px 6px rgba(0,0,0,.32);
  transform: rotate(-1.2deg);
  background-image: linear-gradient(#fdfaf0 95%, rgba(120,140,180,.16) 95%);
  background-size: 100% 18px;
  flex: none;
}
.score-pad .pad-head { display: none; }
.score-pad .pad-row { display: flex; align-items: center; gap: 9px; }
.score-pad .pad-lbl {
  font-family: 'Segoe Script', 'Bradley Hand', cursive; font-size: 13px;
  color: #2f4d86; font-weight: bold; width: 44px; flex: none; letter-spacing: .5px;
}
.score-pad .pad-rule { height: 1px; background: rgba(60,50,35,.22); margin: 3px 0; }
/* The mark drawings carry a viewBox, so they scale rather than clip once they
   are allowed to shrink — `height: auto` matters as much as the max-width, or
   they keep the height off their width attribute and go out of shape as the
   column narrows. See `.dock-pad` above for what this is guarding against. */
.score-pad svg { display: block; max-width: 100%; height: auto; }
/* A width of its own on a desktop. Between 900 and 1549 wide the 42 pad
   drew as a 55px slip reading US and THEM with both mark drawings at 0x0 —
   `max-width: 100%` against a `max-content`-sized pad whose only sized
   content was the two words, so the percentage had nothing to resolve
   against and the SVG went to nothing. It had been that way at least since
   v1.78.0 and nobody had measured that tier; Mike plays at 1913, where the
   four-column dock's `zoom: 2` drew it at 182px, and saw it broken at 1254
   on 2 Sep (§1AC.23). The `90` is the drawing's own `viewBox` width, and
   the 1550+ tier's zoom doubles it exactly as before. `pagecheck` asserts
   each mark drawing is at least 60px wide at 1254 and 1366 with marks on
   the board. */
@media (min-width: 900px) {
  /* `flex: none` on both the drawing and the pad, because the width alone was
     not enough: the pad is a flex item in `.dock-pad` and the drawing a flex
     item in `.pad-row`, and both shrank — the pad to its labels' min-content
     and the drawing, inside it, to zero. Measured after the first fix as
     `width=0px` computed on the SVG with the rule applied. */
  /* The HEIGHT is pinned, not the width. The drawing's viewBox is 48 tall
     and 34, 63 or 90 wide by the marks the game is to (3, 5, 7); pinned at
     90 wide with `height: auto`, a game to 3 scaled its 34-wide drawing up
     by 2.6 and each row came out 200px tall — a pad taller than the dock,
     the bid panel scrolling above a felt that had lost the height, and a
     hand pushed 150px down its own column (Mike's desktop, 9 Sep, a game
     to 3 at an online table). At 48 tall the width is the viewBox's own, 34
     to 90, and the 1550+ tier's zoom scales both together. */
  #screen-42 .score-pad svg { height: 48px; width: auto; max-width: none; flex: none; }
  #screen-42 .dock-pad .score-pad { flex: none; }
}

/* Fives: X's on ruled paper, Us and Them side by side */
.fives-pad .pad-cols { display: flex; align-items: flex-start; gap: 12px; }
.fives-pad .pad-col { text-align: center; }
.fives-pad .pad-col-head {
  font-family: 'Segoe Script', 'Bradley Hand', cursive;
  color: #2f4d86; font-weight: bold; font-size: 15px; letter-spacing: 1px;
  border-bottom: 1.5px solid rgba(47,77,134,.5); margin-bottom: 5px; padding-bottom: 2px;
}
.fives-pad .pad-div { width: 1.5px; align-self: stretch; background: rgba(60,50,35,.3); }
.fives-pad .pad-total {
  font-family: 'Segoe Script', 'Bradley Hand', cursive;
  color: #2f4d86; font-weight: bold; font-size: 15px; margin-top: 3px;
}

/* Caps in CSS, not in the string. Mike asked for LEAVE, RULES and BACK in
   capitals; writing them that way in the markup would make the accessible
   name all-caps too, and some screen readers read that as an initialism and
   spell it out. `text-transform` gets the look and leaves the name alone.

   The arrow and the clipboard came off the strings in the same pass — at
   102x38 the arrow and the word would not sit side by side, so `← Leave` set
   on two lines and made the top bar 58px tall for no gain. One decision,
   applied to the ways out and to Back alike. BACKLOG 1K.1, 1K.16. */
/* ---- one type role for every control ----

   A button's type comes from what it IS, never from where it happens to sit.

   Audited 27 August across the app and five static pages: BACK appeared in
   **three sizes, two weights and four box sizes**, all of it in the body serif,
   while `Shake 'em!` and `Save profile` beside it were set in the brand face at
   19px/700. On the setup screen the two sat one above the other in different
   typefaces. Reported as *"the back button everywhere is now a different font,
   size, weight, etcetera. Like, things just need to match."*

   It happened because each control was styled where it was written. Four
   separate fixes is exactly how there came to be four sizes, so this is one
   declaration that every control reads, and adding a fifth control adds no
   fifth size. BACKLOG 1X.1, 1X.2. */
.big-btn, .ghost-btn, .exit-btn, .back-sticky, .rb-close, .back-btn,
.pass-btn, .undo-btn, .start-here {
  font-family: var(--brand-font);
  font-weight: bold;
}
.exit-btn, .back-sticky, .rb-close, .back-btn {
  text-transform: uppercase; letter-spacing: .6px;
}
/* BACK is a chip you can see, on the two themes Mike named — gold fill,
   dark text, same object as LEAVE. The Hall and Slate deliberately keep
   their current quiet style: "don't change hall and slate themes for now."
   Pic 8, 23 Aug. */
body:not(.theme-kc):not(.theme-slate) .rb-close,
body:not(.theme-kc):not(.theme-slate) .back-btn,
body:not(.theme-kc):not(.theme-slate) .back-sticky {
  background: var(--gold); color: var(--btn-text);
  border-color: transparent; opacity: 1;
}
/* No Table Talk box at a solo table. "You're playing the bots, so there's
   nobody to talk to" is a caption on an impossibility — Mike, 25 Aug: hide
   it completely. The corner keeps its element (pagecheck checks the chat is
   *reserved*, and the column keeps the hand centred); it simply does not
   draw until a live table marks it .live. BACKLOG 1N.12. */
.chat-corner:not(.live) { visibility: hidden; }
/* The first-visit banner — below the cards, and quiet. §1S.6.

   It began as a full-width gold slab above the cards and Mike asked for it
   moved down here and made "more elegant": an outlined pill in the brand
   serif rather than a primary button, because it is an offer, not the thing
   the screen is for. The four cards are the thing the screen is for.

   And it can be dismissed — which the first version could not. Somebody who
   has played 42 for thirty years should not have to enter a table to stop
   being asked. The × says where the hand lives afterwards, so dismissing it
   costs nothing: two flags retire the banner, `playedAny` silently on first
   sitting, `bannerHidden` deliberately. Both live in the profile, on the
   device, like everything else here. */
/* "Start here", pinned to the 42 card's top edge on a device that has never
   finished a hand (`renderHome`, §1S.6). The card is the anchor; the tag is
   a small gold banner in the seat chips' voice so it reads as a note on the
   card and not as a fifth button. */
.game-card { position: relative; }
/* `.start-tag` — the "Start here" chip on the 42 card — stood here until v1.93.0 (§1AE.59). */
/* §1AF.18, moved 9 Sep: the way back to a solo hand a refresh interrupted is
   a row in the active-tables list (`.solo-row`, drawn by `soloRowHTML`), not
   a banner. Dashed where the tables are solid, so it reads as a different
   kind of thing even before the SOLO tag is read. */
.saved-row.solo-row .saved-table { border-style: dashed; border-color: rgba(224,170,62,.55); }
.saved-row.solo-row .st-sub { white-space: normal; }

.start-here-wrap {
  display: flex; align-items: center; justify-content: center;
  gap: 6px; margin: 4px auto 14px;
}
/* Outlined in `--pill-hi`, each theme's colour for a live outlined thing (the
   bid shield, BACK on the two chip themes) — not in gold literals with
   `--gold` type, which was a gold ring round MAROON words on Northgate's wood
   and round navy ones on The Hall's parchment, because `--gold` is the button
   FILL there and only Standard's is gold. Standard looks exactly as it did
   (`--pill-hi` is its gold). Mike, 5 Sep: *"the color schemes for the other
   themes don't look good."* §1AE.36. */
.start-here {
  display: inline-block; background: none; cursor: pointer;
  border: 1px solid var(--pill-hi); border-radius: 999px;
  color: var(--pill-hi); padding: 9px 22px;
  font-size: 15.5px; letter-spacing: .02em;
}
.start-here:hover { background: rgba(255, 255, 255, .08); }
/* The Hall's `--pill-hi` is white, chosen for chips on its dark panels; on the
   parchment of its home screen white type in a white ring is nothing at all
   (seen 5 Sep). There the button is the filled chip — navy, cream type — the
   same object as its Shake 'em!. */
body.theme-kc .start-here { background: var(--gold); color: var(--btn-text); border-color: transparent; }
body.theme-kc .start-here:hover { background: var(--gold); filter: brightness(1.12); }
/* §1AF.21: the tip pill and the training banner share one row; the pill is
   the filled one. Same object as the Shake 'em! button, which is how it
   holds on all four tables — `--gold` and `--btn-text` are theme tokens. The
   promise under the pair is quiet by design: it is a fact, not a pitch. */
.tip-row { display: flex; flex-direction: column; align-items: center; margin: 14px 0 12px; }
.tip-beer { font-size: 1.35em; line-height: 1; vertical-align: -3px; margin-right: 2px; }
.tip-pill {
  display: inline-block; cursor: pointer; border: 1px solid transparent; border-radius: 999px;
  background: var(--gold); color: var(--btn-text); padding: 9px 22px;
  font-size: 15.5px; font-weight: bold; letter-spacing: .02em; font-family: inherit;
  box-shadow: 0 2px 6px rgba(0,0,0,.25);
}
.tip-pill:hover, .tip-pill:focus-visible { filter: brightness(1.08); }
.tip-promise {
  text-align: center; font-size: 12.5px; opacity: .78; margin: 8px 12px 0;
  color: var(--table-text, var(--cream));
}
.pad-hint { border-top: 1px solid rgba(255,255,255,.12); padding-top: 8px; margin-top: 8px; }
/* §1AF.36: the auction line under the card's summary — who shook, the bids
   in order, who led. Smaller than the summary so it reads as the record
   and stays a line ("keep it tidy and small", Mike, 9 Sep). */
.panel .sub.hr-auction { font-size: 12.5px; opacity: .8; line-height: 1.4; margin-bottom: 8px; }   /* `.panel .sub` sets 15px; this must outrank it */
/* §1AF.33: the note and the Save on the end-of-hand card, one line: the
   button a third of it, the note two thirds, both the button's height
   (Mike, 9 Sep, after two tries — a row that squeezed the note to fifteen
   pixels, then a column he found too tall). The squeeze came from
   .ghost-btn's width:100% in a flex row, overridden here. */
.save-hand { display: flex; gap: 8px; align-items: stretch; margin: 10px 0 4px; }
.save-hand .save-note {
  flex: 2 1 0; min-width: 0; box-sizing: border-box; font: inherit; font-size: 15px; padding: 10px 12px;
  border-radius: 10px; border: 1px solid rgba(255,255,255,.22); background: rgba(0,0,0,.28); color: var(--cream);
}
.save-hand .ghost-btn { flex: 1 1 0; width: auto; margin-top: 0; padding: 14px 8px; white-space: nowrap; }
.save-hand.key-add { align-items: center; margin: 4px 0 10px; }
.save-hand.key-add .save-note { flex: 1 1 0; font-family: 'Courier New', monospace; font-size: 13.5px; }
.save-hand.key-add .ghost-btn { flex: 0 0 auto; padding: 10px 14px; }   /* as wide as its word */
.saved-hands { margin-top: 14px; text-align: left; }
.saved-hands summary { cursor: pointer; font-weight: bold; opacity: .85; padding: 6px 0; }
.saved-hands .saved-list { margin-top: 8px; }
.start-here-hide {
  background: none; border: none; cursor: pointer; color: var(--table-text, var(--cream));
  opacity: .4; font-size: 17px; line-height: 1; padding: 4px 6px;
  min-width: 24px; min-height: 24px; font-family: inherit;
}
.start-here-hide:hover, .start-here-hide:focus-visible { opacity: .9; }
/* 10-css — the setup screen's group headings span the grid. */
.rule-head {
  grid-column: 1 / -1; font-size: 13px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--heading); opacity: .85;
  font-weight: bold; margin: 14px 0 2px;
}
.rules-btn { border-color: rgba(224,170,62,.6); }

/* ---------- your name row, in the dock on a desktop ----------

   Mike, 23 Aug: the icon-and-name plate moves off the felt and into the dock
   beside the bid chip, desktop only — on a phone the felt plate stays, where
   it has been since 1F.1. Two slots, one renderer; this is only the switch
   that decides which one shows. */
.dock-plate { display: none; }
/* Fives' and Moon's side columns earn less than 42's — no trick record lives
   in them any more (Moon's is fixed to the viewport edge, Fives never had
   one), so at desktop widths they hand some width back to the middle, where
   the hand's wider gaps had squeezed the scorepad to 98px and tripped
   `pagecheck`'s is-the-pad-real floor. The two sides stay equal, which is the
   mirrored-pair symmetry the same suite holds. */
@media (min-width: 900px) {
  #screen-fives .my-dock, #screen-moon .my-dock {
    --dock-side: clamp(150px, 13vw, 250px);
  }
  /* Moon's column was widened to 42's here for one build on 4 Sep, so the
     record could live in it: at 1254 that starved the middle, the hand
     wrapped and the pad was crushed to 81px (`pagecheck`). Moon's pad is
     in the middle column where 42's is not, and its middle has less to
     spare. So below 1550 Moon's record stays on its felt rail, and it takes
     the dock from 1550, where the three-track dock gives the column room. */
}
@media (min-width: 900px) {
  #screen-42 .me-plate { display: none; }
  #screen-42 .dock-head, #screen-fives .dock-head {
    display: flex; align-items: center; justify-content: center;
    gap: 16px; flex-wrap: wrap;
  }

  /* Fives' plate is in the dock on a desktop, beside the Board view switch.

     It was moved to the felt on 26 Aug because the two collided, and that was
     the wrong half of the problem to move: *"my name chip should not be on the
     table in desktop mode."* The switch is parked in this row by four separate
     asks and an explicit approval, so **the switch owns the row and the plate
     makes way**, not the other way about.

     The row is a grid rather than a centred flex for that reason. The switch is
     absolutely positioned in the left column and reaches across into this one,
     so a flex row cannot see it to leave room; a fixed first track can. The
     track is the switch's own width and the plate takes the middle. */
  #screen-42 .dock-plate, #screen-fives .dock-plate {
    display: flex; align-items: center; justify-content: center;
  }
  #screen-fives .me-plate { display: none; }
  #screen-fives .dock-head {
    display: grid; align-items: center;
    grid-template-columns: auto auto auto;
    /* Centred on the column, like the hand under it — it started at the
       column's left edge, and at 1963 read as a row centred on nothing
       (§1AC.33, 3 Sep). */
    justify-content: center; gap: 16px;
  }
  /* No reserved track any more: the switch is in the row, so the row places
     it. The version before this one left track one empty for a control that
     was not a child of the row, and the plate was auto-placed into it and
     landed on the switch. */
}
@media (max-width: 480px) {
  .score-pad { transform: rotate(-2.2deg) scale(.8); transform-origin: center top; }}
/* No `min-height` here, and that is not an omission. It said 300px until
   19 Sep and never once applied: `.seats-mid { padding-bottom: 0;
   min-height: 0 }` sits later in the file with no guard on it either, put
   there when the felt went back to being only the table. §8J, §4.22. */
.seats-mid { display: flex; flex: 1; align-items: stretch; }
.seat { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px; }
/* ---- why the seats do NOT ask their column yet (Track A, C3, 18 Sep) ----

   The plan was a container query: a seat should size to the room it has, not
   to the window. It was tried, measured, and taken back out, and the reason
   belongs here so nobody spends the afternoon again.

   `container-type: inline-size` stops a box being sized by its contents.
   These columns are flex items that rely on exactly that: between 700 and
   899px the column is 118px of flex-basis with shrink, and what stops it
   collapsing is the card inside it. Contained, nothing stops it — measured
   at 760, 820, 860 and 880: the column went from 76px to **16** and the card
   from 60px to **nothing at all**. The seats disappeared.

   Two other things the same afternoon found: the partner's column is
   shrink-to-fit, so containing it took the card from 168.8px to 8; and an
   unnamed query matched whatever container was nearest, which for that card
   is the felt — a 360px phone drew a desktop seat.

   So containment needs the columns to have widths of their own first, which
   is a layout change and its own slice. Until then the window answers, and
   the numbers below are still named once.

   Worth keeping in view while that is designed: at 600-700px the side column
   is 180-212px — WIDER than the 168 it gets at 900 — and draws a phone-sized
   seat, while between 760 and 880 it is 76px and draws the same phone-sized
   seat. The window is not the room, which is the whole argument. */
.seat .name { font-size: 15px; opacity: .95; }
.seat .name .turn-dot { color: var(--gold); }
/* Bones under the header bar, name below them — the mirror of your own end,
   where the plate sits below the hand. It was the other way up, so both names
   were pushed toward the top of the screen instead of facing each other
   across the table. `column-reverse` rather than reordering the markup: the
   seat is built by one function for four chairs, and the reading order that
   makes sense to a screen reader — who this is, then what they hold — is the
   order the markup already has. BACKLOG 1K.3.

   No clearance rule, and 1Q.2 is why there deliberately is not one. The
   fan's top row appeared cut mid-bone by the header at 1155 and a 14px
   margin was added for it — then measured: the clipping was 1Q.1's dock
   overflow scrolling the page, not the seat sitting too high, and the
   margin itself tipped a height-tight 1917 into scrolling by its own 14px.
   With the dock fixed the fan clears the header at every width with no help.
   One root cause, two symptoms; the second fix was the first one. */
/* `column` with the fan ordered first, not `column-reverse`. The reversed
   axis lays out bottom-up, and with the fan's negative-margin second row in
   the mix the head's resting place differed between a fresh render and a
   reflow — measured at 75px against 90px for identical content, which
   `pagecheck`'s never-move check caught as the partner's name twitching
   mid-hand. `order: -1` gets the same picture (bones above, name below, DOM
   reading order intact) from a normal top-down layout that resolves the same
   way every time. BACKLOG 1K.3, and the twitch rule doing its job. */
/* The partner's name plate was lifted out of flow and pinned to the seat's
   foot, because with the fan above it in one flex column its resting place
   differed between a fresh innerHTML and a later reflow — 75/90 first, then
   64/80 after the fan's margins changed, caught both times by `pagecheck`.
   That was a fix for a fan whose height changed. The pile's height cannot
   change, so the head can go back into flow and the seat can go back to being
   a box with a thing in it. BACKLOG 1K.3's tail, closed by 1R.8. */
.seat-n { align-self: center; }
.trick-area {
  flex: 1; display: grid; place-items: center;
  grid-template-areas: '. n .' 'w c e' '. s .';
  grid-template-columns: 1fr auto 1fr; grid-template-rows: 1fr auto 1fr;
  gap: 4px; padding: 6px;
}
.trick-slot { min-width: 80px; min-height: 42px; display: flex; align-items: center; justify-content: center; }
/* No `opacity` here: it said .6 and lost at every width to `.trick-slot .who
   { font-weight: bold; opacity: .8 }` further down, which is Moon's ask for
   bolder names under the played bones. §8J, §4.22. */
.trick-slot .who { font-size: 10px; display: block; text-align: center; }
.tp-n { grid-area: n; } .tp-s { grid-area: s; } .tp-w { grid-area: w; } .tp-e { grid-area: e; }
/* Pulled in toward the middle. The outer columns are `1fr`, so a slot centred
   in its own column sits halfway out to the seat — and on the east side that
   is exactly where Becky's hand is, which is what the screenshot showed. The
   north and south slots were already central because their column is `auto`.
   Reported 21 Aug, and Mike asked whether the other two were off as well:
   they were not. BACKLOG 1K.9. */
#screen-42 .tp-w { justify-self: end; }
#screen-42 .tp-e { justify-self: start; }

/* ---------- the scorepad, short and wide ---------- §1K.10

   Phone only, and deliberately: on a desktop the pad stands in a column of its
   own beside the hand with height to spare, and this would be solving nothing
   there. On a phone it sits under the bones, where height is the scarce thing.

   Us on the left and Them on the right, as asked, and the words come up in
   size because turning the card sideways is what buys the room for them. */
@media (max-width: 560px) {
  #screen-42 .score-pad { display: flex; align-items: stretch; gap: 12px; }
  #screen-42 .score-pad .pad-row {
    flex: 1; flex-direction: column; align-items: center; gap: 4px; min-width: 0;
  }
  #screen-42 .score-pad .pad-lbl { width: auto; font-size: 17px; }
  #screen-42 .score-pad .pad-rule { width: 1px; height: auto; margin: 0; flex: none; }
  /* Moon keeps its own shape — three players rather than two teams — but the
     same argument applies to its height, so its three rows go across too. */
  #screen-moon .mn-pad-rows { flex-direction: row; gap: 12px; }
  #screen-moon .mn-pad-row { flex-direction: column; align-items: center; gap: 2px; min-width: 0; }
  #screen-moon .mn-pad-name { font-size: 15px; }
  /* 1K.10 turned three rows into three columns to save height and did not give
     the columns the width that saved — and the names are the widest thing in
     there. The card takes the room it needs now. BACKLOG 1M.11. */
  #screen-moon .score-pad.mn-pad { width: 100%; }
  #screen-moon .mn-pad-rows { justify-content: space-around; }
}
/* `min-width: 0` so the label can shrink rather than shoving the grid.

   Reported off an Android phone, 26 Aug: "the first lead in middle is off
   centre". It was, by about 30px at his width, and it is fixed - but not by
   this rule. The cause was the side seats: they were `flex: 0 0 27%` of a row
   whose other member sized itself, and the seat unit gave them a width of
   their own. At 430 the trick centre now measures exactly the felt's centre.

   What is left, and is recorded rather than fixed: **at 360 it is still 16px
   off**, and that one is older and larger than a centring rule. The trick
   area gets 138px there, its three tracks resolve to 54 each plus gaps, and a
   played bone at 360 is 72px wide - the middle of the table is asked to hold
   three bones across a band that fits less than two. Shrinking the label wins
   16px of the 32 it is over by and costs 1F.6, which raised this type
   *because* it was the first thing to vanish on a phone. It wants the played
   bones resized at 360, which is its own piece of work. BACKLOG 1T.2. */
/* The middle of the table: the message, and the trick label under it.

   One cell of the trick grid, holding both, in flow. The played bones take the
   four cells around it, so the grid spreads THEM to fit THIS - which is the
   whole reason the message is a child here rather than a box positioned over
   the felt. Every earlier version was positioned, and every one of them landed
   on a domino or on a seat as soon as something else changed size. */
.tp-mid {
  grid-area: c; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 6px; min-width: 0;
}
/* In flow here, not floating over the felt. The other tables still position
   their stack over their own felt; only 42's has a cell of its own. */
.tp-mid .status-stack {
  position: static; transform: none; left: auto; top: auto;
  max-width: 100%; width: auto;
}
/* No `font-size` here any more. It said 12px and had never applied: the
   §1F.6 block further down sets 14.5px on `.tp-c, .mn-empty, .tk-empty`
   with no guard, and its own comment describes the 12px as the fault it was
   fixing — the corpse simply stayed here for three weeks. §8J, §4.22.

   `opacity: .6` is still here and IS still applying, which is worth a look
   rather than an edit: the same comment names "dimmed to 60%" as half of
   what was wrong, and only the size was ever changed. Filed, not touched. */
.tp-c { grid-area: c; opacity: .6; text-align: center; min-width: 0; }
/* 28 at the foot, not 14. The scorepad is the last thing in this band and it
   was finishing 26px from the bottom of the screen ordinarily and **4px** under
   Big print, where everything in the band is larger — reported as the pad
   running into the bottom edge. The pad is a piece of paper on a table and it
   needs a margin under it like everything else does. */
.my-dock { background: rgba(0,0,0,.3); padding: 12px 8px calc(28px + env(safe-area-inset-bottom)); color: var(--cream); }
/* The gap between your own bones, and its Big print twin, declared together
   so neither can go stale without the other being looked at. Asked for four
   times between 20 Aug and 27 Aug: 6 to 9 (v1.72.0), 9 to 12 (v1.73.0), and
   both times the profile doing the asking was reading a third number in the
   Big print block. It is bigger in Big print, which is the direction that
   block is for. BACKLOG 1U.7.

   The outline is the other half of it. A selected or ringed bone draws a 3px
   outline *outside* its box, so a 12px gap becomes 6px of daylight between two
   ringed bones and none at all where a ring meets its neighbour. The gap has
   to clear the outline on both sides and still read as a gap - "THERE NEEDS TO
   BE A GAP EVEN WHEN THERE IS A BORDER AROUND THE DOMINOES." */
:root { --hand-gap: 20px; --hand-gap-big: 24px; }
.my-hand { display: flex; gap: var(--hand-gap); justify-content: center; flex-wrap: wrap; }

/* bidding / overlay panels */
.overlay {
  position: fixed; inset: 0; background: rgba(0,0,0,.55);
  display: flex; align-items: center; justify-content: center; z-index: 40; padding: 18px;
}
/* Bidding and naming trumps: don't shadow the bones you're deciding with.
   The panel sits above the dock and the hand stays fully lit. */
.overlay-dock {
  /* Centre the panel in the table area, not the whole screen — the dock is
     measured at open time and handed over as --dock-h so your bones stay lit
     and uncovered while you decide. */
  bottom: var(--dock-h, 38vh); height: auto;
  align-items: center; padding-bottom: 6px;
  background: linear-gradient(to bottom, rgba(0,0,0,.6) 0%, rgba(0,0,0,.6) 74%, rgba(0,0,0,0) 100%);
  pointer-events: none;
}
.overlay-dock .panel {
  pointer-events: auto;
  /* Capped to the slot it is centred in. `.panel` carries `max-height: 86vh`
     for the full-screen overlays, and on a short phone that is taller than the
     gap between the top bar and the dock — so a centred panel overflowed at
     both ends and the top end landed on "← Leave". It scrolls inside itself
     instead, which it was already set up to do. */
  max-height: 100%;
}
/* And when it still cannot fit — a short phone with Big print on leaves a
   228px slot at 360x640 and 247 at 375x667, against a bid panel of 417 — say
   so. A sticky fade at the foot of the scroll box: it rides the bottom edge
   while there is content below it and settles into its own place, over the
   panel's padding, once the end is reached. Pure CSS, no measuring, and it
   is the difference between "the Pass button is hidden" and "there is more
   down there". BACKLOG §1AA.8, 3 Sep. */
.overlay-dock .panel::after {
  content: ''; display: block; position: sticky; bottom: 0; height: 28px;
  margin: 0; pointer-events: none;
  background: linear-gradient(to bottom, rgba(28,21,14,0), #1c150e 85%);
}
/* In flow, not pulled up over the last line. The first version carried
   `margin: -28px 0 0`, which put the strip over the last 28px of CONTENT —
   the bid history, which is the last thing in the panel — so on every panel
   that fitted, *Ed: 30 · Bobo: pass · Becky: pass* sat under the gradient
   and read as faded (§1AC.12; seen at 430 on 42 and Moon and on a 1254
   desktop). With no negative margin the strip is an empty 28px below the
   history: while the panel scrolls it rides the bottom edge over what is
   below, which is its job, and when it settles it covers nothing but
   itself. The panel's own bottom padding gives way to it so the total
   height is unchanged. `pagecheck` asserts the history is unfaded at 360,
   430 and 1280 wherever the panel does not scroll. */
.overlay-dock .panel { padding-bottom: 0; }
/* The way out is never covered. The dock overlay starts at the top of the
   screen, so on a phone a tall bid panel reached up over the "← Leave" bar and
   ate the tap — the button was visible, which is why the report said "not
   visible" only sometimes. Two halves to the fix and both are needed: the
   overlay now begins below the bar, and the bar outranks it. Measured before
   and after with `leavecheck.js`, which is in the release suite so this cannot
   come back quietly.

   Leaving mid-bid is meant to be possible. It asks for confirmation like every
   other exit, and somebody who wants out of a table should never have to guess
   whether the app will let them. */
.overlay-dock { top: var(--top-h, 58px); }
/* Sticky rather than merely raised. On a 360×640 phone the table is a few
   pixels taller than the glass whatever we do — the felt has a floor and the
   dock has a hand in it — so the page can scroll, and any scroll at all used
   to carry the way out off the top. Sticky costs nothing on the screens that
   do fit and is the difference between "usually reachable" and "reachable". */
.table-top { position: sticky; top: 0; z-index: 60; }
body.hand-lit .my-dock {
  position: relative; z-index: 50;
  box-shadow: 0 -6px 26px rgba(0,0,0,.5);
  background: rgba(0,0,0,.55);
}
body.hand-lit .my-dock .domino { box-shadow: 0 0 0 2px rgba(224,170,62,.35), 0 3px 6px rgba(0,0,0,.5); }
.panel {
  background: linear-gradient(170deg, #2a2018, #1c150e);
  border: 1px solid rgba(224,170,62,.5); border-radius: 14px;
  padding: 20px; max-width: 460px; width: 100%; text-align: center;
  max-height: 86vh; overflow-y: auto;
  color: var(--cream);              /* dark panel — always light type */
  font-size: 16px; line-height: 1.6;
}
.panel b { color: var(--emph); }
.panel h3 { color: var(--heading); margin-bottom: 4px; font-size: 24px; }
.fb-addr {
  margin-top: 12px; padding: 11px; border-radius: 8px; background: rgba(0,0,0,.4);
  font-family: monospace; font-size: 16px; color: var(--pill-hi); word-break: break-all;
}
.panel .sub { font-size: 15px; opacity: .8; margin-bottom: 14px; line-height: 1.5; }
.bid-grid { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; margin-bottom: 10px; }
.bid-grid button, .trump-grid button {
  background: var(--cream); color: var(--ink); border: none; border-radius: 10px;
  padding: 17px 22px; font-size: 23px; font-weight: bold; min-width: 74px;
}
/* The bid panel a fifth larger than the rest. It is the one modal you read
   under time pressure with three people waiting, and it was sized like an
   ordinary dialogue. Only where there is room for it — a phone has none to
   give, and this is a laptop complaint. */
@media (min-width: 700px) {
  .panel:has(.bid-grid), .panel:has(.trump-grid) { max-width: 552px; padding: 24px; }
}
/* On a phone the bid panel must FIT, not scroll. It sits in the slot between
   the top bar and the dock, and that slot was 368px tall at 360x780 with Big
   print on while the panel wanted 486 — so the 42 / Pass row sat below the
   panel's bottom edge with nothing to say it was there. The panel does scroll,
   but a stranger at a fairground does not know a modal scrolls, and Pass is
   the one button a beginner needs most. Measured 3 Sep (BACKLOG §1AA.8):
   three columns of 118px buttons made five rows; at 70px minimum the same
   fourteen make four rows of four, and the panel fits at 360x780 in both
   print sizes and at 375x667. Big print keeps its 46px touch floor below. */
@media (max-width: 699px) {
  .panel:has(.bid-grid) h3 { font-size: 20px; }
  .panel:has(.bid-grid) .sub { font-size: 13px; line-height: 1.35; margin-bottom: 10px; }
  .bid-grid { gap: 6px; margin-bottom: 6px; }
  .bid-grid button { padding: 10px 8px; font-size: 20px; min-width: 70px; flex: 1 0 70px; max-width: 110px; }
  .bid-grid button.pass { max-width: none; }
}
/* 42 in gold with dark letters — the LEAVE chip's dress — and Pass in red
   with cream. Mike, 2 Sep, off the phone and again off the desktop: *"make
   the forty-two button a gold button with black forty-two and the pass
   button a red button with white text."* One `.bid-grid` at every width, so
   this is every width. §1AC.13. */
.bid-grid button.pass { background: var(--red); color: var(--cream); }
.bid-grid button[data-bid="42"] { background: var(--gold); color: var(--btn-text); }
.bid-grid button.nello { background: #4a3a63; color: var(--cream); }
.trump-grid { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.bid-history {
  font-size: 16px; opacity: .95; margin-top: 12px; line-height: 1.7; color: var(--cream);
  border-top: 1px solid rgba(255,255,255,.14); padding-top: 10px;
}
.bid-history b { color: var(--pill-hi); }
/* After the base rule, or source order hands the phone's tighter history back
   to the desktop's numbers — `csslint` caught exactly that on 3 Sep. */
@media (max-width: 699px) {
  .bid-history { margin-top: 8px; padding-top: 6px; font-size: 14px; line-height: 1.5; }
}
.result-marks { font-size: 40px; margin: 8px 0; }
/* The end-of-hand card since §1G.7 (11 Sep): one line for the result, one
   for the standing, the next action, then the extras in rows. No mark token
   — Mike does not want the gold/white circle, in any form. */
.hr-line { font-size: 20px; font-weight: bold; margin: 6px 0 2px; color: var(--cream); }
.panel .sub.hr-marks { margin-bottom: 10px; }
.hr-row { display: flex; gap: 8px; align-items: stretch; margin-top: 8px; }
.hr-row .ghost-btn { flex: 1 1 0; width: auto; margin-top: 0; padding: 12px 8px; white-space: nowrap; min-width: 0; }
.hr-row.hr-keep .ghost-btn { flex: 1 1 0; padding: 10px 6px; font-size: 14.5px; }
/* *Back to your game*, on the end-of-hand card of a replayed hand (§3.18a).
   Full width and on its own, not in an `.hr-row` pair: it is the way out of
   a side trip rather than one of two equal extras, and the score under it
   is the thing that tells the player which game they are returning to —
   the replay has marks of its own on the same card.

   The score is a block rather than a second line of the label so the button
   keeps one height at both print sizes; `.hr-long`/`.hr-short` above solve
   the narrow case by swapping words, and this one has no long form to drop.
   No typed height here: Big print raises the font and the button grows with
   it, which is §3.18a's own rule about not writing a second number that can
   go stale. */
#hr-unstash { display: block; width: 100%; text-align: center; line-height: 1.25; }
#hr-unstash .hr-back-sub { display: block; font-size: .82em; opacity: .72; font-weight: 400; margin-top: 2px; }
/* The note box on the row, and the feedback box in the fold below it: the
   same dark box. The second was left to the browser's default (white, a
   monospace face) in the first cut of v1.114.0 — caught on the screenshot. */
.hr-row .save-note, .hr-trouble .hr-feedback {
  flex: 2 1 0; min-width: 0; resize: none; font: inherit; font-size: 14px; line-height: 1.35; padding: 8px 10px;
  border-radius: 8px; border: 1px solid rgba(255,255,255,.22); background: rgba(0,0,0,.28); color: var(--cream);
}
/* Two lengths of label: the whole phrase where the row has room, one word
   on a phone where "Replay this hand" ran off its half of the row. */
.hr-short { display: none; }
@media (max-width: 480px) {
  .hr-long { display: none; }
  .hr-short { display: inline; }
  .hr-row .ghost-btn { padding: 12px 4px; }
}
/* "What happened?" (§1G.8): the fold's line in bold so it pops a little
   (Mike, 11 Sep), the box first, the record under its heading, and the
   card growing when the fold opens — the record gets up to 45% of the
   screen's height to scroll in, and the panel takes the height it has. */
.hr-trouble { text-align: left; margin-top: 12px; }
.hr-trouble summary { cursor: pointer; font-size: 14px; color: var(--cream); padding: 4px 0; }
.hr-trouble summary b { font-weight: bold; }
.hr-trouble .hr-feedback { width: 100%; box-sizing: border-box; margin-top: 8px; resize: none; }
.hr-trouble #hr-feedback-send { width: 100%; margin-top: 6px; padding: 10px 8px; }
.panel .sub.hr-sendnote { font-size: 12px; opacity: .7; margin: 4px 0 10px; }
.hr-rec-head { font-size: 13.5px; margin: 8px 0 2px; color: var(--pill-hi); font-weight: bold; }
.hr-trouble-text {
  font: 12px/1.45 'Courier New', monospace; white-space: pre-wrap; word-break: break-word; color: var(--cream);
  background: rgba(0,0,0,.3); border-radius: 8px; padding: 8px 10px; margin: 6px 0; max-height: min(45vh, 360px); overflow: auto;
}
.hr-trouble .ghost-btn.quiet { margin-top: 0; padding: 8px 12px; font-size: 13px; }
.panel .big-btn { margin-top: 14px; }

/* everybody's hand at the end, so the count can be checked */
.reveal { text-align: left; margin-top: 6px; }
.reveal-row {
  padding: 8px 9px; border-radius: 9px; margin-bottom: 6px;
  background: rgba(255,255,255,.05);
}
.reveal-row.counted { background: rgba(224,170,62,.15); border: 1px solid rgba(224,170,62,.35); }
.reveal-who { font-size: 15px; color: var(--cream); margin-bottom: 5px; }
.reveal-who b { margin: 0 6px 0 4px; }
.reveal-pips { float: right; color: var(--pill-hi); font-weight: bold; }
.reveal-tiles { display: flex; flex-wrap: wrap; gap: 4px; }
.reveal-empty { font-style: italic; opacity: .6; font-size: 13px; }

/* toast */
.toast {
  /* clear of the header strip, which now carries the mark */
  position: fixed; top: 62px; left: 50%; transform: translateX(-50%);
  background: rgba(0,0,0,.88); border: 1px solid var(--gold); color: var(--cream);
  padding: 12px 24px; border-radius: 24px; font-size: 16px; font-weight: bold; z-index: 60;
  animation: toastIn .2s ease; pointer-events: none; max-width: 92vw; text-align: center;
}
@keyframes toastIn { from { opacity: 0; transform: translate(-50%, -8px); }}

/* score pop for fives */
/* 24, not 42. It was set larger than the wordmark at the top of the page and
   dropped across the middle of the board — a passing message drawn as an
   announcement. 1K.6 gave 42's passing messages a small dark pill for exactly
   this reason; this keeps its own shape because it is a number arriving at a
   place on the board, but it comes down to the size of one. BACKLOG 1M.9. */
.score-pop {
  position: absolute; font-size: 24px; font-weight: bold; color: var(--pill-hi);
  text-shadow: 0 2px 6px rgba(0,0,0,.8), 0 0 18px rgba(0,0,0,.5); animation: popUp 2s ease forwards;
  pointer-events: none; z-index: 55; left: 50%; top: 10px;
}
/* A shorter rise than it had: it starts at the top edge of the board now
   (see `popIfScored`), and 52px up from there is the standing seats' row. */
@keyframes popUp { 0% { opacity: 0; transform: translate(-50%,10px) scale(.6); }
  14% { opacity: 1; transform: translate(-50%,-2px) scale(1.2); }
  70% { opacity: 1; transform: translate(-50%,-10px) scale(1.05); }
  100% { opacity: 0; transform: translate(-50%,-24px) scale(1); }}

/* The board's Compact / Normal switch. It sits in the column Fives reserved
   and never filled — open space beside the board on a desktop, and lifted
   above your hand on a phone by `.dock-left { order: -1 }`, so it costs the
   bones nothing in either layout. Anchored to the top of its column because
   the column is as tall as the whole band and a control floating in the middle
   of it reads as something that has come loose. */
/* Centred in its column rather than hung off the left corner — it reads as
   part of the table that way rather than as something left in a margin. */
/* `--bv-lane` is this control's width and the dock head reserves a track of
   exactly it — see `#screen-fives .dock-head`. Measured at 310px ordinary and
   347 under Big print; the lane takes the larger so the plate never lands on
   the switch at either print size. */
:root { --bv-lane: 360px; }
.board-view { display: flex; align-items: center; gap: 10px; }
.bv-slot { display: flex; align-items: center; }
/* Bigger and plainer than it first shipped. It went in as an 11px caption
   reading "Board" beside a small pill, which is the size you make something
   when you are not sure it earns its place — and the point of the switch is
   that people should notice it is theirs to change. Reported 18 Aug. */
/* 16, not 13. It is a label on a control the player is meant to notice is
   theirs to change, and at 13px on a 1900px screen it read as fine print
   beside a switch twice its height. Asked for directly, 27 Aug. The note two
   rules up already says this control went in too small once and was raised
   for the same reason; this is the label catching up with the switch. */
.bv-head {
  font-size: 16px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--heading); opacity: .9; font-weight: bold;
}
.seg.bv-seg { padding: 3px; }
.seg.bv-seg button { padding: 9px 16px; font-size: 14.5px; }
/* On a phone the column is a strip above the hand rather than a tall box, so
   the switch lies across it and centres instead of hanging from a corner. */
@media (max-width: 899px) {
  .board-view {
    position: static; flex-direction: row; align-items: center;
    justify-content: center; gap: 8px; margin-bottom: 4px;
  }
  /* Shorter, on the game with the least height to spare. The label goes on the
     same line as the two options rather than standing over them, and the
     buttons lose the padding they were carrying from the desktop. BACKLOG
     1K.12. */
  .bv-head { font-size: 11.5px; }
  .seg.bv-seg { padding: 2px; }
  .seg.bv-seg button { padding: 5px 12px; font-size: 13.5px; }
  /* Your hand is the last thing before the edge of the screen, as it is on 42.
     The score row sat between the board and the bones. */
  #screen-fives .dock-mid { display: flex; flex-direction: column; }
  #screen-fives .dock-mid .my-hand { order: 1; }
  #screen-fives .dock-mid .dock-head { order: 2; }
  #screen-fives .dock-mid .dock-pad { order: 3; }
}
/* On a desktop it hung from the top corner of a column the height of the
   whole band, which put it about as far from the dock it belongs to as the
   band allows. Pinned to the bottom of its column instead, so it sits beside
   the bones rather than above the felt. BACKLOG 1L.2. */
@media (min-width: 900px) {
  /* Out of the column entirely. Two attempts left it in the left column —
     first its top corner, then its inner edge — and Mike asked a third time
     in capitals. `left: 100%` parks it past the column's edge, at the start
     of the dock's middle, beside the bones it controls. BACKLOG 1Q.8. */
  /* `#f-dock-left .board-view`, not bare `.board-view`: the host is a
     `.dock-side`, and `.dock-side > *` — later in the file — sets `inset: 0`
     at the same specificity, so the bare version lost the tie and the switch
     never moved. Third selector-tie of the night; the lesson is the id. */
  /* The Board view switch is a child of the dock's head row now, so there is
     nothing to position: it takes the row's height and baseline like every
     other chip in it. What stood here was `left: 100%` pushing it out of the
     left column into the middle one, plus a reserved track in the row to keep
     the plate off it, plus a `top` nudged by hand — three rules holding one
     control in a place the row could not see. BACKLOG 1V.2, 1W.4. */
}

/* ---------- Fives board ---------- */
.table-fives { flex: 1; display: flex; flex-direction: column; overflow-x: hidden; }
.fives-seats { display: flex; justify-content: space-around; padding: 4px 8px; flex: none; }
/* On a phone the two side seats hug the corners and the partner stands
   between them — *"keep Ed in that top-left corner, Becky in the top-right
   corner, put Bobo in the middle with a vertical layout like Ed and Becky."*
   (§1AC.16, 2 Sep.) `space-between` with a little side padding; the three
   cards are the same shape now, so the row reads as three of a kind. */
@media (max-width: 899px) {
  .fives-seats { justify-content: space-between; padding: 4px 12px; align-items: flex-start; }
}
/* Fives' and Moon's piles were sized here, in their own tiers, on their own
   `--pile-bone`. The comment above the Moon rule said "interim until 1R.8's
   component" — this is 1R.8, and the component is where those sizes live now.
   One scale for every seat on every table. */
/* Third size request, and this one carried the reason that settles it: the
   player's chosen back is the point of the custom-backs feature, and a back
   nobody can read is a feature nobody can see. Mike, 24 Aug, in capitals.
   Two desktop tiers rather than one number, because 120 at 1400 squeezed the
   Fives seat row until the turn token sat on a count — seen in the shot
   before this shipped, which is the harness doing its job. BACKLOG 1R.4. */
@media (min-width: 900px) {
}
@media (min-width: 1550px) {}
/* The overlap lives on `.pile` now — three hand-tuned numbers here (-26, -30
   and -36 below) were each measured against a different row width, which is
   how a spread that fitted at the end of a hand never fitted at the start of
   one. BACKLOG 1N.1. */
/* On a phone, only your partner's bones are drawn. §1B.8.

   Three fans of seven overlapping backs need about 570px, and a phone row is
   360. The seats do not wrap — they are clipped by `overflow-x: hidden` on the
   table — so measured at 360px the west seat ended at 214, the north seat ran
   to 396 and the east seat to 578: **two of the three opponents were partly or
   wholly off the side of the screen**, 218px past the edge.

   What made it look intermittent rather than broken is worth recording,
   because it is what the report noticed: by the end of a hand all three *are*
   visible, because each is down to a bone or two and the fans have shrunk. The
   strip was sized for the end of a hand and used throughout one.

   So the opposition give up their backs and keep the two facts you actually
   need from them across a table — who they are, and how many bones they are
   still holding. Your partner keeps the drawn bones, which is the seat worth
   spending the width on: it is the one that makes the table feel occupied
   rather than tabulated, and it is the request as it was made.

   `#fseat-n` is always the seat opposite you — `PP.seatAt(2)` — so this is
   your partner whenever there is one. In cutthroat there is no partner and it
   is simply the seat with the room; drawing one hand of backs rather than
   three is still the right trade there.

   CSS rather than a branch in the renderer on purpose: the table does not
   re-render when the window changes, so a phone turned on its side has to be
   handled by the stylesheet or not at all. */
/* **All three seats keep their bones again, as of 22 Aug.** Everything above
   is still true of a *spread*, and the pile is what makes it moot: seven backs
   at a 4px step come to 80px rather than 198, so three seats need about 240 in
   a 344px row and fit at the widest moment of a hand rather than the narrowest.
   Mike asked for exactly this trade — *"single stacks for everyone so we see
   backs from each but are respectful of space"* — and then for the pile that
   pays for it. The paragraph above is kept because it records why they were
   ever taken away, and that reasoning is what the pile answers. BACKLOG 1N.1,
   1K.13. */
@media (max-width: 560px) {
  /* And the partner's own fan is tightened, because the width that has to fit
     is the *start* of a hand and not the middle of one.

     A small back measures 54px, so seven of them at the -30px overlap above
     come to 378 − 180 = 198 — and the three seats then need 381 in a 344px
     row. Sizing this against a hand already part-played is exactly the mistake
     the strip made in the first place. At -36 the same seven come to 162, and
     the row fits at its widest moment with room to spare. */
  /* Superseded by the pile's step, which is one number for every seat. */
  /* Superseded by `.pile` — see the rule near the bone sizes. Phone-only was
     the wrong scope: Fives seats all three opponents in one row, so a desktop
     spread competes with itself for one row's width rather than using three
     edges the way 42 does. BACKLOG 1N.1. */
  .fives-seats { padding-left: 4px; padding-right: 4px; }
  /* Two seats' counts were sized here and the third was not, which is the
     kind of thing that only happens when there is no component. The unit
     sizes them all. */
  .fives-seats { align-items: flex-start; }
}
/* The zoom. Both boxes move together — the inner one scales, the outer one
   reports how much room the scaled thing actually takes so the flex centring
   stays honest — and both are eased, so a formation that outgrows the table
   backs away from you instead of snapping to a new size. */
.fb-box { overflow: visible; transition: width .34s ease, height .34s ease; }
.fb-inner { position: relative; transform-origin: top left; transition: transform .34s ease; }
@media (prefers-reduced-motion: reduce) {
  .fb-box, .fb-inner { transition: none; }}
/* Every bone and end-marker on the Fives board is absolutely placed; only the
   two coordinates differ, and those are set from script after the element is
   in the document. See `renderBoard` — the reason they are not written into
   the markup is that `style-src` governs style attributes that arrive in
   parsed HTML, and the CSSOM is outside it. */
.fb-at { position: absolute; }

/* Hidden, as a class rather than an inline `display:none`. One name, so the
   markup that draws a row and the code that later hides it agree in kind. */
.gone { display: none !important; }
/* The theme picker's rows: left-aligned, and the chosen one edged in gold. It
   was an inline style with a conditional tacked on the end of it. */
.theme-pick { text-align: left; }
.theme-pick.on { border-color: var(--pill-hi); }

.board-wrap { flex: 1; overflow: hidden; display: flex; align-items: center; justify-content: center; padding: 8px; min-height: 220px; }
/* ---------- the page never scrolls mid-hand ---------- §1S.1

   Fives' scorepad grows all game — a column of X's per fifty points — and the
   dock grows with it. The board's own promise (scale to the space, never
   scroll) held for the board's box while the PAGE total quietly broke it:
   past a threshold the window overflowed, the browser then auto-scrolled to
   every clicked bone, and the seat row ended up under the header bar with a
   pile printed through the wordmark. Caught by playtour's 42-mark frame.

   The screen owns the viewport and the board is the designated shrinker: as
   the dock grows the wrap gives, and the board rescales into what is left —
   which is exactly the machinery it already has. The 220px floor stays for
   the usual case; `min-height: 0` on the flex chain is what lets the give
   actually happen when the pad is tall. */
@media (min-width: 900px) {
  #screen-fives.active { height: 100vh; height: 100dvh; max-height: 100vh; max-height: 100dvh; display: flex; flex-direction: column; overflow: hidden; }
  #screen-fives .table-fives { min-height: 0; }
  #screen-fives .felt { min-height: 0; }
  #screen-fives .board-wrap { min-height: 120px; }
}
.board { display: grid; grid-template-rows: auto auto auto; justify-items: center; align-items: center; gap: 6px; min-width: min-content; margin: auto; }
.board-mainrow { display: flex; align-items: center; gap: 5px; }
.board-armv { display: flex; flex-direction: column; align-items: center; gap: 5px; }
.end-marker {
  width: 40px; height: 40px; border: 2px dashed var(--marker); border-radius: 8px;
  display: flex; align-items: center; justify-content: center; color: var(--marker);
  font-size: 12px; font-weight: bold; cursor: pointer; background: rgba(224,170,62,.12);
  animation: pulse 1.2s infinite; flex: none;
}
@keyframes pulse { 50% { background: rgba(224,170,62,.3); }}
.board-empty-hint { opacity: .6; font-size: 14px; font-style: italic; padding: 30px 10px; text-align: center; }
.pass-btn { background: var(--red); color: #fff; border: none; border-radius: 24px;
  padding: 10px 26px; font-size: 15px; font-weight: bold; margin: 6px auto; display: block; }
.fives-total { font-size: 13px; opacity: .8; text-align: center; padding-bottom: 4px; }
.fives-total b { color: var(--pill-hi); font-size: 16px; }

@media (max-width: 480px) {
  /* §4.18: the cosmetic half of the wrap above. It touches every settings
     .seg, not only the theme row — the avatar faces go 5 and 3 rather than
     4 and 4, and AI difficulty narrows. Both were better in the shots. */
  #screen-rules .rule-row > .seg button { padding-left: 8px; padding-right: 8px; }
  .domino.h { width: 68px; height: 34px; } .domino.v { width: 34px; height: 68px; }
  .domino .half { width: 34px; height: 34px; }
  .trick-slot { min-width: 70px; }}

/* ---------- phones ----------
   Keep the whole table on one screen: compact chrome, a hand that fits in a
   single row, and no wasted vertical space. */
@media (max-width: 430px) {
  .table-top {
    padding: 6px 8px; gap: 5px; font-size: 12px;
    justify-content: center;
  }
  .score-pill { padding: 3px 9px; font-size: 11.5px; }
  .score-pill small { display: none; }
  .exit-btn { padding: 3px 9px; font-size: 11px; }
  .status-line { padding: 4px 8px; font-size: 13px; }

  /* ---------- the hand on top, the score under it ----------

     Reported 14 August: *"dominos in hand should be at top, score should be at
     bottom."* Side by side was an improvement on what came before it — the pad
     used to squash seven bones into one thin strip — but it still spends a
     third of the width on the thing you glance at between hands, and gives
     two-thirds to the thing you read every few seconds.

     Stacked the other way round, the hand gets the whole screen. Measured at
     390px: the hand had 244px beside the pad and has about 370px above it,
     which is what pays for the bones below. The pad keeps its natural size
     underneath, where a glance finds it.

     This is not the arrangement the original complaint was about. That one was
     *seven bones in a single row* with the pad under them; this is four then
     three, across the full width. */
  .my-dock { padding: 8px 4px calc(10px + env(safe-area-inset-bottom)); }
  /* Sized off the screen rather than guessed at per breakpoint. The hand now
     has the whole width — the pad moved out from beside it on 15 Aug — so four
     across is exactly (100vw − 8 padding − 12 gaps) / 4, and there is no
     reason to write that number down by hand at three different widths and
     keep them in step. 390px gives 92.5 where the old fixed 82 sat; 360 gives
     85 where it was 78.

     The cap is the only judgement in here: past about 92 the bones stop
     reading as a hand and start reading as flashcards, and the width freed up
     is better spent on the felt above.

     One number, in one place, and three rules that read it. The sizes used to
     be written out three times per breakpoint and again for Big print — which
     is how Big print came to be *smaller* than ordinary print twice: somebody
     grows the ordinary hand and the other four numbers sit there unread.

     **A thing that is now true and was not before:** on a narrow phone this is
     screen-limited, not taste-limited, so ordinary and Big print arrive at the
     same number — four across at 360px cannot exceed 85 for anybody. Big print
     is still larger wherever there is room to be. `fitcheck.js` knows this and
     says so where it checks. */
  /* Bigger, and closer together. Reported 17 Aug: bigger helps visibility and
     should not add much risk of a misplay — which revisits §1.1's call of
     13 Aug in the other direction, by the person who made it.

     Two numbers move together and only one of them matters much. The gap goes
     4px to 2px, which frees six pixels across the whole row — about a pixel
     and a half a bone, worth having for the look rather than for the size. The
     real lever is the reserve in the formula: 20px down to 18, and the cap 92
     up to 96, so a wider phone gains and the narrowest is unchanged by the cap
     because it was never reaching it.

     `fitcheck.js` is the arbiter and it is not a formality — it fails the
     build if four across stop fitting, if two bones overlap, or if one leaves
     the screen, at four widths in both print sizes. §1.1 and §1A.4 are both
     records of getting this wrong by eye. */
  /* Cap 96 to 100, 19 Aug, and the number is bounded from both sides.

     96 was the largest that fitted four across at 412, which is where it was
     measured — but this block runs to 430, and there (430 − 18) / 4 is 103, so
     phones between 412 and 430 were drawing 412's bones and leaving up to 7px
     a bone on the table. Reported as "the dominoes are still not as big as
     they should be on mobile".

     **Not 104, and `fitcheck` is why.** Big print must be strictly larger than
     ordinary at the widest phone measured, because that is the one width with
     headroom under the cap where a stale Big print number would show up. At
     430 the glass allows 103px to anybody; take ordinary to 104 and both sizes
     land on 103 and that check has nothing left to catch. 100 keeps the
     headroom and still hands back four of the seven pixels.

     **And the rest of those pixels are not available at all.** Four bones
     across a 430px screen cannot exceed 103px for anyone, whatever the cap
     says. Bigger than that means three to a row and a third row — which is a
     layout decision, not a number. BACKLOG 1D.3. */
  .my-dock { --bone: min(100px, calc((100vw - 18px) / 4)); }
  /* And the same sum against the room that actually exists.

     `100vw` is the window, not the row the bones sit in — they differ by the
     dock's padding, by a scrollbar, and by whatever a device does with display
     scaling. Reported 20 Aug on Android as bones with big gaps between them, on
     a build where this measures 100px bones and 6px gaps here, filling 418 of
     the 422 available. The layout was right on every width I could measure and
     wrong on the phone it was reported from, which is the signature of a sum
     done against the wrong box.

     `cqi` is that box: the dock's own inline size. Where container queries are
     supported this wins by being later; where they are not, the line above
     still applies and nothing is worse than it was. Four across either way —
     §1F.5 asks for the hand to fill the bottom of the screen four at a time,
     and that is what the divisor is. */
  .my-dock { container-type: inline-size; }
  .my-hand { --bone: min(100px, calc((100cqi - 18px) / 4)); }
  .my-dock { --dock-bone-h: calc(var(--bone) / 2); }
  .my-dock .domino.h { width: var(--bone); height: calc(var(--bone) / 2); }
  .my-dock .domino.v { width: calc(var(--bone) / 2); height: var(--bone); }
  .my-dock .domino .half { width: calc(var(--bone) / 2); height: calc(var(--bone) / 2); }
  .dock-head { margin-bottom: 6px; padding-bottom: 6px; }
  .score-pad { transform: rotate(-1.2deg) scale(.72); transform-origin: center top; max-width: 100%; }
  .fives-pad .pad-cols { gap: 8px; }
  /* The step comes down with the bone, and `fitcheck` is why the two move
     together rather than only the bone. Its pile-spread rule says a pile must
     stay near one bone wide — a fanned spread of seven measures about 3.5, and
     staying near one is the whole reason three of these fit a 360px row. At
     52px with a 7px step the reserve is 1.81 bones and the rule bites at 1.8;
     at 6px it is 1.69. Six-pixel strips are still countable by eye, which is
     the only thing the step owes anybody. BACKLOG 1N.1. */
  /* The seat card carries its own four modes — see `.tok` — so nothing about
     a seat is sized here any more. */

  .seat .name { font-size: 11.5px; }
  .seat { padding: 4px; gap: 3px; }
  .trick-slot { min-width: 54px; min-height: 34px; }
  .trick-slot .who { font-size: 9px; }

  /* board and pad */
  .board-wrap { padding: 4px; min-height: 180px; }
  /* Big print on a 360 phone was 61px over at the start of a Fives hand —
     the one phone layout that still scrolled (§1AC.9's leftover, 3 Sep). The
     sum: three standing seats 189, the board's floor 180, the lane 87, the
     dock 327 with a hand three across. The floor is the only number in it
     that is not holding something: it keeps the board from jumping while the
     first bones go down, and at the start there are two. 104 is under a
     vertical double at Big print's 101 plus the padding, so at the start
     the board is exactly as tall as what is on it and the page fits at
     360x780 with a few pixels spare (116 was tried first and left it 9
     over in `fitcheck`). The board still grows with the hand after that,
     which is §1W.1's item, not this one. */
  html.big-print .board-wrap { min-height: 104px; }
  .panel { padding: 16px; }
  .rb-sec p { font-size: 14px; }
}

/* very short screens: trim more vertical padding */
@media (max-height: 700px) {
  .tagline { margin-bottom: 18px; }
}

/* ---------- How it works: the honest account of what the app is doing ---------- */
/* A panel whose body already scrolls must not scroll as well, or a desktop
   shows two bars side by side for the same content. The inner one is the one
   that matters — but the way that used to be achieved was to switch the
   panel's own `overflow-y` to `visible`, and that was wrong in a way that only
   showed on a short screen.

   A panel is more than its body. There is a heading above the scroller and
   there are buttons below it, and 66vh of body plus a heading plus three
   buttons does not have to fit inside 92vh. When it did not, `visible` meant
   the excess was not merely off the bottom, it was **unreachable** — the thing
   that would have scrolled to it had been switched off. Measured on 16 Aug at
   450x974: 806px of content in a 775px box, so 31px sat outside the panel with
   no way to get to it, and that 31px was the Close button. The bots card was
   worse for the dullest reason — four buttons under the scroller instead of
   three. Reported twice, as "the Close goes outside the black box".

   So bound the *body* instead of un-bounding the panel. The panel becomes a
   column, keeps its `max-height` and keeps `overflow-y: auto` from the base
   rule as a safety net that should now never be needed; the scroller takes the
   room that is left over after the heading and the buttons rather than
   claiming 66vh regardless of them. Still exactly one scrollbar, still the
   inner one — and the buttons are inside the box at every height.

   Both panels named above are gone as of 19 Aug — *How it works* and the
   bots card became `how-the-bots-play.html`. The rule is kept because What's
   new and the Taroky panel have the same shape and would hit the same bug;
   the measurements above were taken on the panels that are no longer here. */
.panel:has(> .whatsnew), .panel:has(> .tr-panel) {
  display: flex; flex-direction: column; max-height: 92vh;
}
/* Everything that is not the scroller keeps its own size. Without this the
   buttons are flex items with the default `flex-shrink: 1` and get squashed
   along with the body when the panel is tight, which is the same bug wearing
   a different hat. The `:not()` also keeps this off the scroller entirely, so
   there is no equal-specificity tie with the rule below deciding it on source
   order — see `csslint.js` for why that matters here. */
.panel:has(> .whatsnew) > *:not(.whatsnew),
.panel:has(> .tr-panel) > *:not(.tr-panel) { flex: 0 0 auto; }
.panel > .whatsnew, .panel > .tr-panel {
  flex: 1 1 auto; min-height: 0; max-height: none; overscroll-behavior: contain;
}

/* A laptop is wide but short: keep the far seats' bones big enough to show the
   design without pushing the scorepad off the bottom of the screen. */
@media (min-width: 561px) and (max-height: 900px) {
  .dock-pad .score-pad { transform: rotate(-1.2deg) scale(.85); transform-origin: center top; }
}

/* ten backs, five across — the previews shrink before the grid does */
.back-picker .domino.back.h { width: 100%; max-width: 72px; height: auto; aspect-ratio: 2 / 1; }
.back-picker .domino.back .half { width: 50%; height: 100%; }
@media (max-width: 480px) {
  .back-picker { gap: 5px; }
  .back-opt { padding: 7px 3px 6px; }
  .back-name { font-size: 10px; }}

/* The partner's bones were a third size, because his seven lay in a line
   across the top and the other two stacked down the edges. He holds a pile
   like everybody else now, so there is one size for every seat at a given
   width and it lives on `.seat-unit`. That is most of what 1R.8 is for. */

/* ---------- peek: a result panel you can push out of the way ---------- */
.overlay-peek {
  background: rgba(0,0,0,.3);
  bottom: var(--dock-h, 34vh); height: auto;
  align-items: center; padding-bottom: 6px; pointer-events: none;
}
/* No taller than the strip above the dock, and scrolling inside it. The
   panel was centred in that strip whatever its height, so a card taller than
   the strip ran off BOTH ends — at 360x780 the end-of-hand card, 664px with
   §3.18's replay row on it, started 46px above the screen with its 👁 Table
   button on the part nobody could reach (92px in Big print). Caught by
   `pagecheck` clicking that button on 2 Sep. A short card is still centred;
   a tall one fills the strip and scrolls, with the button at its top. */
.overlay-peek { overflow-y: auto; }
.overlay-peek .panel { pointer-events: auto; position: relative; max-height: none; overflow: visible; margin: auto; }
/* Since v1.107.0 the card is never capped: it is the strip that scrolls,
   not the pane (Mike, 9 Sep: "I don't want to have to scroll to play next
   hand… scrolling on a device on the whole screen but not the pane").
   `margin: auto` centres a short card in the strip and lets a tall one
   start at the strip's top and scroll with it — an auto margin gives way to
   nothing when the child is the taller. Wheel and touch over the pane
   scroll the strip, its pointer-events: none notwithstanding, because the
   pane is the target and the strip is the nearest thing that scrolls. And
   on a wide screen the strip is the whole height below the top bar, over
   the dock: at the end of a hand the dock is empty of bones and the
   scorepad sits to its right, outside a 460px card. A phone keeps the
   strip above the dock, where the GAME chip and the hint about it live. */
@media (min-width: 900px) { .overlay-peek { bottom: 0; } }
.peek-btn {
  position: absolute; top: 9px; right: 9px;
  background: rgba(0,0,0,.35); border: 1px solid rgba(255,255,255,.22);
  color: var(--cream); border-radius: 18px; padding: 5px 11px; font-size: 13px;
  font-family: inherit;
}
.overlay-peek.peeking { background: none; }
.overlay-peek.peeking .panel { display: none; }
.peek-back { display: none; }
.overlay-peek.peeking .peek-back {
  display: block; pointer-events: auto;
  position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%);
  background: var(--gold); color: var(--btn-text); border: none; border-radius: 22px;
  padding: 10px 22px; font-size: 15px; font-weight: bold; font-family: inherit;
  box-shadow: 0 3px 12px rgba(0,0,0,.55);
}

/* The build id, small and out of the way. It exists so "did my deploy land?"
   is a question you answer by looking at the footer instead of guessing. */
.build-id { margin-top: 10px; font-size: 11px; opacity: .35; letter-spacing: .5px; }

/* ---------- version, and the release notes behind it ---------- */
.build-id {
  margin-top: 12px; font-size: 11.5px; opacity: .45; letter-spacing: .5px;
  background: none; border: none; color: var(--table-text); font-family: inherit;
  padding: 4px 8px; border-radius: 12px;
}
.build-id:hover { opacity: .85; background: rgba(0,0,0,.2); }
.whatsnew { text-align: left; max-height: 58vh; overflow-y: auto; margin: 6px 0 8px; padding-right: 4px; }
.whatsnew::-webkit-scrollbar { width: 0; }
.whatsnew { scrollbar-width: none; }

/* Moved below the two rules it has to beat, 15 Aug. It sat above them and
   therefore applied only to `.hood` — a third panel, removed on 19 Aug when
   *How it works* became a page — while `.whatsnew` and `.tr-panel` are
   declared further down and won on source order, so two thirds of this
   fallback had never once run. Found by `csslint.js`, which is the whole
   reason that file exists. */
/* :has() is well supported now, but on anything that lacks it the fallback is
   the outer bar alone rather than two — so cap the inner one to the panel. */
@supports not selector(:has(*)) {
  .whatsnew, .tr-panel { max-height: none; overflow-y: visible; }
}
.wn-rel { margin-bottom: 18px; }
.wn-rel h4 { color: var(--heading); font-size: 18px; margin-bottom: 3px; }
.wn-rel h4 small { font-weight: normal; opacity: .6; font-size: 12.5px; margin-left: 8px; }
.wn-tag {
  background: var(--gold); color: var(--btn-text); font-size: 10.5px; font-weight: bold;
  border-radius: 10px; padding: 2px 8px; letter-spacing: .5px; vertical-align: middle;
}
.wn-sum { font-size: 14.5px; line-height: 1.6; opacity: .9; color: var(--cream); margin-bottom: 7px; }
.whatsnew ul { list-style: none; }
.whatsnew li {
  font-size: 14px; line-height: 1.6; color: var(--cream); opacity: .9;
  padding-left: 16px; position: relative; margin-bottom: 5px;
}
.whatsnew li::before { content: '·'; position: absolute; left: 4px; color: var(--pill-hi); font-weight: bold; }
.wn-build { font-size: 11.5px; opacity: .45; letter-spacing: .5px; margin-bottom: 4px; }

/* ---------- what the game cards are made of, per table ----------
   Standard is tooled leather (above). The other two take after what's actually
   under your elbows in those rooms. */

/* The Hall: the red plastic baskets the fish and pickle chips come out in.
   The first pass ran the weave straight under the text and you couldn't read a
   word of it — which is also true of a real basket, and is exactly why anybody
   serving food in one lays a sheet of paper in first. So: woven red basket,
   checked deli paper inside it, dark type on the paper. Authentic and legible
   for the same reason. */
body.theme-kc .game-card {
  position: relative;
  background-color: #a8241b;
  background-image:
    repeating-linear-gradient(90deg, rgba(0,0,0,.34) 0 4px, rgba(0,0,0,0) 4px 13px),
    repeating-linear-gradient(0deg,  rgba(0,0,0,.34) 0 4px, rgba(0,0,0,0) 4px 13px),
    repeating-linear-gradient(90deg, rgba(255,180,165,.16) 0 1.5px, rgba(0,0,0,0) 1.5px 13px),
    linear-gradient(168deg, #cf3a2c 0%, #ac251c 55%, #831710 100%);
  /* Two pixels, like every other table. It was three, and with border-box
     sizing that took two pixels out of the content box — so switching to the
     Hall nudged every card's heading up by one. A room should change the
     colours and nothing else. The heavier rim is drawn with a shadow instead,
     which costs no layout. */
  border-color: #7d150f;
  box-shadow: inset 0 0 0 1px #6d1009;
  border-radius: 14px;
  color: #2b2018;
  box-shadow:
    inset 0 3px 0 rgba(255,160,140,.4),
    inset 0 -5px 10px rgba(0,0,0,.4),
    0 6px 16px rgba(0,0,0,.45);
}
/* the sheet of checked deli paper the food sits on */
body.theme-kc .game-card::before {
  content: ''; position: absolute; inset: 10px; border-radius: 4px;
  background-color: #fbf6e9;
  background-image:
    repeating-linear-gradient(0deg,  rgba(190,60,45,.16) 0 9px, rgba(0,0,0,0) 9px 18px),
    repeating-linear-gradient(90deg, rgba(190,60,45,.16) 0 9px, rgba(0,0,0,0) 9px 18px);
  box-shadow: 0 1px 4px rgba(0,0,0,.35);
  transform: rotate(-.5deg);
  pointer-events: none;
}
/* everything the player reads sits on the paper, not the weave */
/* The Hall paints its cards with a ::before, so the contents are lifted above
   it. The Bonus star is positioned rather than in the flow, and this rule was
   overriding that — the star ended up wherever the flow put it, which on this
   one table was 150px in from the corner it belongs in. */
body.theme-kc .game-card > *:not(.bonus-star) { position: relative; z-index: 1; }
body.theme-kc .bonus-star { z-index: 2; }
body.theme-kc .game-card h2 { color: #8d1a12; text-shadow: none; }
body.theme-kc .game-card p { color: #33291f; opacity: 1; }
body.theme-kc .game-card .badge { box-shadow: 0 2px 6px rgba(0,0,0,.35); }

/* Northgate: a stainless steel tray — brushed grain, a bright rolled edge and
   the dents a few decades of table service leaves behind. */
body.theme-northgate .game-card {
  position: relative;
  background-color: #9aa0a4;
  background-image:
    repeating-linear-gradient(96deg,
      rgba(255,255,255,.13) 0 1px, rgba(0,0,0,0) 1px 3px,
      rgba(0,0,0,.10) 3px 4px, rgba(0,0,0,0) 4px 7px),
    radial-gradient(ellipse at 24% 18%, rgba(255,255,255,.30) 0%, rgba(0,0,0,0) 46%),
    radial-gradient(ellipse at 78% 88%, rgba(255,255,255,.16) 0%, rgba(0,0,0,0) 42%),
    linear-gradient(162deg, #c3c8cb 0%, #9aa0a4 44%, #6e747a 100%);
  border-color: #5d6368;
  border-radius: 16px;
  color: #23282c;
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.5),
    inset 0 -3px 7px rgba(0,0,0,.3),
    0 5px 14px rgba(0,0,0,.45);
}
/* the pressed well of the tray */
body.theme-northgate .game-card::before {
  content: ''; position: absolute; inset: 6px; border-radius: 11px;
  border-top: 2px solid rgba(0,0,0,.16);
  border-left: 2px solid rgba(0,0,0,.12);
  border-bottom: 2px solid rgba(255,255,255,.42);
  border-right: 2px solid rgba(255,255,255,.32);
  pointer-events: none;
}
body.theme-northgate .game-card h2 { color: #4a1010; text-shadow: 0 1px 0 rgba(255,255,255,.45); }
body.theme-northgate .game-card p { color: #2b3034; opacity: .92; }
body.theme-northgate .game-card .badge { box-shadow: 0 2px 5px rgba(0,0,0,.4); }

/* ---------- Taroky (bonus) ----------
   Playing cards, not dominoes: a separate visual language on purpose, so the
   bonus game never gets mistaken for part of the domino set. */
.table-taroky { flex: 1; display: flex; flex-direction: column; position: relative; overflow-x: hidden; }
/* Three seats across, always — a wrapping row put one player on a line of
   their own and made the table look lopsided. */
.tk-seats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; padding: 10px 6px; }
.tk-seat { text-align: center; min-width: 0; position: relative; }
/* "good" / "prever" under a seat during the ask floats over the felt below
   the back, not in the row's flow: in flow it made the seat row 25px taller
   for the length of the ask, and that was a 12px overflow on a 768-tall
   laptop under Big print (11 Sep). */
.tk-seat .tk-said { position: absolute; left: 50%; transform: translateX(-50%); bottom: -26px; white-space: nowrap; }
@media (min-width: 900px) {}
/* Three seats in three equal grid columns, and each unit fits its column.
   Without the floor a `max-content` seat simply overflows a `1fr` track -
   which put two of Taroky's three off the side of a 360px screen. */
.tk-seats > .tk-seat { min-width: 0; }
/* `.tk-backs` and `.tk-count` stood here — Taroky's own pile and its own
   count, a fourth rendering of the same idea. Both are the unit's now; the
   cards go in `.su-pile` and are sized with it. */
.tk-said {
  margin-top: 5px; font-size: 14px; color: var(--pill-hi);
  background: rgba(0,0,0,.35); border-radius: 12px; padding: 2px 10px; display: inline-block;
}
.tk-trick {
  flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
  /* 158: a played card at the 72px deck is 103px tall, its name label ~18,
     the band's own padding 24 — 120 left the card poking out of the bottom
     of the band and into the name plate below it. Sized to its contents at
     the largest deck, like every reserved height in this file. BACKLOG
     1Q.6. */
  gap: 14px; flex-wrap: wrap; padding: 12px; min-height: 158px;
}
/* Before a card has been led — the ask, the call, the lay-away — the trick
   band holds nothing, and on a phone the felt needs that height for the
   sixteen cards a povinnost holds while laying four away: three rows of
   six, 20px over a 780-tall screen in Big print (pagecheck, 11 Sep). In
   play the band is never empty ("trick 3 of 12" stands in it), so the
   reserved height holds through the hand as it must. */
@media (max-width: 899px) { #tk-trick:empty { min-height: 36px; } }
/* On the narrowest phones the buttons' second lines come off: three
   announcements with a sentence under each wrapped the row and put the
   last button below the screen at 360×780 in Big print (11 Sep). The
   status line above says what the choice is. */
@media (max-width: 420px) { #tk-actions .tk-btn small { display: none; } }
.tk-played { display: flex; flex-direction: column; align-items: center; gap: 4px; }
/* Kept here, unlike 42's. Taroky seats differently and the card in the
   middle does not sit in front of whoever played it, so the label is
   carrying its own weight. See 1F.4 for why 42's went. */
.tk-played .who { font-size: 13px; opacity: .7; color: var(--table-text); }
/* Size comes from the `.tp-c` rules below, which these now share — one
   label, one size, three games. BACKLOG 1J.3. */
.tk-empty { opacity: .5; font-style: italic; color: var(--table-text); }
.tk-hand { flex-wrap: wrap; gap: 4px; justify-content: center; }

/* a playing card */
/* Sized from a variable so the whole deck moves together — the played cards,
   the backs at the seats and your own hand were three places to forget.
   Reported 21 Aug as cards, names and text all being too small on a wide
   screen, and the room was already there: 1L.7 was leaving a screenful of
   empty table under the dock. BACKLOG 1L.8. */
:root { --pcard-w: 46px; }
/* 72 on a desktop: sixteen in one row comes to 1152 in a column of about 1180,
   so the row is full rather than floating in it. 68 on a phone, where 1P.5's
   4x4 grid is four across and 272 of 430. Both picked after the arrangement,
   which is the order 1P.4 asked for. */
@media (min-width: 900px) { :root { --pcard-w: 62px; } }
/* 72 only where eight of them fit the dock's middle column: 8x72 + gaps is
   611px, and the column is ~576 at a 900px window. One step, not a clamp,
   because the deck is discrete — cards at two fixed sizes read as two decks,
   cards scaling continuously read as a rendering bug. */
@media (min-width: 1150px) { :root { --pcard-w: 72px; } }
@media (max-width: 899px) { :root { --pcard-w: 68px; }}
.pcard {
  display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
  width: var(--pcard-w); height: calc(var(--pcard-w) * 1.435); border-radius: 5px; flex: none;
  background: #fbf7ec; color: #23282c;
  border: 1px solid #b8ae96;
  box-shadow: 0 2px 5px rgba(0,0,0,.4);
  font-family: Georgia, 'Times New Roman', serif; line-height: 1;
  position: relative; user-select: none;
}
.pcard.small { width: calc(var(--pcard-w) * .478); height: calc(var(--pcard-w) * .696); border-radius: 3px; }
.pcard .pc-rank { font-size: 15px; font-weight: bold; }
.pcard .pc-pip { font-size: 15px; margin-top: 2px; }
.pcard.red { color: #b3231a; }
.pcard.black { color: #1b2024; }
/* taroky are the trumps — they get their own face so they read at a glance */
.pcard.tarok {
  background: linear-gradient(165deg, #f3e6c8, #e2cfa2);
  border-color: #a8905c; color: #4a3410;
}
.pcard.tarok .pc-rank { font-size: 13.5px; letter-spacing: .5px; }
.pcard.tarok .pc-pip { font-size: 11px; opacity: .55; }
.pcard.trull { box-shadow: 0 0 0 2px var(--gold), 0 2px 5px rgba(0,0,0,.45); }
.pcard.back {
  background:
    repeating-linear-gradient(45deg, rgba(255,255,255,.08) 0 2px, rgba(0,0,0,0) 2px 5px),
    linear-gradient(160deg, #7d2420, #4d1512);
  border-color: #3a100e;
  overflow: hidden; padding: 0;
}
/* The back is Mark G: the rough Czech lion on Bandera Red, an inline SVG that
   fills the card (`cardHTML`, `brand/gen_cardback.py`). The gradient above is
   what shows if the mark is ever missing, which is also what it was before. */
.pcard.back .card-back-art { display: block; width: 100%; height: 100%; }
/* The seat's card — one back per opponent, and its size is a variable with a
   Big print value of its own, per the §1 lesson. Twice what it was (§1AB.1,
   §1AC.8): the hatched rectangle was 16x24 on a phone, and the lion needs
   about 34 across to read as a lion. On a desktop the seats lie down at a
   fixed 80px (`.tok.tok-h`), so the card is sized to sit inside that row —
   52 wide is 75 tall — and Big print's 86px row takes 60. On a phone the
   card stands (§1AC.2's column) and the row is `auto`, so height is free. */
:root { --tk-back-w: 52px; }
html.big-print { --tk-back-w: 60px; }
@media (max-width: 899px) {
  :root { --tk-back-w: 34px; }
  html.big-print { --tk-back-w: 40px; }
}
/* Taroky's card back inside a seat. The radius is the CARD's, not the
   seat's — it matches every other pcard on the table and would be wrong as a
   seat token. Left raw on purpose; C6's gate reads the unit's own block. */
.tok .dom-back .pcard.small.back {
  width: var(--tk-back-w); height: calc(var(--tk-back-w) * 1.435);
  border-radius: 4px; background: #A8362F; border-color: #5a1a15;
}
.pcard.selectable { cursor: pointer; }
.pcard.selectable:hover { transform: translateY(-4px); }
.pcard.dim { opacity: .38; }
.pcard.picked { outline: 3px solid var(--gold); transform: translateY(-6px); }

/* the action strip under your hand */
/* `#tk-actions` as well as `.tk-actions`, and that is the whole of why the bid
   choices sat hard left while everything else on the screen was centred: the
   template emits `<div id="tk-actions">` with no class on it, so this rule has
   never once applied. BACKLOG 1P.5. */
.tk-actions, #tk-actions { margin-top: 6px; text-align: center; }
#tk-actions .tk-act-lbl { text-align: center; }
/* The choices on one line, centred, with Pass among them rather than dropped
   onto a row of its own. */
#tk-actions { display: flex; flex-direction: column; align-items: center; gap: 8px; }
/* The bid row is the tallest thing this area ever holds; reserving it keeps
   the dock's top edge planted through the whole hand. Same rule as 1K.11 and
   the seat badges: a row that is sometimes there is worse than a row that is
   always there and sometimes empty. Pic 8, 23 Aug. */
#tk-actions { min-height: 98px; justify-content: center; }   /* 118 until 11 Sep; the four-player rows are shorter, and the twenty came off a 1366×768 laptop's overflow with sixteen cards in hand */
/* Every width, not just the desktop the report came from. The dock's height
   breathed with whatever the actions area held — bid buttons, the discard
   picker, nothing — and since the dock hangs from the window's bottom edge,
   every breath moved the hand above it. `tkuitest` caught it as a card that
   was never still long enough to click under load, which is the same fact a
   player feels as the table fidgeting. */
/* `.tk-btns`, not `#tk-actions > div`. The markup is #tk-actions >
   .tk-actions > (.tk-act-lbl + .tk-btns) — the first fix aimed one level too
   high, made the *wrapper* a flex row, and the label and the buttons wrapped
   into the left-hanging 2x2 block Mike photographed. One row, centred, with
   Pass among the choices rather than dropped below them. BACKLOG 1Q.4. */
.tk-btns { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }

/* ---------- Taroky's dock holds still ---------- §1P.5

   Measured 22 Aug: about 90px when you are sitting out a hand and about 545
   when you are holding sixteen cards and bidding, so the felt above it moved
   by half the screen between one hand and the next. Mike: *"pick a size once
   you have the arrangement done and stick with that."*

   Reserved from the largest state — the hand plus the bid row — the same way
   1K.11 reserves the phone hand's full height. */
#screen-taroky .my-dock { min-height: 300px; }
@media (max-width: 899px) { #screen-taroky .my-dock { min-height: 250px; }}


/* Sixteen cards in one row on a desktop, which is what there is width for, and
   4x4 on a phone — Mike's own fallback, named in advance on 22 Aug rather than
   invented at the time. One row at 430px would need 24px cards, which is
   smaller than the thing this release is making bigger. */
@media (min-width: 900px) {
  /* Eight across, two deep. The first attempt was `flex-wrap: nowrap` — a
     flex property on what is actually a grid (`.my-hand` is
     `repeat(4, max-content)` on a desktop), so it never did anything and
     sixteen cards stacked four-by-four off the bottom of the window. 8x2 is
     the fallback Mike named in advance for exactly this case: one row of
     sixteen at 72px is ~1190px, which is wider than the window he plays in.
     BACKLOG 1Q.3. */
  /* `#screen-taroky .tk-hand`, because bare `.tk-hand` ties `.my-hand` on
     specificity and this section sits earlier in the file — the base
     four-column rule won the tie on source order and the first version of
     this fix silently lost. `csslint` cannot catch a tie between two
     *different* selectors that land on one element. */
  #screen-taroky .tk-hand { grid-template-columns: repeat(8, max-content); gap: 5px; }
}
@media (max-width: 899px) {
  .tk-hand {
    display: grid; grid-template-columns: repeat(4, max-content);
    justify-content: center; gap: 5px;
  }
  /* Six across, then eight on a phone (below). Four across at 68px stood in
     four rows with a card's width of felt between the columns, and the bid
     row's Pass button was below the window (§1AC.8, screenshot at 430):
     *"there is a ton of green space in between the card columns, let's use
     it."* Sixteen cards in two rows of eight at the 38px phone card is 332px
     — inside 360 — and half the height of four rows. The id wins the tie
     with `.my-hand`'s own phone grid, the same way the desktop rule above
     had to. */
  #screen-taroky .tk-hand { grid-template-columns: repeat(6, max-content); gap: 4px; min-height: 0; }
  /* The rest of what a phone has to give up so the bid row is on the glass —
     measured at 360x780 after the eight-across hand: still 83px over
     ordinary and 188 with Big print on. Every band below was reserved for
     the tallest state on a tall screen (§1AB.2's diagnosis, again), so each
     comes down to what its contents need at this deck:
       - the status line loses its desktop 70px box (`.status-line { height:
         70px }`) and sits on its one sentence;
       - the seat's count goes BESIDE its card, not under it — §1AC.2's
         column is for the domino seats, and a 49px-tall card with a count
         under it made a 176px seat row on a 780px phone;
       - the seat row's padding and the hand's reserved height (`.my-hand`
         holds two rows of a BONE's height for §1V.9, which Taroky's cards do
         not need) come off. */
  /* (The status line's own rule stood here and moved to the lane block by
     the felt — it is hidden at every width now, §1AC.20/§1AC.26.) */
  #screen-taroky .tk-seats { padding: 4px 6px; }
  #screen-taroky .tok:not(.tok-h) .hand { flex-direction: row; gap: var(--sc-gap-hand-col); }
}
.tk-act-lbl { font-size: 15px; opacity: .9; margin-bottom: 8px; color: var(--cream); text-align: center; }
/* Centred like everything else that speaks to you. It sat hard against the
   left edge under the score line, which read as a fragment that had not been
   placed rather than as a sentence somebody wrote. BACKLOG 1M.12. */
#screen-taroky .tk-empty, #screen-taroky .dock-note { text-align: center; display: block; width: 100%; }
.tk-btns { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin-bottom: 8px; }
.tk-btn {
  background: #fbf7ec; color: #23180f; border: none; border-radius: 9px;
  padding: 9px 14px; font-size: 15px; font-weight: bold; font-family: inherit;
  display: flex; flex-direction: column; align-items: center; gap: 2px; min-width: 96px;
}
.tk-btn small { font-size: 10.5px; font-weight: normal; opacity: .65; }
.tk-btn em { font-style: normal; color: #8a5a12; }
.tk-btn.pass { background: #7a6c55; color: var(--cream); }
.tk-btn.bonus { background: rgba(0,0,0,.35); color: var(--cream); border: 1px solid rgba(255,255,255,.2); }
.tk-btn.bonus.on { background: var(--gold); color: var(--btn-text); border-color: var(--gold); }
.tk-btn.bonus.on small, .tk-btn.bonus.on em { color: inherit; opacity: .8; }

.tk-bonus-lines { margin: 8px 0 4px; font-size: 14px; }
.tk-bonus-lines div { padding: 3px 0; color: var(--cream); }
.tk-bonus-lines .made { color: var(--pill-hi); }
.tk-bonus-lines .missed { opacity: .6; }
.tk-bonus-lines span { float: right; font-weight: bold; }

.tk-draft-tag {
  font-size: 12px; letter-spacing: 1px; text-transform: uppercase;
  background: var(--gold); color: var(--btn-text);
  border-radius: 10px; padding: 3px 9px; vertical-align: middle; margin-left: 8px;
}
.tk-intro { background: rgba(0,0,0,.25); border-radius: 10px; padding: 12px 14px; margin: 10px 0 16px; }
.tk-intro p { font-size: 14px; line-height: 1.6; color: var(--cream); opacity: .9; margin-bottom: 8px; }
.tk-intro p:last-child { margin-bottom: 0; }

@media (max-width: 480px) {
  .pcard { width: 38px; height: 55px; }
  .pcard .pc-rank { font-size: 13px; }
  .pcard .pc-pip { font-size: 12px; }
  .pcard.small { width: 16px; height: 24px; }
  /* 120, sized to its contents at this deck: a played card is 55, its label
     18, the band's padding 24. The 20 it had spare was the Pass button's. */
  .tk-trick { min-height: 120px; gap: 10px; }
  .tk-btn { min-width: 78px; padding: 8px 10px; font-size: 14px; }
  #screen-taroky .tk-hand { grid-template-columns: repeat(8, max-content); }
  /* The trick band and the action reserve at the desktop's shortest tier
     (see the §1AB.2 ladder), and the hint line under the hand goes: on a
     phone it wrapped to two lines, and every word of it is already on the
     buttons themselves — *take 3 of the talon · take 2, braver · take 1,
     braver still*. */
  #screen-taroky .tk-trick { min-height: 104px; }
  #screen-taroky #tk-actions { min-height: 96px; }
  #screen-taroky .tk-act-lbl { display: none; }}


/* ---------- the official marks ----------
   Mark D (the wordmark with the URL) heads every page; Mark E (the wordmark
   with the flag tile) heads every table. They're the same outlined letterforms
   the embroidery and the merch use, so nothing drifts. Three colours are wired
   to the theme; the flag tile keeps its real colours everywhere. */
.brand-mark { display: block; width: 100%; height: auto; }
h1.brand {
  --wm-ink: var(--table-text);
  --wm-star: var(--brand);
  --wm-sub: var(--table-text);
  width: min(560px, 86vw); margin: 0 auto;
  font-size: 0; line-height: 0;          /* the mark is the heading */
  filter: drop-shadow(0 2px 0 rgba(0,0,0,.28));
}
h1.brand .brand-d { opacity: 1; }
/* the URL line sits quieter than the wordmark above it */
h1.brand [stroke="var(--wm-sub)"], h1.brand [fill="var(--wm-sub)"] { opacity: .62; }

/* Mark E.1, the flag lockup, is what the home screen carries since 18 Aug.

   It takes none of the variables above, and that is the point: it ships as a
   light cut and a dark cut, and `homeMark()` picks between them by theme. A
   Texas flag that recoloured with the tablecloth would not be a Texas flag, so
   the mark stopped following the room and the room started choosing the mark.
   The colour-coded star went with that, deliberately.

   Its viewBox is 1659x350 against mark D's 1258x300 — wider and shorter in
   proportion — so it is given its own width rather than inheriting one drawn
   for a different shape. */
h1.brand .brand-e1 { display: block; width: 100%; height: auto; opacity: 1; }
h1:has(.brand-e1).brand { width: min(620px, 90vw); }

.table-brand {
  /* the strip behind it is darkened on every table, so the mark goes light
     here even where the tabletop itself is pale */
  --wm-ink: var(--cream);
  /* Full strength since 4 Sep — *"seems like the full mark is a little
     shaded and not full color … want it natural colors and not faded in
     the header"*. It was .8, which took the flag's navy and red down with
     the cream it was meant to soften. */
  display: block; line-height: 0; opacity: 1;
  flex: 0 1 auto; min-width: 0;
  width: min(300px, 44vw); max-width: 100%;
}
.table-brand .brand-e { display: block; width: 100%; height: auto; }
@media (max-width: 480px) {
  h1.brand { width: 88vw; }}

/* Headings take the slab serif so the type near the marks belongs with them. */
h1, h2, h3, h4,
.game-card h2, .rules-panel h2, .panel h3, .rb-title, .big-btn, .badge,
.wn-rel h4 { font-family: var(--brand-font); }
.big-btn, .badge { letter-spacing: .4px; }

/* Slate: the cards are offcuts of the same stone, so they carry the same grain
   and the same split edge. */
body.theme-slate .game-card {
  position: relative;
  background-color: #2b3034;
  background-image:
    var(--slate-grain),
    radial-gradient(ellipse 70% 50% at 26% 20%, rgba(130,142,152,.15), rgba(0,0,0,0) 70%),
    linear-gradient(158deg, #3a4045 0%, #2b3034 45%, #1e2225 100%);
  background-size: 7px 7px, 11px 11px, auto, 100% 100%, 100% 100%;
  border-color: #14181a;
  border-radius: 8px;
  box-shadow:
    inset 0 1px 0 rgba(190,204,214,.16),
    inset 0 -2px 6px rgba(0,0,0,.5),
    0 5px 14px rgba(0,0,0,.55);
}
/* the chipped, slightly lighter edge where the stone was split */
body.theme-slate .game-card::before {
  content: ''; position: absolute; inset: 0; border-radius: 8px; pointer-events: none;
  border-top: 1px solid rgba(200,212,222,.1);
  border-left: 1px solid rgba(200,212,222,.07);
  box-shadow: inset 0 0 22px rgba(0,0,0,.35);
}
body.theme-slate .game-card h2 { text-shadow: 0 1px 2px rgba(0,0,0,.6); }

/* The theme's props (cups, plates, the flyer taped up) belong on the home
   screen. Over a table they sit on top of the bones and the scorepad, so they
   come off once a game is running — the tabletop itself still carries the
   theme. */
/* The room doesn't stop existing when the game starts. The carved names on the
   Northgate rail and the notes on the slate were hidden at the table, which
   left the two most characterful rooms looking like plain colour the moment you
   sat down — the one moment you're actually in them.

   What stays hidden in game is the furniture: the pitcher, the cups, the fish
   plate, the flyer. Those are objects sitting on a table, and there is a game
   on that table now. The writing is *in* the surface — cut into the wood,
   chalked on the slate — so it belongs under the bones the same way the mark
   does. */
body.in-game #decor-layer .decor-svg,
body.in-game #decor-layer .flyer { display: none; }
body.in-game #decor-layer .carve { opacity: .82; }
body.in-game #decor-layer .chalk { opacity: .5; }

/* ---------- rule presets and the special contracts ---------- */
.preset-head {
  font-size: 13px; letter-spacing: .09em; text-transform: uppercase;
  color: var(--heading, #e0aa3e); opacity: .85;
  margin: 14px 0 6px; font-weight: bold;
}
.preset-row { display: flex; gap: 8px; margin: 4px 0 8px; flex-wrap: wrap; }
.preset-btn {
  flex: 1 1 0; min-width: 0; background: rgba(0,0,0,.32); color: var(--cream);
  border: 1px solid rgba(255,255,255,.18); border-radius: 10px;
  padding: 9px 10px; font-family: inherit; font-size: 15px; font-weight: bold;
  display: flex; flex-direction: column; gap: 3px; text-align: center;
}
.preset-btn small { font-size: 10px; font-weight: normal; opacity: .62; line-height: 1.3; }
.preset-btn:hover { border-color: var(--pill-hi); }
.preset-note { font-size: 12.5px; opacity: .75; margin-bottom: 12px; line-height: 1.55; }
.preset-note a { color: var(--pill-hi, #e0aa3e); }

/* Three presets across a phone gives each about 99px, and "Tournament" is a
   single unbreakable word that needs 107. Measured 16 Aug at 390px: content
   107 against a 97px box, so the word sat outside its own button. `min-width: 0`
   above is what lets the button shrink under its content — it is right, and
   the answer is not to take it away but to stop asking three of them to share
   a phone.

   Two up, and the odd one out takes the whole row rather than leaving a hole
   beside it. Same breakpoint and same shape as the rule book's game switch, so
   the two screens agree with each other. */
@media (max-width: 560px) {
  .preset-row { display: grid; grid-template-columns: 1fr 1fr; }
  .preset-row > .preset-btn:last-child:nth-child(odd) { grid-column: 1 / -1; }
}

/* The AI tiers under the difficulty picker. Prose that happens to have three
   parts, so it stays light. The matching lists inside *How it works* used to
   share this treatment; they are on `how-the-bots-play.html` now and take the
   page's own type. */
.ai-tiers { font-size: 12.5px; opacity: .78; line-height: 1.6; margin: 2px 0 14px; }
.ai-tiers b { opacity: 1; }
.ai-tiers a { color: var(--pill-hi, #e0aa3e); white-space: nowrap; }

/* A contract is a different kind of bid from a number, so it looks like one. */
.bid-grid button.special {
  background: #4a3a63; color: var(--cream);
  display: inline-flex; flex-direction: column; gap: 1px; line-height: 1.15;
}
.bid-grid button.special small { font-size: 10px; opacity: .7; font-weight: normal; }
.bid-grid button.splash { background: #2f5568; }
.bid-grid button.plunge { background: #6b3550; }
.bid-grid button.sevens { background: #3f5a35; }
.bid-spec-note {
  font-size: 12.5px; line-height: 1.55; opacity: .8;
  background: rgba(0,0,0,.28); border-radius: 8px; padding: 8px 11px; margin: 6px 0 4px;
}


/* A rule that is a set rather than a choice: more than one can be on at once,
   so the buttons latch instead of behaving like a radio. */
.rule-row.wrap { flex-wrap: wrap; }
.rule-row.wrap .rule-note {
  flex: 1 0 100%; font-size: 12px; opacity: .7; margin: 6px 0 0; line-height: 1.5;
}
.seg.multi button.on { position: relative; }
.seg.multi button.on::after {
  content: '✓'; font-size: 10px; margin-left: 5px; opacity: .8;
}
/* The Nel-O declaration is three sentences, not seven suits — give them room. */
.trump-grid.nello-grid { grid-template-columns: 1fr; }
.trump-grid.nello-grid button {
  display: flex; flex-direction: column; gap: 3px; padding: 11px 12px; text-align: center;
}
.trump-grid.nello-grid small { font-size: 11px; font-weight: normal; opacity: .68; }

/* ==========================================================================
   BIG PRINT

   Not a zoom. Zooming a page this dense just pushes the score pad off the
   bottom of a phone. What actually helps somebody who can't read the pips is
   bigger *bones*, bigger *numbers*, more contrast between the pip and the tile,
   and no dimming — so the three things this changes are size, weight and
   contrast, and everything that greys something out is switched off.
   ========================================================================== */
html.big-print { font-size: 118%; }

html.big-print .domino.h { width: 108px; height: 54px; }
html.big-print .domino.v { width: 54px; height: 108px; }
html.big-print .domino .half { width: 54px; height: 54px; }
html.big-print .pips { fill: #14100a; }
html.big-print .domino.small.h { width: 70px; height: 35px; }
html.big-print .domino.small.v { width: 35px; height: 70px; }
html.big-print .domino.small .half { width: 35px; height: 35px; }
/* Your own hand is the exception. Seven bones have to fit across a phone on one
   row — wrapping them onto two pushes the score pad off the bottom, which costs
   more than the extra millimetres gain. So the hand grows modestly and the pips
   inside it do the real work: bigger, darker, and further apart. */
/* Big print has to be *bigger* than ordinary print, which sounds too obvious to
   write down — and has now been wrong twice, both times for the same reason:
   somebody grows the ordinary hand and this number sits here unread. It was 64
   against an ordinary 58, then 64 against an ordinary 82.

   It is one number now, and it must stay above `--bone` at every width where
   both apply. `fitcheck.js` measures both print sizes at four widths and fails
   the build if four bones plus their gaps stop fitting — but nothing there can
   tell that Big print has quietly become the small one, so the rule lives here
   in words: **whatever `--bone` is, this is larger.**

   Since 15 Aug the hand takes the whole width, so this is a *higher cap* on
   the same measurement rather than a second hand-written number. Where the
   screen allows it, Big print is larger; on a narrow phone four across is
   limited by the glass and both arrive at the same size, which is arithmetic
   rather than a regression. */
html.big-print .my-dock { --bone: min(112px, calc((100cqi - 18px) / 4)); }
/* And Big print's own container sum, or the ordinary one below it wins on the
   hand and the two sizes collapse onto each other — which `fitcheck` refuses,
   because the widest phone is the one place a stale Big print number would
   ever show up. §1E.1. */
html.big-print .my-hand { --bone: min(112px, calc((100cqi - 18px) / 4)); }
/* Written out again rather than inherited, because `html.big-print .domino.h`
   further up sets 108px for bones everywhere else on the page and out-ranks a
   plain `.my-dock .domino.h` — one type plus three classes against three
   classes. The dock needs its own size and has to say so at a specificity that
   wins. The *number* still lives in one place; only the plumbing repeats. */
html.big-print .my-dock { --dock-bone-h: calc(var(--bone) / 2); }
html.big-print .my-dock .domino.h { width: var(--bone); height: calc(var(--bone) / 2); }
html.big-print .my-dock .domino.v { width: calc(var(--bone) / 2); height: var(--bone); }
html.big-print .my-dock .domino .half { width: calc(var(--bone) / 2); height: calc(var(--bone) / 2); }
/* **Bigger than ordinary print, not smaller.** This said 4px while the
   ordinary hand said 12 — so every one of the three increases asked for and
   shipped between v1.72.0 and v1.73.0 was invisible to the one profile that
   asked for them, and the request came back a fourth time in capitals. The
   note further up this block says Big print must be *bigger*; it was written
   about the bone and the gap was never held to it.

   Big print is not a font size. It is a second layout, and every number in it
   has a second value that can go stale on its own. `fitcheck` now measures the
   gap at both print sizes for exactly this reason. BACKLOG 1U.7. */
html.big-print .my-hand { gap: var(--hand-gap-big); }

/* Nothing is greyed out. A hint you can't see is worse than no hint, so legal
   plays are marked by a ring rather than by dimming everything else. */
html.big-print .domino.dim { opacity: 1; box-shadow: 0 2px 4px rgba(0,0,0,.45); }
/* Neutral ink, NOT gold — and this was a real bug for anyone playing in Big
   print. The rings above are a language: gold solid says count, red dashed
   says trump, and both are literals precisely so a hint never changes
   meaning by room. This rule then painted *every legal bone* in the same
   gold as the count ring, so a Big print hand showed "you may play this" and
   "this carries count" in one indistinguishable colour — Mike, three times,
   most plainly as "why all yellow?".
   
   It stays a ring rather than going away: Big print exists for people who
   need more than the .38 dim, and taking the affordance out to fix a colour
   would be trading one person's clarity for another's. Dark ink on an ivory
   bone reads as a defined edge and claims none of the hint colours. */
html.big-print .domino.selectable {
  outline: 3px solid rgba(35, 24, 15, .5); outline-offset: 1px;
}
html.big-print .domino.ring-count::after,
html.big-print .domino.ring-trump::after,
html.big-print .domino.ring-both::after,
html.big-print .domino.ring-both::before {
  inset: -5px; border-radius: 10px; border-width: 5px;
}
html.big-print .domino.ring-count::after { border-color: rgba(224,170,62,.98); }
html.big-print .domino.ring-both::before { border-color: rgba(224,170,62,.98); }
html.big-print small,
html.big-print .rules-sub,
html.big-print .sub { font-size: 0.95em; opacity: .92; }
html.big-print .rule-name small { opacity: .85; }
html.big-print .ti-lbl { opacity: .95; }

/* Touch targets. The WCAG floor is 44px and several of these were under it. */
html.big-print .seg button,
html.big-print .bid-grid button,
html.big-print .trump-grid button,
html.big-print .preset-btn { min-height: 46px; font-size: 1.02em; }
html.big-print .big-btn, html.big-print .ghost-btn { min-height: 52px; }

/* The scorepad is the thing people squint at hardest. */
html.big-print .score-pad { transform: scale(1.14); transform-origin: top center; }

/* Table talk. A definition list, because that is what this is — and it reads
   better as term-above-definition than as a wall of bolded run-on text. */
dl.gloss { margin: 10px 0 0; }
dl.gloss dt {
  font-weight: bold; color: var(--pill-hi, #e0aa3e);
  margin-top: 12px; font-size: 15px;
}
dl.gloss dd { margin: 2px 0 0; line-height: 1.55; opacity: .92; }

/* ---------- Moon ----------
   Three seats, so the table is 42's minus one: you at the bottom and two across
   from you rather than one opposite and two on the flanks. */
.mn-seats { display: flex; justify-content: space-around; gap: 10px; padding: 6px 8px 2px; }
/* Moon seats two opponents, each one a `.seat-unit`. Nothing about the seat
   is described here any more; this row only says how the two share the band. */
/* Moon's played bones, sized like a bone you are meant to read rather than
   glance at. 1F.5 gave 42 one size for everything that is not your hand and
   was scoped to #screen-42 on purpose; Moon never got an equivalent, so its
   two played bones were about a quarter of the ones in your own hand.
   BACKLOG 1P.2. */
#screen-moon .mn-trick .domino.h { width: 92px; height: 46px; }
#screen-moon .mn-trick .domino.v { width: 46px; height: 92px; }
#screen-moon .mn-trick .domino .half { width: 46px; height: 46px; }
@media (max-width: 560px) {
  #screen-moon .mn-trick .domino.h { width: 74px; height: 37px; }
  #screen-moon .mn-trick .domino.v { width: 37px; height: 74px; }
  #screen-moon .mn-trick .domino .half { width: 37px; height: 37px; }}
/* Moon on a phone: the sentence above the bones, never across them.

   The stack is anchored to the middle of the felt (see `.status-stack`), and
   on Moon the middle of the felt is where the two played bones are — so
   "Your play. (Ed needs 2 more of 4)" was drawn straight over them and their
   labels (§1AC.7, screenshot at 430). 42 solved the same collision by giving
   the sentence a cell of its own (`.tp-mid`); Moon's felt is a flex column,
   so the stack simply takes its place in the flow between the seats and the
   trick, with `order` saying where. The scorepad goes with it — *"for Mobile
   just get rid of the bottom scorepad. no need"* — the dock's SCORE chip
   already says the same numbers. The desktop keeps both as they were. */
@media (max-width: 899px) {
  #screen-moon .status-stack {
    position: static; transform: none; left: auto; top: auto;
    width: auto; max-width: 100%; order: 1; padding: 2px 8px 0;
  }
  #screen-moon .mn-seats { order: 0; }
  #screen-moon .mn-trick { order: 2; }
  #screen-moon .me-plate { order: 3; }
  /* `#screen-moon .dock-pad { display: none }` stood here for the phone;
     Moon has no scorepad on any screen since 4 Sep — the rule is global,
     near the foot of the file. */
}
/* `.mn-tricks` stood here — Moon's count of tricks taken, a pill of its own
   wedged into the name row, which is why it moved the name every time
   somebody took one. It is the unit's count slot now, and it keeps the word:
   a bare 0 beside a name says nothing on the one table where the whole bid is
   a number of tricks. BACKLOG 1P.11, 1M.13. */
.trick-area.mn-trick {
  display: flex; align-items: center; justify-content: center; gap: 16px;
  flex: 1 1 auto; min-height: 90px; padding: 6px 0;
}
.mn-slot { display: flex; flex-direction: column; align-items: center; gap: 3px; }
.mn-slot .who { font-size: 11px; opacity: .7; }
.mn-empty { opacity: .45; letter-spacing: .04em; }

/* The pad is a running column each, the way it is kept on paper — and a
   negative is written as a negative, because losing your bid is visible. */
.mn-pad-rows { display: flex; flex-direction: column; gap: 4px; padding: 8px 10px; }
.mn-pad-row {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; font-size: 15px;
}
.mn-pad-name { color: #23324a; font-weight: bold; }
.mn-pad-score { color: #23324a; font-weight: bold; font-size: 17px; }
.mn-pad-row.behind .mn-pad-score { color: #a3231a; }
.score-pad.mn-pad { min-height: 0; }

/* Your own photo, as the back of your bones. It is set as a CSS variable at
   load so every tile on screen picks it up at once without re-rendering. */
.back-art.own-photo {
  background-image: var(--own-photo, none);
  background-size: cover;
  background-position: center;
  border-radius: 3px;
}

/* An aside in the rule book: a note about how other tables do it, set apart
   from the rules themselves so nobody mistakes it for one. */
.rb-note {
  display: block; margin-top: 10px; padding: 9px 12px;
  border-left: 3px solid var(--pill-hi, #e0aa3e);
  background: rgba(0,0,0,.16); border-radius: 0 8px 8px 0;
  font-size: 13px; line-height: 1.6; opacity: .85;
}

/* ==========================================================================
   FOCUS, MOTION AND TOUCH TARGETS

   Three things that were measured and found wanting rather than assumed:

   - The browser's default focus ring here computed to a 1px near-black outline,
     which on a dark green table is invisible. Somebody navigating by keyboard
     could not see where they were.
   - Nothing honoured prefers-reduced-motion, so a player who has asked their
     phone to stop animating things still got every transition.
   - Two buttons measured 20 and 21 pixels tall against the 24 minimum.
   ========================================================================== */

/* :focus-visible rather than :focus, so a mouse click doesn't leave a ring
   behind but a Tab does. Gold on any of the four tables, with a dark halo so it
   stays visible on the cream surfaces too. */
:focus-visible {
  outline: 3px solid #ffd166;
  outline-offset: 2px;
  border-radius: 6px;
  box-shadow: 0 0 0 5px rgba(0, 0, 0, .55);
}
/* Domino faces are light, so the halo goes the other way round on those. */
.domino:focus-visible, .back-opt:focus-visible {
  outline-color: #1c4f8a;
  box-shadow: 0 0 0 5px rgba(255, 255, 255, .65);
}

/* Someone who has asked their system to reduce motion means it. Everything
   still works — the trick pause, the toasts, the panels — it just stops
   sliding and fading. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .domino.selectable:hover { transform: none; }
}

/* WCAG 2.5.8 puts the floor at 24x24 including padding. */
.exit-btn, .rules-btn { min-height: 26px; padding-top: 4px; padding-bottom: 4px; }

/* ---------- drawing for the first shake ----------
   Only a tournament table does this, and it happens once a game, so it is a
   plain read-it-and-tap panel rather than anything animated. The winning bone
   is marked because with four bones on screen the highest is not instantly
   obvious to somebody still learning which end counts. */
.sd-round { margin: 12px 0 4px; }
.sd-lbl { font-size: 12px; opacity: .65; margin-bottom: 5px; letter-spacing: .04em;
          text-transform: uppercase; }
.sd-row { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.sd-seat { display: flex; flex-direction: column; align-items: center; gap: 4px;
           padding: 7px 8px 5px; border-radius: 10px; min-width: 62px;
           border: 2px solid transparent; }
.sd-seat.sd-high { border-color: var(--pill-hi, #e0aa3e);
                   background: rgba(224, 170, 62, .12); }
.sd-who { font-size: 12px; opacity: .85; max-width: 76px; overflow: hidden;
          text-overflow: ellipsis; white-space: nowrap; }
.sd-result { margin: 14px 0 4px; font-weight: bold; }

/* ---------- Nello: tricks stacked in piles of two ----------
   Each pile overlaps the one before it, the way a real stack of won tricks
   sits, and the newest pile is the open one because that is the one still
   being added to and the last trick has to stay readable. */
.tr-piled { display: flex; flex-direction: column; gap: 0; }
.tr-pile { position: relative; padding-left: 15px; margin-top: -5px;
           border-top: 1px solid rgba(224, 170, 62, .22);
           border-radius: 7px 7px 0 0; background: rgba(0, 0, 0, .16); }
.tr-pile:first-child { margin-top: 0; }
.tr-pile-open { margin-top: 2px; background: rgba(224, 170, 62, .09);
                border-top-color: rgba(224, 170, 62, .5); }
/* Closed piles sit lower and dimmer — present, countable, not competing with
   the trick that just happened. */
.tr-pile:not(.tr-pile-open) .tr-pile-rows { opacity: .62; }
.tr-pile-n { position: absolute; left: 3px; top: 4px; font-size: 10px;
             opacity: .55; font-variant-numeric: tabular-nums; }

/* ---------- a bone you have moved to with the keyboard ----------
   The colour of the ring is set further up, where the light-faced elements are
   handled. What is needed here is height: bones sit in a tight overlapping row,
   so a ring drawn outside one gets clipped by its neighbour. Lifting the
   focused bone out of the stack keeps the whole ring visible. */
.domino:focus-visible { position: relative; z-index: 5; outline-offset: 1px; }
.domino[role="button"] { cursor: pointer; }

/* A phone number half rubbed out. The digits after the area code are gone the
   way they'd actually go — a sleeve through them, not a black bar — so the
   note reads as something somebody wrote down and half wiped off, which is
   the point. It also means there is no number on the table to dial. */
.chalk-smudge { position: relative; }
.chalk-rub {
  display: inline-block; width: 4.6em; height: .82em; margin-left: .12em;
  vertical-align: -.08em;
  background:
    repeating-linear-gradient(96deg,
      rgba(238, 236, 226, .30) 0 3px,
      rgba(238, 236, 226, .06) 3px 7px);
  filter: blur(1.6px);
  border-radius: 3px;
  opacity: .8;
}

/* ---------- the Bonus star on the Taroky card ----------
   Taroky used to carry a "Bonus" kicker above its heading, which pushed the
   word Taroky a line lower than Texas 42, Fives and Moon — the four headings
   no longer read across. The star is pinned to the corner instead, out of the
   flow, so every card's title starts at the same height. Twelve points because
   that is the Texas star doubled, and because a six-pointed one reads as a
   sheriff's badge. */
.game-card { position: relative; }
.bonus-star {
  position: absolute; top: -13px; right: -13px;
  width: 62px; height: 62px;
  color: var(--gold, #e0aa3e);
  display: grid; place-items: center;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, .45));
  transform: rotate(-9deg);
  pointer-events: none;
}
.bonus-star svg { position: absolute; inset: 0; }
.bonus-star b {
  position: relative;
  font-size: 10px; letter-spacing: .02em; font-weight: 800;
  color: var(--bonus-word, #2a1a06);
}
/* On the dark wood and the Hall's steel the gold star is dark enough that near-
   black lettering disappears into it. White cuts through on both. The green and
   the slate keep the dark word, which reads better against their brighter
   star. */
body.theme-northgate { --bonus-word: #ffffff; }
body.theme-kc { --bonus-word: #ffffff; }
body.theme-northgate .bonus-star b, body.theme-kc .bonus-star b {
  text-shadow: 0 1px 2px rgba(0, 0, 0, .5);
}
/* Nothing should overlap the card's own edge on a narrow screen. */
@media (max-width: 460px) {
  .bonus-star { top: -9px; right: -6px; width: 54px; height: 54px; }
  .bonus-star b { font-size: 9px; }
}

/* Who's playing is the first decision at a table and it was reading like the
   sixth setting down a list. It gets the emphasis it earns — this is the row
   that turns the app from a solo game into four people round one phone, and
   nobody was finding it. */
.rules-panel .seat-row {
  border: 1px solid rgba(224, 170, 62, .34);
  background: rgba(224, 170, 62, .07);
  border-radius: 10px;
  padding: 10px 12px;
  margin-bottom: 12px;
}

/* ==========================================================================
   The table, second pass — 9 August

   Five complaints, one cause between them: everything on this table was laid
   out relative to everything else, so anything that changed size moved four
   other things. A bone leaves a hand and the seat re-centres; the trick record
   grows and it walks into the west player's bones; the info strip sits at two
   opposite ends of a line so the eye has to travel to read one score.

   The fix throughout is reservation. Space is claimed up front at the size it
   will eventually need, and things move inside it rather than pushing on each
   other. A table that doesn't twitch is worth more than a table that packs
   tightly.
   ========================================================================== */

/* ---------- the wordmark across the top ----------
   Wider without being taller. The mark is one SVG at a fixed 6.4:1, so width
   and height move together — the bar is given a pinned min-height and thinner
   padding, and the mark grows into the room that frees up. */
/* One width, declared on the bar and read by the mark AND by the chips
   beside it. The chips' words are outlined marks now (Mark H, `chipWord`),
   and their cap height is tied to the wordmark's — measured at 430 the
   wordmark's capitals are 6.4% of its width — so the three tiers below
   size all of it at once. §1AC.15. */
/* Capped at 16px: tied to the wordmark the words were 40px caps on a 1963
   desktop and the loudest things on the screen (§1AC.27). On a phone the
   tie still governs — 12px caps at 430, exactly the wordmark's. */
.table-top { --brand-w: min(340px, 58vw); --chip-cap: min(calc(var(--brand-w) * .064), 16px); }
.table-brand { width: var(--brand-w); }
@media (min-width: 900px) {
  /* Twice the height — Mike, 26 Aug. Width is the lever; the SVG keeps its
     own ratio. */
  .table-top { --brand-w: min(560px, 36vw); }}
@media (max-width: 620px) { .table-top { --brand-w: min(230px, 44vw); }}

/* The chip words (Mark H, `chipWord`): the drawing's box is the cap height,
   so its height IS the cap height, tied to the wordmark's. The chip grows
   with it — `height: auto` here beats §1AC.1's fixed 38 by source order at
   equal specificity, and the pair still match because both are one line of
   the same drawing. TABLE RULES is two words; they sit side by side, and
   stack on a phone the way the text used to wrap. */
.word-mark { display: inline-flex; align-items: center; line-height: 0; }
.word-mark svg { display: block; height: var(--chip-cap, 12px); width: auto; }
.word-mark-text { line-height: 1; }
/* The gap between TABLE and RULES is a share of the cap height, not of the
   button's 15px em — at a 29px cap on a desktop an em's .4 was invisible
   and the two words touched. */
.table-top .exit-btn { height: auto; min-height: 38px; padding: 8px 18px; gap: calc(var(--chip-cap, 12px) * .5); }
.table-top .rules-btn { flex-direction: row; }
@media (max-width: 430px) {
  /* Two words stacked, and BOTH chips take the stacked height, so the pair
     stay equal (§1AC.1): measured 38 against 42 with only RULES stacked. */
  .table-top .rules-btn { flex-direction: column; gap: 3px; }
  .table-top .exit-btn { min-height: 42px; }
}
/* On the footer bar, and in the same face: the bar's own height is the
   measure there, not the wordmark's. */
.back-sticky .word-mark svg { height: 14px; }

/* ---------- the info strip, on one centred line ----------
   Who you are, the game score, the hand score and the bid used to sit at
   opposite ends of the dark bar with a gulf between them. They are one thought
   and they read as one line, centred directly over your bones. */
.dock-head { justify-content: center; flex-wrap: wrap; gap: 8px; }
.dock-name { flex: none; }
.table-info { flex: 0 1 auto; justify-content: center; }

/* ---------- the bottom band, in three ----------
   Won tricks on the left, you in the middle, the talk on the right. They used
   to float over the felt in its two bottom corners, which read as a panel
   sitting on the table — and the record was forever a pixel away from landing
   on the west player's bones. The green is where the game happens; the dark
   band underneath is where the record of it belongs.

   The two sides are fixed-width columns so the middle never shifts as the
   record fills, and the record fills upward inside its column rather than
   growing it. */
:root { --dock-side: clamp(210px, 21vw, 340px); }

/* The felt is everything above the bottom band, and it takes whatever height is
   left over. Without this it sizes to its contents and the band floats in the
   middle of the screen with green underneath it — which is what happened the
   first time this section was rewritten and the rule was lost with it. */
.felt { position: relative; flex: 1; display: flex; flex-direction: column; min-height: 0; }
/* ---------- shrink the table ---------- §1M.8, §1P.8

   Moon and Taroky have far less to put on a felt than 42 does — two or three
   seats, a couple of played cards, and no trick record — so on a tall screen
   the felt stretched and the middle was simply empty: measured 22 Aug at about
   600px of nothing on a phone and 300 above the played bones on a desktop.

   Decided with Mike: shrink the table rather than fill it. Giving the middle
   something to hold — the widow, the talon, the last trick — stays available
   and is deliberately not being done now.

   So the felt takes its content's height and stops there, and the table is
   centred in what is left rather than stretched across it. `justify-content`
   on the wrapper is what keeps the dock under the felt instead of pinned to
   the bottom of the window with the gap merely moved. */
/* The shrink experiment is over, and both of its variants failed the same
   way. Centring the shrunk table split the leftover above and below it;
   top-stacking it stranded the dock mid-window with dead green underneath —
   Mike, 23 Aug, in capitals. The felt fills again (base .felt flex: 1), the
   dock sits on the window's bottom edge like 42's, and the emptiness that
   started all this is answered by scale instead: the seats and their piles
   are big now, because seeing the backs is part of the charm of the game.
   The plates stay in flow and the trick bands keep their reserved heights —
   those parts of the shrink eras were right. BACKLOG 1Q.5, 1M.8. */
/* The 300px floor is 42's — four seats round a felt need it and Moon's two do
   not, so on Moon it was 300px of guaranteed green. */
#mn-felt .seats-mid { min-height: 0; }

.my-dock {
  display: grid;
  /* The middle has right of way. The sides take their width when there is room
     and give it up when there isn't, because a hand of seven bones wrapping
     onto two rows to make space for an empty chat panel is the wrong trade. */
  grid-template-columns:
    minmax(150px, var(--dock-side)) minmax(0, 1fr) minmax(150px, var(--dock-side));
  align-items: stretch; gap: 12px;
}
/* Each child pinned to its column. The review (`body.reviewing`) hides both
   side columns, and with nothing in column 1 the grid auto-placed the middle
   column INTO it — 150px wide — so the scorepad came up in the bottom-left
   corner at the width of its labels with the mark drawing hanging off the
   paper, every hand, at every width under 1550 (Mike, 9 Sep, §1AF.24; the
   1550+ tier places its children by name and never had it). A hidden column
   is still that column. And under the review the pad goes to the right edge
   of its column, where it sits during the hand on the wide tier, at the size
   it has during the hand. */
.my-dock > .dock-side.dock-left { grid-column: 1; }
.my-dock > .dock-mid { grid-column: 2; }
.my-dock > .dock-side.dock-right { grid-column: 3; }
body.reviewing .dock-mid .dock-pad { justify-self: end; }
/* 42 between 900 and 1549: the sides at their floor, because the middle
   needs the width and the sides are not using it. The pad's column is the
   middle's leftover after the hand — `minmax(0, 1fr)` either side of a
   508–520px hand — and with the sides at 273–325px it came to 87px at 1300
   and 128 at 1440 for a pad that needs 155, so the pad's mark drawings ran
   30–60px past its paper (Mike's screenshot at ~1500, §1AE.41; measured on
   v1.86.0 and v1.87.0 alike, so older than either). What the sides held in
   this band on 42: nothing. The record left the left column for its fixed
   felt box in v1.86.0 (§1AE.25) and the chat corner on the right is hidden
   at a solo table and narrow at any. At 150 a side the pad's column is 189px
   at 1254 in Big print and grows from there; the sides stay equal, so the
   hand is still centred on the window, which `pagecheck` holds. From 1550
   the four-column tier has its own columns and its own zoom. */
@media (min-width: 900px) and (max-width: 1549px) {
  #screen-42 .my-dock { grid-template-columns: 150px minmax(0, 1fr) 150px; }
}
.dock-mid { display: flex; flex-direction: column; min-width: 0; }
/* The two side columns take their height from the middle one and never give
   it back. Their contents are taken out of the flow so a long record cannot
   grow the row — the band was getting 130px taller by the seventh trick, which
   is 130px of felt lost every hand, and losing table as the hand goes on is the
   exact thing this layout was built to stop. The record scrolls instead. */
.dock-side { position: relative; min-width: 0; min-height: 0; }
.dock-side > * { position: absolute; inset: 0; }

.trick-record {
  left: auto; bottom: auto; top: auto;
  width: 100%; max-width: none; height: 100%; max-height: none;
  display: flex; flex-direction: column; overflow: hidden;
  background: rgba(0, 0, 0, .28); border: 1px solid rgba(255, 255, 255, .10);
  border-radius: 11px; padding: 8px 9px;
}
/* The record takes the full left margin on a desktop — from under the header
   to the bottom of the window — instead of a scrolling squint of a box in the
   dock's corner beside an acre of unused felt. Pre-sized for seven tricks;
   the scroll stays as a guard it should never need. Moon at every desktop
   width; 42 in the 900-1549 band, where its dock column is short (from 1550
   the four-column band already gives it the full dock height). `position:
   fixed` escapes `.dock-side > *`'s inset lock; the column keeps its place in
   the grid, so the mirrored-pair symmetry `pagecheck` holds is untouched.
   Pic 7, 23 Aug. */
@media (min-width: 900px) {
  /* 42's record takes some of the mat off the dock — Mike, 26 Aug, in as
     many words, understanding confirmed. Not the full margin: the west seat
     lives there, and a full-height panel sat on its pile once already.
     120px of upward growth plus the column's own height fits seven tricks
     with no scrollbar. `overflow: visible` on the dock is what lets a
     column child reach over the mat at all. */
  #screen-42 .my-dock { overflow: visible; }
  /* ---- the PLAYED record: the corner, all seven, and off the chip ----

     Three requirements, all three reported at once, and they pull against each
     other: *"it overlaps the left player's chip. It should extend all the way
     to the bottom corner. It should then extend up such that you never have to
     scroll to see all dominoes, and it should also NOT OVERLAP A PLAYERS
     CHIP."*

     Measured before changing anything, at 1900x950 with Big print on:
       - the panel ran 557..816; the west seat's unit ran 299..**586**, so it
         sat 29px over the chip;
       - it stopped at 816 with the screen ending at 950 — 134px short of the
         corner;
       - its rows wanted **614px** and had **199**, so it scrolled hard.

     `-120px` of upward growth was the cause of the first: it was chosen when
     the side seat was a tall fan reaching most of the felt, and the seat unit
     made that column shorter and the number never followed. Same staleness as
     Moon's rail and the status stack — a box positioned against a seat by a
     typed offset. See the note at the foot of this block.

     **`bottom: 0` and a top that clears the unit.** The panel now runs from
     just under the west seat's bones to the dock's own bottom padding, which
     is the corner. That is the tallest it can be without landing on the chip,
     and the row height below is set so seven tricks fit it exactly. */
  #screen-42 .dock-side.dock-left .trick-record {
    /* `top: 0` — the panel does not climb onto the felt at all, and that is a
       decision rather than a default.

       It used to climb 120px and land on the chip. The obvious repair is to
       climb less, and it does not work: the free felt under the west seat is
       36px at a 950-tall window, **20px at Mike's own 908**, and shrinking
       from there, because the felt flexes with the window while the seat unit
       is a fixed height anchored to its top. Any constant put here is right at
       one window height and on the chip at every shorter one — which is the
       third time in two releases a box has been positioned against a seat by a
       typed offset. There is no constant that works, so there is no constant.

       `height: auto` matters as much as either edge. `.trick-record` carries
       `height: 100%` from the rule that pins a dock column's child to its box;
       with a top AND a bottom set, that height wins and the bottom is ignored
       — the panel kept ending 100px short of the corner while the CSS plainly
       said `bottom: 0`. Over-constrained boxes resolve by dropping the edge
       you meant. */
    top: 0; bottom: 0; height: auto;
  }
  /* Seven tricks in the height that leaves, without scrolling and without
     shrinking a bone anybody reads. The panel is ~330px; the heading and key
     take 48, so seven rows have ~280 and a row must come in at 40px. The gap
     and the padding pay for it first — the same trade §3.19a made for the hand
     review — and the record's own bone comes down only for the last of it,
     because the whole complaint about this table is that things are too small.
     BACKLOG 1U.5. */
  #screen-42 .trick-record .tr-rows { gap: 2px; }
  #screen-42 .trick-record .tr-row { padding: 1px 4px; }
  /* The panel's own chrome pays before the bones do. The heading's 7px of
     bottom margin and the key's spacing are 20px of nothing; a row's height is
     the upright bone plus 4, so 20px of chrome is worth half a bone. */
  #screen-42 .trick-record .tr-head { margin-bottom: 2px; }
  #screen-42 .trick-record .tr-key { margin-top: 2px; font-size: 10px; }
  /* And then the bone, by the least that makes seven fit in what is left.

     This is the one place in this pass where something gets SMALLER, and it is
     worth saying why rather than hoping it goes unnoticed. Three things were
     asked for at once — reach the corner, show all seven without scrolling,
     never touch a chip — and at a 908-tall window they do not all fit at 44px.
     The corner and the chip are absolutes; the bone is the only give. These
     are the record's bones, a log of what has been played, rather than the
     table's, which are read across a room.

     If seven-without-scrolling turns out to matter less than the size here,
     the trade runs the other way and this number goes back up. BACKLOG 1U.5. */
  /* `--rec-bone: 28px` stood here — the corner box's bone, the one Mike
     called too small *"when fully on dock"*. The record is on the dock again
     from 4 Sep, laid the other way, and its bone comes from the column; see
     "the record, across the dock" below. */
  #screen-moon .dock-side.dock-left .trick-record {
    /* Below the seat band, not from the header: at widths under ~1900 a
       record starting at 72px sat on Ed's pile — the "unused" left margin in
       Mike's screenshot was unused at HIS width and occupied at most others.
       Moon only: 42's side seats run half the felt's height at the middle
       widths, so its record keeps the corner box there and gets the full
       column from 1550, where the four-column dock already gives it one.

       **This number is the seat band's height and nothing else, so it moves
       whenever the seat does.** It said 240 and the band ended at 292 — the
       rail was fixed to the viewport, the seat unit made the band taller, and
       the record went straight back onto Ed's pile and covered his DOWN chip.
       Exactly the fault the paragraph above describes, recurring because the
       thing it was measured against changed and the measurement did not.

       Measured, not chosen: the band bottoms out at 292 in this tier and at
       347 in the one below, which are the seat unit's own two desktop tiers
       for a three-across row. If `--su-id-h` or `--su-bone` moves for
       `#mn-seats`, these move with it. */
    /* The felt rail — `position: fixed` down the left of the table, top
       302 or 358, bones to 80 — stood here from 27 August to 4 September.
       It is gone: the record is on the dock again, laid across it. The
       rule below is shared with 42's. Its last afternoon on the felt it
       grew a height cap so seven tricks fit at 908 without a scrollbar
       (Mike, 4 Sep, *"slightly too big"*); that cap is now the column's
       height term. */
  }

  /* The record lay ACROSS the dock here for one day (4 Sep, §1AE.8/11):
     seven columns from the corner, container-sized bones. Mike the next
     morning: *"just make it exactly the same as Moon"* — so both records
     are the felt box below (§1AE.25), and those rules are gone. */
}
/* Moon's record, on a desktop: its own box down the left of the felt, from
   under the header to the dock — *"just move the played dominoes for Moon
   only to the space between the header and the dock for now; keep it in
   its own box but fill that space"* (4 Sep, the evening; it was a felt
   rail from the seat band down on the 27th and across the dock for one
   cut that morning). Rows, newest at the bottom. Each row's chip ends
   after the bones and the name sits outside it to the right, so the box is
   as wide as three bones and a name and no wider. The bone is what seven
   rows of the box's height allow, from the window: the header is 96, the
   dock 230-odd at this width, and the box keeps 8px off each. */
/* And 42's, the same box, from the morning of the 5th (§1AE.25): *"just
   make it exactly the same as Moon … put the pane between the top header
   and the dock … go back to how you had the orientation."* One set of
   rules for both; 42's rows are the rows they always were — plain bones
   upright on the left, the count stacks flat to the right, the points at
   the end — and its box is a bone wider than Moon's for the fourth bone.
   The rows sit closer than the first cut's (gap 2, chip padding 1 — *"a
   little bit less space between the rows so the dominoes could be
   bigger"*, §1AE.22), which is what the bone's sum below reads. */
@media (min-width: 900px) {
  #screen-42 .dock-side.dock-left .trick-record,
  #screen-moon .dock-side.dock-left .trick-record {
    position: fixed; inset: auto; left: 10px; top: 104px; bottom: 242px;
    /* A width from the bone, not from the rows: with `width: auto` the box
       grew and shrank as tricks with stacks and tricks without came in,
       and `pagecheck`'s "the record's column never changes width" refused
       the cut. 42's widest row is four upright bones or a stack, a stack
       and an upright — about 3.2 bones — plus number and points; Moon's is
       three upright bones plus number and name. */
    width: calc(var(--rec-bone) * 3.2 + 76px); max-width: 30vw; height: auto; max-height: none;
    display: flex; flex-direction: column; padding: 8px 10px;
  }
  /* The record's bone, declared on the SCREEN rather than on the record,
     because two things read it now: the record's box, and 42's seat row,
     which pads itself by the box's width so the west seat is not under it
     (§1AE.29, 5 Sep: *"you forgot to shift left player towards the
     middle"*). One formula, inherited by both; a second copy of it is the
     trap CLAUDE.md names under Big print.

     Seven rows out of the box's height: the window less header, dock and
     the box's own margins (346), less the heading and padding (50), split
     seven ways, less a row's padding, its gap and the bone's own border
     (11 with the tight rows; it was 16 with gap 5 and padding 3, and 57px
     bones at 908 tall — 62 now). */
  #screen-42, #screen-moon {
    --rec-bone: max(40px, min(80px, calc((100vh - 346px - 50px) / 7 - 11px)));
  }
  #screen-moon .dock-side.dock-left .trick-record { width: calc(var(--rec-bone) * 1.6 + 170px); }
  /* 42's seat row's padding by the box's width is declared beside its
     top padding, further down — the base rule `#screen-42 .seats-mid {
     padding-left: 18px …}` sits between here and there without a guard,
     and a first copy of this here lost to it at every width (`csslint`,
     5 Sep). */
  #screen-42 .dock-side.dock-left .trick-record .tr-rows,
  #screen-moon .dock-side.dock-left .trick-record .tr-rows { gap: 2px; margin-top: auto; }
  #screen-42 .dock-side.dock-left .trick-record .tr-row,
  #screen-moon .dock-side.dock-left .trick-record .tr-row {
    background: none; border: none; padding: 0; gap: 8px; align-items: center;
  }
  #screen-42 .dock-side.dock-left .trick-record .tr-row .tr-bones,
  #screen-moon .dock-side.dock-left .trick-record .tr-row .tr-bones {
    padding: 1px 4px; border-radius: 7px; border-left: 3px solid transparent;
  }
  #screen-42 .dock-side.dock-left .trick-record .tr-row.ours .tr-bones,
  #screen-moon .dock-side.dock-left .trick-record .tr-row.ours .tr-bones {
    background: rgba(224,170,62,.24); border-left-color: var(--pill-hi);
  }
  #screen-42 .dock-side.dock-left .trick-record .tr-row.theirs .tr-bones,
  #screen-moon .dock-side.dock-left .trick-record .tr-row.theirs .tr-bones {
    background: rgba(255,255,255,.09); border-left-color: rgba(255,255,255,.36);
  }
  #screen-42 .dock-side.dock-left .trick-record .tr-pts,
  #screen-moon .dock-side.dock-left .trick-record .mn-who {
    margin-left: 0; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  #screen-moon .dock-side.dock-left .trick-record .tr-rows {
    display: flex; flex-direction: column; gap: 5px; margin-top: auto; overflow-y: auto;
  }
  #screen-moon .dock-side.dock-left .trick-record .tr-row {
    flex-direction: row; align-items: center; gap: 6px; padding: 3px 5px;
    border-top: none; border-left: 3px solid transparent; border-radius: 7px;
  }
  #screen-moon .dock-side.dock-left .trick-record .tr-row.ours { border-left-color: var(--pill-hi); }
  #screen-moon .dock-side.dock-left .trick-record .tr-row.theirs { border-left-color: rgba(255,255,255,.36); }
  #screen-moon .dock-side.dock-left .trick-record .tr-bones { flex-wrap: nowrap; width: auto; gap: 3px; }
  #screen-moon .dock-side.dock-left .trick-record .tr-pts {
    margin-left: auto; margin-top: 0; white-space: normal; font-size: 12px; text-align: right;
  }
}
/* ---------- Moon has no scorepad, and 42's across-record and Focus ---------- 4 Sep */
/* *"Just go ahead and remove scorepad entirely from Moon, desktop and
   mobile. We have the chips on the dock that'll work for that."* Last in
   the file so it outranks the review's `display: flex !important`. */
#screen-moon .dock-pad { display: none !important; }
/* And none in the training hand, at any moment — *"just take it out of
   learning hand"* (5 Sep). The review's rule turns the pad back on for the
   end of an ordinary hand; this is later in the file with the same weight,
   so it wins there too. */
body.tutoring .dock-pad { display: none !important; }
/* The names under the played bones, bold — Moon's ask; the tables share
   the slot, so all of them. */
.trick-slot .who { font-weight: bold; opacity: .8; }
/* The standing sentence in a lane: *"the text is way too big in this case,
   too narrow, and center justified. Would like it smaller, right justified
   generally, and not so thin/compressed."* 26px at 900+ was the phone's
   middle-of-the-table size carried into a 40%-wide lane column; 19 and a
   60% column, right-aligned, on every lane. */
@media (min-width: 900px) {
  /* The whole of the lane after the plate, not a share of it. This said
     60% and the later rule in the desktop block said 40%, and 40% won —
     219px at 1366, in which "You won the bid — name your trumps." broke
     after "name" (§1AF.38, Mike, 9 Sep: "broken between two lines for no
     good reason"). The plate is `flex: none`; the sentence takes the rest
     and is still right-aligned against the lane's edge. */
  .status-stack.stack-lane { max-width: none; }
  .stack-lane .st-standing { font-size: 19px; text-align: right; line-height: 1.25; }
  /* Focus: the board and what it takes to play (§1AE.15, then §1AE.26 on
     the morning of the 5th — *"we really don't need people's icons up top.
     It just needs to be their name and how many dominoes they have left …
     who's the shaker and who's my partner … the dominoes in one row of
     seven instead of four and three … maximum vertical space for the
     board"*). Three moves, each measured at 1913x908 by `pagecheck`:

     1. The seats become one line each: no ring, no face, no back — the
        tag as a small word, the name, the count. The disc keeps its chips
        (SHAKER, PARTNER) and loses its face and its circle, so the words
        stay without a picture to hang on. Whose turn it is goes gold on
        the name, since the ring that showed it is gone.
     2. The hand is one row of seven at a smaller bone, so the dock loses
        its second row.
     3. The lane is one line: the plate's ring goes, the sentence is one
        line tall.
     No pad, no board floor, as before. */
  #screen-fives.focus .dock-pad { display: none !important; }
  #screen-fives.focus .board-wrap { min-height: 0; }
  #screen-fives.focus .fives-seats { padding: 0 12px; }
  #screen-fives.focus .fives-seats .tok {
    display: flex; flex-direction: row; align-items: center; gap: var(--sc-gap-line);
    height: auto; min-height: 0; width: auto; max-width: none; padding: 0;
  }
  #screen-fives.focus .fives-seats .ring {
    width: auto; height: auto; padding: 0; background: none; border: none; box-shadow: none;
  }
  #screen-fives.focus .fives-seats .disc {
    width: auto; height: auto; font-size: 0; background: none; border: none;
    box-shadow: none; display: flex; gap: 4px; align-items: center;
  }
  #screen-fives.focus .fives-seats .disc .chip { position: static; transform: none; font-size: 10px; }
  #screen-fives.focus .fives-seats .tok.is-turn .pname { color: var(--pill-hi); }
  /* The line's parts, in order and unsqueezed — Mike's screenshot at
     1233x993 on 5 Sep had Ed's name gone, Becky's cut to "B." and Bobo's
     count wrapped under him (§1AE.26, the red circle). Three causes, each a
     card rule doing its card job on a line:
       - `.hand` is a grid whose columns reserve room for the back even with
         the back hidden (70–100px of nothing), and the name — `max-width:
         100%` inside a shrinkable `.namerow` — paid for it: `.pname`
         measured 3px wide. So the hand is a plain flex box here and the
         name may not shrink.
       - the partner's `.info` stacks name over count (`flex-direction:
         column`), which is the lying-down card's shape; on a line the two
         sit side by side.
       - the tags (SHAKER, PARTNER) came first because the disc does; after
         the name reads as a line — `Becky 7 SHAKER` — so the ring goes last. */
  #screen-fives.focus .fives-seats .tok { gap: var(--sc-gap-line-wide); }
  #screen-fives.focus .fives-seats .tok .ring { order: 3; }
  #screen-fives.focus .fives-seats .namerow,
  #screen-fives.focus .fives-seats .pname { max-width: none; flex: none; width: auto; overflow: visible; }
  #screen-fives.focus .fives-seats .tok.tok-h .info { flex-direction: row; align-items: baseline; gap: var(--sc-gap-line-wide); }
  #screen-fives.focus .fives-seats .tok.tok-h .info > .namerow { width: auto; }
  #screen-fives.focus .fives-seats .hand { margin: 0; display: flex; width: auto; gap: 0; }
  #screen-fives.focus .fives-seats .dom-back { display: none !important; }
  #screen-fives.focus .my-hand { grid-template-columns: repeat(7, max-content); }
  #screen-fives.focus .my-dock { --dock-bone-h: 42px; }
  #screen-fives.focus .my-dock .domino.h { width: 84px; height: 42px; }
  #screen-fives.focus .my-dock .domino.v { width: 42px; height: 84px; }
  #screen-fives.focus .my-dock .domino .half { width: 42px; height: 42px; }
  #screen-fives.focus .felt-lane { padding-top: 0; padding-bottom: 2px; }
  #screen-fives.focus .felt-lane .me-plate .ring { display: none; }
  #screen-fives.focus .felt-lane .me-plate { height: auto; min-height: 0; }
  #screen-fives.focus .lane-status { height: 1.25em; font-size: 18px; }
  /* The three fixed heights that survived the first cut, measured at 1913:
     the lying-down cards (the partner's and your plate's) keep
     `calc(var(--sc-stack) + 4px)` — 74 and 80 — and the hand keeps its
     two-row reserve (104) under one row of 42. With those the seat row is
     74 and the box gained 151; without, about 30 each and the box gains
     roughly 330. The dock's safe-area padding is a phone's. */
  #screen-fives.focus .fives-seats .tok, #screen-fives.focus .felt-lane .tok { height: auto; min-height: 0; }
  #screen-fives.focus .my-hand { min-height: var(--dock-bone-h); }
  #screen-fives.focus .my-dock { padding-bottom: 12px; }
  #screen-fives.focus .dock-head { margin-bottom: 4px; padding: 2px 0 4px; }
}
/* Fill from the bottom up, the way a stack of won tricks grows. */
.trick-record .tr-rows { margin-top: auto; overflow-y: auto; min-height: 0; }
.trick-record .tr-note, .trick-record .tr-head { flex: none; }

/* The record's bones grow with the column they live in.

   Reported 17 Aug: on a desktop the record is the box you actually play off
   for the whole hand, and its bones were too small to read. They were pinned
   at the 56px `small` size while the column around them is
   `clamp(210px, 21vw, 340px)` — so on a wide screen there was room going spare
   and on a 1240px one there was none.

   A flat bump was therefore not available, and neither was a new breakpoint:
   the room depends on the column, and the column depends on the viewport, so
   the bone does too. Four bones, three gaps, the row's padding and the
   record's own come to `4b + 44 <= 21vw`, which is `b <= 5.25vw - 11`. That
   expression is the clamp, with today's 56 as the floor so no width gets
   smaller than it is now, and 73 as the ceiling because the column stops
   growing at 340px and 4x73 + 44 is 336.

   `:not(.tr-chip)` keeps this off the phone, where the record is a chip you
   tap rather than a panel you read. */
/* The ceiling is the column, not a taste: the record lays four bones to a
   trick inside a `minmax(220px, 340px)` column with about 20px of padding, so
   80 is what fits and 73 was leaving the difference as empty green. Reported
   21 Aug off a desktop screenshot. BACKLOG 1L.5. */
:root { --rec-bone: clamp(56px, calc(5.25vw - 11px), 80px); }
/* The middle band pays for its taller dock. Between 900 and 1549 the pad sits
   inline beside the hand (the four-column band starts at 1550 now), the dock
   is ~70px taller, and the review panel — sized off the felt — lost exactly
   the height that made seven tricks fit. A 48px floor at these widths gives
   the record back 28px of the 21 it was over. BACKLOG 1Q.1's tail. */
@media (min-width: 900px) and (max-width: 1549px) {
  :root { --rec-bone: clamp(48px, calc(5.25vw - 11px), 73px); }
  /* And the review panel's bones, which are what the seven-tricks-fit check
     actually measures — the first version of this band adjusted only
     --rec-bone and left the review 21px over at 900x700. */
  .hand-review { --review-bone: 48px; }
}
.trick-record:not(.tr-chip) .domino.small.h {
  width: var(--rec-bone); height: calc(var(--rec-bone) / 2);
}
.trick-record:not(.tr-chip) .domino.small.v {
  width: calc(var(--rec-bone) / 2); height: var(--rec-bone);
}
.trick-record:not(.tr-chip) .domino.small .half {
  width: calc(var(--rec-bone) / 2); height: calc(var(--rec-bone) / 2);
}

/* Twice the size they were. These are meant to be read across the table, not
   squinted at — the whole reason to keep won tricks visible is that reading
   them is part of the game. */
/* Half what they were. Seven tricks at the larger size made the record taller
   than the rest of the band, and a panel that grows the band grows the whole
   bottom of the screen — the felt loses height every trick, which is the thing
   all of this was trying to stop.

   The two orientations stay the same bone turned: flat is 36 by 18, upright is
   18 by 36. That equality is what makes a stack of two flat ones exactly the
   height of one upright, and it is the whole reason the rows line up. */
.trick-record .domino.small.h { width: 36px; height: 18px; }
.trick-record .domino.small.v { width: 18px; height: 36px; }
.trick-record .domino.small .half { width: 18px; height: 18px; }

.chat-corner {
  width: 100%; height: 100%;
  border: 1px dashed rgba(255, 255, 255, .13); border-radius: 11px;
  background: rgba(0, 0, 0, .14);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; color: var(--cream); opacity: .5; text-align: center; padding: 12px;
  pointer-events: none;
}
.chat-corner b { font-size: 13px; letter-spacing: .06em; }
.chat-corner span { font-size: 12px; opacity: .8; line-height: 1.45; max-width: 230px; }

/* The felt goes back to being only the table: seats, the trick in play, and
   the mark. Nothing sits in its corners any more. */
.seats-mid { padding-bottom: 0; min-height: 0; }

/* The two side hands overlap the way a held hand does. Seven backs standing
   clear of each other is nearly 300 pixels of table given to the edge, and it
   never looked like somebody holding bones — it looked like bones laid out. */
/* A pile, not a ladder. At -15px seven backs stood ~295px tall, and with the
   seat head above them the column outgrew any window under ~980 — Mike's own
   is ~905, so the bottom bones slid under the dock band and the page
   scrolled. D4's rule (1N.1) applies here the way it already does to Fives
   and Moon: the stack shows the true count in about one bone's footprint,
   edges peeking, top bone whole. Seven now stand ~115px. */
/* The side columns' overlap, and a 130px reserve against a stack that
   measured 115. Both are `.seat-unit.su-col` now, where the reserve is
   derived from the step rather than typed next to it. */
.seat-n { align-self: center; }

/* ---------- bigger bones on a desktop ----------
   A phone is held at arm's length by one person. A laptop is on a table with
   people round it, and at that distance the bones were small. Only the hand
   and the trick in play grow — the far seats show backs, and the record has
   its own size above. */
/* The bigger bones need a screen wide enough to hold seven of them beside two
   side columns. Below that they stay the standard size and still read fine —
   1500px is where the sum stops fitting, measured rather than guessed. */
@media (min-width: 1500px) {
  .my-dock { --dock-bone-h: 55px; }        /* the hand's reserve reads this */
  .my-dock .domino.h { width: 110px; height: 55px; }
  .my-dock .domino.v { width: 55px; height: 110px; }
  .my-dock .domino .half { width: 55px; height: 55px; }
  .trick-slot { min-width: 118px; min-height: 60px; }
}

/* A phone has one column, not three: the record stays a chip you tap and the
   chat slot doesn't render at all. */
@media (max-width: 899px) {
  .my-dock { display: flex; flex-direction: column; gap: 0; }
  .dock-right { display: none; }
  .dock-left { order: -1; position: static; }
  .dock-side > * { position: static; }
  .trick-record {
    width: auto; height: auto; padding: 0;
    background: none; border: none;
  }
}

/* ---------- the dock on a phone: bones left, scorepad right ----------
   Reported twice from real games, and the complaint was exact. Stacked, the
   pad is a big object sitting under a thin strip of bones, so the one thing
   you have to be able to read — your own seven — ends up the smallest thing on
   the screen. Side by side, the hand gets most of the width and the pad gets
   what it needs, which is not much.

   **Four on top, three underneath, as a fixed split.** A grid of four columns
   does that by construction. Flex-wrap was the tempting version and it is
   wrong: it gives 4/3 at one width and 5/2 at another, so the hand reshuffles
   itself when the keyboard opens, and a hand that moves while you are looking
   at it is worse than a small one.

   **Nothing overlaps.** Not on the short edge, not on the long edge either.
   If four across means the bones get smaller, they get smaller — a hand where
   every bone is whole and separate beats a hand where they are bigger and
   touching. Decided 13 August; overlap stays on the shelf as an option.

   **`zoom`, not `transform: scale`, on the pad.** A transform shrinks what you
   see and not what the layout thinks is there, so a scaled pad still claims
   its full width and pushes the bones back into a strip — the exact bug this
   section exists to fix, arriving by the back door. `zoom` changes both. */
/* On a laptop the same shape, for the same reason. The dock's middle column
   has the room for it, and putting the pad beside the hand rather than under
   it buys the bones a size — which is the thing everybody actually wants to
   read. The two outer columns are untouched: the trick record and the chat
   keep exactly the space they had. */
.dock-mid {
  display: grid;
  /* Three columns so the bones sit in the middle of the dock rather than the
     middle of what the pad left them. The outer two are equal `1fr`, the left
     one deliberately empty and the right one holding the pad, so the hand
     column — sized to the hand — lands dead centre however wide the pad is.

     Centring the hand inside a two-column grid, which is the obvious version,
     is what the bones were already doing: 98px clear on the left and 280 on
     the right at 1440, because the pad's column was pulling the whole thing
     left. Reported 15 Aug off a screenshot. */
  grid-template-columns: minmax(0, 1fr) max-content minmax(0, 1fr);
  grid-template-areas: 'head head head' 'info info info' 'gap hand pad';
  align-items: start; column-gap: 16px;
}
.dock-head { grid-area: head; }
/* Taroky opts out of the 'gap hand pad' grid above. That grid is written for
   42 and Fives, whose children name their areas; Taroky's #tk-actions names
   none, so grid auto-placement dropped the bid buttons into the empty left
   'gap' column — the whole of "the bidding is way to the left" (1P.5's fix
   aimed CSS at the buttons and missed that their *container's* grid was the
   thing placing them). A dock with no pad is a plain centred column.
   BACKLOG 1Q.4. */
#screen-taroky .dock-mid { display: flex; flex-direction: column; align-items: center; }
/* On a laptop the score band stays above the bones: there is height to spare
   here, so the hand does not have to sit between the two things you glance at
   the way it does on a phone. Mike, 15 Aug. */
.dock-info { grid-area: info; justify-content: center; margin-top: 0; margin-bottom: 6px; }
.my-hand {
  grid-area: hand;
  display: grid; grid-template-columns: repeat(4, max-content);
  /* THE THIRD COPY OF THIS NUMBER, and the one that actually governed.

     There were three: 6px on the base rule, 4px in the Big print block, and
     12px here on the desktop. Between 20 and 27 August the gap was raised
     three times - 6 to 9, 9 to 12, each "verified at the width the report came
     from" - and the report came back a fourth time in capitals, because the
     profile making it reads the Big print copy and that one had never moved.
     Every increase went into a number the person asking could not see.

     One variable now, declared with its Big print twin next to it, so raising
     one puts the other in front of whoever is raising it. BACKLOG 1R.2, 1U.7. */
  justify-content: center; align-content: start; gap: var(--hand-gap);
  /* Two rows of height, reserved, whatever the hand holds.

     Seven bones lay 4+3 and four lay 4, so the grid lost a row and the whole
     dock stepped down with it — *"once I went from five dominoes to four in my
     hand, it went from two rows to one row, and the dock moved down slightly.
     And as you know, I don't want any of this stuff to move."*

     This is the seat unit's fixed-footprint rule (1R.8) in one more place: a
     box that sizes to its contents moves everything around it when those
     contents change, and a hand empties four times a trick.

     Naming two grid rows is NOT enough and was tried first — an empty row is
     `min-content: 0`, so the second one collapses exactly when it is needed.
     The height has to be reserved outright.

     `--dock-bone-h` is that reservation's one input, and every band below that
     sets a dock bone's height sets this beside it. One number per band, one
     derivation here — rather than a second copy of "two rows" per band, which
     is how the hand gap came to have three values that disagreed. 1V.9. */
  /* A fallback in the `var()`, NOT a declaration here — declaring it on this
     element shadows the value inherited from `.my-dock`, so the reserve was
     computed from 28px while the bones were 56 and came out at 76px against a
     needed 132. The band sets the number; this only reads it. */
  min-height: calc(var(--dock-bone-h, 28px) * 2 + var(--hand-gap));
}
.dock-pad { grid-area: pad; margin-top: 0; justify-content: flex-start; align-items: start; }

@media (min-width: 1550px) {
  /* ---------- the bottom band, on a screen with room in it ----------

     Four columns: the won tricks, your hand, the scorepad, the talk. Off a
     1900px screenshot, 15 Aug — the bones were bunched to the left of a band
     with two empty thirds in it, because the pad and the chat were sharing
     their column with everything else instead of holding their own.

     **1550, not 900, and the number is arithmetic rather than taste.** Grid
     track sizing grows the two minmax(220px, 340px) side columns to their
     340px limit *before* the hand's `1fr` receives anything, so the hand gets
     `width - 1074` (two sides, the zoomed pad's ~340, three 18px gaps) and
     needs ~466 for four bones. At the old 900 entry the columns overflowed
     into each other from 900 to ~1540 — the scorepad sat on the hand and the
     page scrolled — and it was never seen because every check ran at 1917,
     where it fits. Below 1550 the dock uses the same three-column band as the
     other games, which holds at every width in that range. BACKLOG 1Q.1.

     `display: contents` on `.dock-mid` is what makes this a four-column grid
     without moving anything in the markup: the wrapper stops generating a box
     and its children — the head, the hand, the score band — become items of
     the dock's own grid, so the pad can be lifted out into a column of its own
     while still sitting inside `.dock-mid` in the HTML. The alternative was
     rearranging the template for a rule that applies to wide screens only. */
  /* **42 only.** Fives, Moon and Taroky keep the three-column band they had.
     Their pads are different shapes — Fives' is a running column of Xs that
     grows all game — and none of them has the score band this splits around,
     so pulling their pad into a column of its own would be a layout invented
     for a screenshot of a different game. `pagecheck` asserts the three
     columns for those three, which is how this was caught.

     **The two side columns stay a mirrored pair**, the same width 8px in from
     each edge. That symmetry is a deliberate thing `pagecheck` checks, and it
     was worth keeping rather than arguing with: making the talk wider than the
     record buys the talk very little and costs the band its shape. The room
     for chat comes out of the middle, which had it spare. */
  /* Centred on the WINDOW, at every width in this tier (§1AC.30, 3 Sep:
     *"the dominoes are offset to the left"* — 200px at 1913). Four columns
     cannot centre the second one unless the first carries what the third
     and fourth carry beyond the fourth's own width: the pad and a gap. So
     the outer two share the free space equally as `1fr`, and the left one's
     floor is the talk's floor PLUS the pad's width plus the gap — 220 + 271
     + 18 — which keeps the hand's column on the window's centre line
     whatever the width. The hand's column is `max-content`, which is four
     bones or the head row, whichever is wider. The pad is drawn at 1.6
     rather than 2 for the sums to fit at 1550, the tier's floor:
     16 + 509 + 460 + 271 + 54 + 220 = 1530. `pagecheck` measures the
     hand's centre against the window's at 1254, 1366 and 1913. */
  /* Three tracks, not four. The first version of this fix put the pad's
     width into the left track as a constant, and the pad is not a constant:
     the tournament pad (`US ALL / THEM ALL`) is 473 wide against the marks
     pad's 339, and the hand sat 101px left of centre with it. Equal outer
     tracks, the hand's column between them, and the talk panel UNDER the
     pad in the third column rather than beside it in a fourth. The dock is
     taller by the talk panel's height at this tier — about 80px — which
     the felt gives up; §1V.8's "natural space for chat on the far right"
     becomes the space under the pad. */
  #screen-42 .my-dock {
    display: grid;
    grid-template-columns:
      minmax(0, 1fr)            /* what has been won */
      max-content               /* your hand, and the head row over it */
      minmax(0, 1fr);           /* the pad, with the talk under it */
    grid-template-rows: auto auto auto;
    align-items: start; column-gap: 18px;
  }
  #screen-42 .dock-side.dock-left { grid-column: 1; grid-row: 1 / -1; height: 100%; }
  #screen-42 .dock-mid { display: contents; }
  /* Moon's dock takes the same three tracks at this width, for the same
     reason and one more: its record lives in the left column now (4 Sep),
     and `clamp(150px, 13vw, 250px)` gave that column 248 at 1913 — seven
     tricks across 248 is a 30px bone. Equal outer tracks either side of a
     `max-content` hand give it 440, and the bone comes to 57. */
  #screen-moon .my-dock {
    grid-template-columns: minmax(0, 1fr) max-content minmax(0, 1fr);
    column-gap: 18px;
  }
  #screen-42 .dock-head { grid-column: 2; grid-row: 1; }
  #screen-42 .my-hand { grid-column: 2; grid-row: 2; }
  #screen-42 .dock-info { grid-column: 2; grid-row: 3; }
  /* The pad stands the full height of the band with a little air top and
     bottom, so it reads as part of the dock rather than as a note left on it —
     Mike, 15 Aug. `stretch` gives it the row; the pad inside grows to fill it
     and stays centred on its own axis. */
  #screen-42 .dock-pad {
    grid-column: 3; grid-row: 1 / 3; align-self: stretch;
    display: flex; align-items: center; justify-content: center; padding: 6px 0;
  }
  #screen-42 .dock-pad .score-pad {
    transform: rotate(-1.2deg); transform-origin: center center;
    /* 1.6 from 2 (§1AC.30): the pad's width is what the left column has to
       carry to keep the hand centred, and at 2 the sums did not fit the
       tier's floor. Same drawing, a fifth smaller, 271 wide. */
    zoom: 1.6;
  }
  #screen-42 .dock-side.dock-right { grid-column: 3; grid-row: 3; height: auto; }}


/* Bigger bones in your own hand than anywhere else on the table. Yours is the
   one hand you have to read rather than glance at, and four across leaves the
   width to do it. */
/* **Guarded by a min-width, and it must stay that way.** Unguarded, this sits
   later in the file than the phone sizes and therefore beats them at equal
   specificity — which is exactly what happened between v1.44.0 and v1.49.0.
   A 390px screen was drawing 100px bones, the fourth one ran off the edge, and
   the scorepad was clipped away entirely by the dock's `overflow: hidden`. It
   shipped because the mobile screenshots that proved the layout were taken
   before this rule existed, and because the tests measured overlap and page
   overflow — neither of which a clipped element trips. `fitcheck.js` now looks
   at whether the pad is actually on the screen. */
@media (min-width: 561px) {
  /* 112 rather than 100 — Mike, 15 Aug, with a screenshot showing clear felt
     above the hand that the bones could be spending. Yours is the one hand you
     read rather than glance at, and on a laptop nothing is competing for the
     room. `fitcheck` guards the phone sizes, which this rule cannot reach:
     it is behind a `min-width` on purpose, because unguarded it once sat later
     in the file than the phone sizes, beat them, and drew 100px bones on a
     390px screen. */
  .my-dock { --dock-bone-h: 56px; }        /* the hand's reserve reads this */
  .my-dock .domino.h { width: 112px; height: 56px; }
  .my-dock .domino.v { width: 56px; height: 112px; }
  .my-dock .domino .half { width: 56px; height: 56px; }
}

@media (max-width: 560px) {
  /* One column, three bands: who and what is bid, then the bones, then the
     score. The pad no longer stands beside the hand — it shares the score
     band's slot and appears only when the score is tapped — so the hand gets
     the whole width of the screen instead of whatever the pad left it.

     What that is worth, measured at 390px on 15 Aug: bones went from 82px to
     93px, four across, edge to edge. The old two-column grid is the reason
     they were 82 — the pad took a 40px strip on the right and the hand column
     was sized to the remainder. A scorepad nobody looks at between hands was
     costing the one thing on the screen everybody has to read.

     `1fr` on the hand row rather than `max-content`: the bones size themselves
     off the column now, so the column has to be the screen. */
  .dock-mid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: 'head' 'hand' 'info';
    align-items: start; row-gap: 0;
  }
  .dock-head { grid-area: head; justify-content: center; }
  /* Four across, and **the bone is the cell**.

     This is the fault behind three separate reports of "big gaps between the
     dominoes", and none of my first two attempts touched it. The row is a grid
     of four equal fractions of the full width, but the bone inside carried a
     *fixed* width from `--bone` — so whenever `--bone` came out smaller than a
     quarter of the row, every cell held dead space, and four lots of dead space
     read as gaps. Raising the cap and then deriving `--bone` from the container
     both made the number closer on the machines I could measure and neither
     could make it exact, because the two are computed from different things.
     Measured here at 104px cells holding 100px bones; on the phone it was
     reported from, far wider.

     A bone that fills its cell cannot disagree with it. The width comes from
     the row, the height from the aspect ratio, and the halves from the bone —
     one chain, no second opinion. `--bone` still sizes everything else in the
     dock. §1E.1, §1F.5. */
  .my-hand {
    grid-area: hand;
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    justify-content: center; align-content: start; gap: var(--hand-gap); width: 100%;
    /* The gap a phone actually shows, and the reason it was never the one
       being raised. This line read `gap: 2px` — a literal, chosen on 18 Aug
       (§8B.5, "gap 4 to 2") to buy bone width at 360 — while the block that
       declares `--hand-gap` said "one variable now, so raising one puts the
       other in front of whoever is raising it". True of the two rules that
       read the variable; false of this one. Every raise from 20 August on
       went into a number the ordinary-print phone never read, and Mike
       asked eight times (§1AC.11). It reads the variable now, and the
       variable takes the phone's value here: 10px, so two 4px rings on
       neighbouring bones still show daylight between them. Four bones at
       360 are 80px against 86 before, which is the trade he chose. Big
       print keeps its 24 from `--hand-gap-big`; `fitcheck` holds a floor
       of 8 in both print sizes at every width. */
    --hand-gap: 10px;
  }
  /* Written twice, because `html.big-print .my-dock .domino.h` sets a width
     and out-specifies a plain `.my-hand .domino.h` — so Big print kept its
     fixed bone in a wider cell and got the dead space back, 37px of it at 430.
     Same rule, enough specificity to hold in both print sizes. */
  .my-hand .domino.h,
  html.big-print .my-hand .domino.h { width: 100%; height: auto; aspect-ratio: 2 / 1; }
  .my-hand .domino.h .half,
  html.big-print .my-hand .domino.h .half { width: 50%; height: auto; aspect-ratio: 1 / 1; }
  .my-hand .domino.v,
  html.big-print .my-hand .domino.v { width: 100%; height: auto; aspect-ratio: 1 / 2; }
  /* Room for the whole hand, kept whether the hand is whole or not. A bone is
     a quarter of the row wide and half as tall again, so a row is `cqi / 8`
     and the two rows a hand of seven needs are `cqi / 4`. Big print deals the
     same seven three across, which is three rows of `cqi / 6`.

     Derived rather than typed, because the two numbers that decide it — the
     bones per row and the aspect — are already written above and a constant
     here would be a third place to keep them. Reported 21 Aug: the dock lost
     a row as the hand went down and took the whole table with it, which is
     the twitch the seats spent a release getting rid of. BACKLOG 1K.11. */
  .my-hand { min-height: calc(100cqi / 4 + 2px); }
  html.big-print .my-hand { min-height: calc(100cqi / 2 + 4px); }
  .my-hand .domino.v .half,
  html.big-print .my-hand .domino.v .half { width: 100%; height: 50%; }
  /* Both live in the same slot. Only one is shown, so swapping between them
     cannot move anything above or below — the bones do not jump because
     somebody checked the score.

     Scoped to 42. Moon has a pad too and no score button to bring it back
     with, so an unscoped `display: none` here would simply delete it. It keeps
     the slot and stays visible; the swap is a 42 feature because the 42 pad is
     the one that was stealing the width. */
  .dock-info, .dock-pad { grid-area: info; }
  .dock-info { margin-top: 8px; justify-content: center; }

  /* The record chip and the bid share one row, half each.

     They were two rows. The chip had a full-width row of its own, and the bid
     sat beside your name on the row under it — where, once your plate is
     carrying SHAKER and BID and DOWN, there is not room for both and the whole
     head wraps to two lines. That is the row this buys back, and it buys it
     back *permanently*: with the bid moved out, the head holds one thing and
     can never wrap however many badges turn up.

     `display: contents` on `.dock-mid` and `.dock-head` is what makes this
     possible without touching the template — the wrappers stop generating
     boxes and their children become items of the dock's own grid, so the chip
     (which lives in `.dock-left`, a different subtree entirely) can sit beside
     the bid. Same trick as the four-column band in v1.56.0.

     What it does NOT buy is bigger bones, and it is worth writing down so
     nobody expects it to. `--bone` is `min(92px, (100vw - 20px) / 4)`: at
     390px that is 92.5 capped to 92, and at 360px it is 85. The bones are
     limited by the width of the screen at four across, not by the height of
     the band, so vertical room cannot reach them. It goes to the score line
     and back to the felt. */
  #screen-42 .my-dock {
    display: grid;
    /* `minmax(0, 1fr)` and not `1fr`, which is the same reason `.dock-mid`
       above is written that way. A bare `1fr` track takes an automatic
       min-content floor, and the two score pills are `white-space: nowrap`, so
       the info row — which spans both columns — refused to shrink and pushed
       the whole dock to 452px inside a 390px screen. Measured, not guessed:
       the bid pill's right edge landed 27px off the side of the phone. */
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: 'rec bid' 'name name' 'hand hand' 'info info';
    column-gap: 8px; row-gap: 4px;
  }
  #screen-42 .my-dock > .dock-mid,
  #screen-42 .my-dock .dock-head { display: contents; }
  #screen-42 .my-dock > .dock-left { grid-area: rec; justify-self: stretch; align-self: center; }
  /* The two halves are a matched pair, and they have to *look* like one.

     First cut sized each to its own content, so the chip and the bid were
     different widths and — once "1 of 7" wrapped inside a 172px half at 360px
     — different heights too: 47px against 30px. Reported the same day, and
     fairly: two controls side by side at two different sizes read as a mistake
     rather than as a pair.

     So both fill their half, share a floor height, and neither may wrap. The
     type scales with the screen instead, which is what keeps the label and the
     count inside 172px at 360 where a flat size overflows it by about seven
     pixels. Same `clamp()` approach as the score band directly below. */
  /* `gap`, and it is fixing a bug rather than tidying spacing.

     These two become flex containers here and nowhere else, so on a phone the
     chip's text node and its `<b>` are separate flex items — and flex collapses
     the trailing whitespace of an anonymous item. "Buck bid 30" rendered as
     "Buck bid30", measured at a 0px gap, while the desktop was fine because
     `.ti-item` is not flex there. The markup was never wrong, which is why
     reading it found nothing. §1F.2. */
  /* Every table's dock, not 42's alone. These were scoped `#screen-42`, and
     Moon's bid chip and Fives' BOARD chip sit in the same slot at whatever
     size the base rule gave them — three sizes across one row, which Mike
     read as *"size is all over the place"* (§1AC.7). And one clamp for the
     whole row: the bid chip ran `3.6vw` against the score band's `3.3vw`
     beside it, a pixel apart at 430 and visibly so. */
  .my-dock .tr-chip-btn,
  .my-dock .dock-bid .ti-item {
    display: flex; align-items: center; justify-content: center; gap: .32em;
    width: 100%; min-height: 38px; white-space: nowrap;
  }
  .my-dock .tr-chip-btn .tr-head {
    font-size: clamp(10.5px, 2.9vw, 12px);
    letter-spacing: clamp(.4px, .25vw, 1.2px);
  }
  .my-dock .tr-chip-count { font-size: clamp(12px, 3.3vw, 15px); }
  /* The bid chip's type fills it, and its number carries the weight — the two
     halves of this row are a matched pair and should be lettered as one.
     Reported 17 Aug: the chip had room around its words and the number, which
     is the thing you read from across a table, was no heavier than the rest. */
  /* One size across the chip, and weight and colour do the rest.

     It ran three at once — 15px words, 17px number, 13.5px suit — and read as
     three things stuck together rather than one chip. Mike, 20 Aug: *"all text
     size to be the same so it looks better."* The number keeps its gold and its
     weight, which is what made it findable; it no longer also has to be
     bigger. §1E.3, §1F.2. */
  .my-dock .dock-bid .ti-item,
  .my-dock .dock-bid .ti-item b,
  .my-dock .dock-bid .ti-item small,
  .my-dock .dock-bid .ti-lbl { font-size: clamp(12px, 3.3vw, 15px); }
  .my-dock .dock-bid .ti-item b { color: var(--pill-hi); }
  .my-dock .tr-chip-btn .tr-chip-count,
  .my-dock .tr-chip-btn .tr-head { font-size: clamp(12px, 3.3vw, 15px); }
  /* Fills its half, like the chip opposite, and is therefore in the same place
     whether or not the chip is there yet — the chip only appears once a trick
     has been played, and a bid that slid sideways at that moment would be
     movement for nothing.

     `stretch` and not `center`, and the distinction cost a round trip: written
     as `center` here while a second rule further up said `stretch`, the two
     had identical specificity — one id and two classes each — so this one won
     for being lower in the file, and the bid stayed 159px against the chip's
     172. That is the fourth time in two days that source order at equal
     specificity has decided which rule applies, and the first where the two
     rules were both mine. The duplicate is deleted rather than re-ordered:
     one declaration, in one place. See §4.6. */
  #screen-42 .my-dock .dock-bid { grid-area: bid; justify-self: stretch; align-self: center; min-height: 38px; }
  /* The slot stays in the grid when it is empty, and holds the row's height.

     With the dock chip's bid gone (§1AA.5, `DOCK_BID_CHIP`), this slot is
     empty from the end of the auction until the first trick puts the SEE
     PLAYED chip in the other half of the row — and `.dock-bid:empty {
     display: none }` took the whole row with it, so the dock shrank by the
     chip's 38px and grew back a trick later. `pagecheck` caught it as the
     training hand's Next button moving 38px between steps: §1V.9's rule,
     "nothing may move, and the dock still does", broken by one word in
     `ui.js`. The 38 is the chip's own `min-height` above, so the row is the
     same height empty as full. */
  #screen-42 .my-dock .dock-bid:empty { display: flex; }
  #screen-42 .my-dock .dock-name { grid-area: name; justify-self: center; }
  #screen-42 .my-dock .my-hand { grid-area: hand; }
  #screen-42 .my-dock .dock-info,
  #screen-42 .my-dock .dock-pad { grid-area: info; }
  /* Moon's phone dock is the same grid (§1AE.48, 5 Sep): SEE PLAYED left,
     the bid-and-trump chip right, the hand, the score. No name row — Moon's
     plate is on the felt — and no pad. The two halves share every rule
     above (`.my-dock .tr-chip-btn`, `.my-dock .dock-bid .ti-item`), which is
     what makes them the matched pair 42's are. The score row's own
     `bidding…` chip is the desktop's; here the bid chip says it. */
  #screen-moon .my-dock {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-areas: 'rec bid' 'hand hand' 'info info';
    column-gap: 8px; row-gap: 4px;
  }
  #screen-moon .my-dock > .dock-mid,
  #screen-moon .my-dock .dock-head { display: contents; }
  #screen-moon .my-dock > .dock-left { grid-area: rec; justify-self: stretch; align-self: center; }
  #screen-moon .my-dock .dock-bid { grid-area: bid; justify-self: stretch; align-self: center; min-height: 38px; }
  #screen-moon .my-dock .dock-bid:empty { display: flex; }
  #screen-moon .my-dock .my-hand { grid-area: hand; }
  #screen-moon .my-dock #mn-info { grid-area: info; }
  #screen-moon .my-dock #mn-info .ti-bid { display: none; }
  /* Game and Hand on one line. At the full pill size the two of them come to
     374px inside 382 and wrap, which costs a whole band of height directly
     above the safe area — 72px where 32 will do. They are glanceable numbers,
     not reading matter; the bones are the reading matter and they got the
     width instead. */
  .dock-info .table-info { gap: 6px; }
  /* Grown into the row the bid vacated — but along the axis that was actually
     freed, and only as far as the narrowest phone allows.

     Measured 16 Aug: the two pills come to 343px. At 390 that leaves 39px
     spare; at 360 it leaves **nine**. So a flat font bump is not available —
     it wraps on the smallest screen, which is the thing the note above this
     block was already guarding against, and a wrap here costs a whole band of
     height directly above the safe area.

     So the type scales with the screen instead of stepping at a breakpoint:
     `clamp()` holds it at today's 13.5px at 360, and lets it reach 15px on a
     430px phone that has the room. The horizontal padding is deliberately
     untouched, because that is the number that would eat the nine pixels. The
     vertical padding is where the freed row goes, which is the room the bid
     gave up. */
  /* And the score band the same: one size, three weights. §1E.3, §1F.2.

     Everything in the chip, including the label and the caret — GAME was 10.5px
     against 15 for the score beside it, and the caret 12, so three sizes sat on
     one line and the uppercase label rode visibly higher than the words next to
     it. Reported 20 Aug: *"Game is obviously higher than Us and a different font
     size."* Uppercase, letter-spacing and opacity still tell the label from the
     score; the size no longer has to. */
  .table-info .ti-item,
  .table-info .ti-item b,
  .table-info .ti-item small,
  .table-info .ti-lbl,
  .table-info .ti-swap { font-size: clamp(12px, 3.3vw, 15px); }
  /* Tighter, because one type size made the chips wider: the labels went from
     10.5px to the same 15 as the score, and the two pills stopped fitting on
     one line below 430. The size is the thing that was asked for, so the width
     comes back out of the padding and the separators instead. */
  .table-info .ti-item { padding: 6px 8px; }
  .table-info .ti-item small { opacity: .6; }
  /* Centred as boxes rather than sat on a shared baseline: with one size the
     boxes are one height, and centring survives a label that wraps where a
     baseline does not. */
  .table-info .ti-item { display: flex; align-items: center; justify-content: center; gap: .22em; }
  .table-info .ti-lbl { margin-right: 0; }
  /* And the same numbers again for Big print, at a specificity that wins.

     **This is a bug fix, not a preference.** `html.big-print .ti-item` sets
     `1.06em`, and against the rule above — three classes, no element — it wins
     on specificity, everywhere, silently. So the phone size that exists
     *precisely* to stop the score band wrapping was never the size Big print
     used: it took an em off the inherited body size instead, and the band
     wrapped on **all four phone widths**. It had been doing that since the day
     the 13.5px was written; nothing measured it until `fitcheck` was taught to
     look on 16 Aug. Same shape as the §1B.5 badge rule — correct, written
     down, and never the one that applied — by specificity here rather than by
     source order.

     Why Big print does not simply get a larger band: it cannot have one. The
     pair of pills come to 343px against 352px of glass at 360, so there is no
     room to be bigger without wrapping, and a wrap costs more height than the
     larger type wins. That is the same compromise `--bone` already makes and
     documents — on a narrow phone the screen is the limit, both print sizes
     arrive at the same number, and that is arithmetic rather than a
     regression. On a wider phone the `vw` term gives Big print the same growth
     it gives everyone.

     If the band is ever to be genuinely larger in Big print, the pills have to
     shed something — the `to 7` target is the obvious candidate — rather than
     the type getting smaller. Filed rather than done. */
  /* Big print keeps one size across the chip too — it had three of its own,
     including a 17px number and a flat 12px `small`, which is the same fault
     the ordinary band had and the same answer. Slightly larger than ordinary,
     as the setting promises, and still one line: at 360 the two pills wrapped
     until the ceiling came down from 17 to 16. §1E.3. */
  html.big-print .table-info .ti-item,
  html.big-print .table-info .ti-item b,
  html.big-print .table-info .ti-item small,
  html.big-print .table-info .ti-lbl,
  html.big-print .table-info .ti-swap { font-size: clamp(12.5px, 3.4vw, 16px); }
  .table-info .ti-lbl { margin-right: 4px; }
  .dock-pad {
    margin-top: 8px; justify-content: center; align-items: start;
    overflow: hidden;
  }
  #screen-42 .dock-pad { display: none; }
  #screen-42 .my-dock.pad-open .dock-info { display: none; }
  #screen-42 .my-dock.pad-open .dock-pad { display: flex; }
  /* The pad is no longer squeezed into a side strip, so it can be read. */
  .dock-pad .score-pad { zoom: .82; transform: rotate(-1.2deg); }
  /* Big print's pad is `transform: scale(1.14)` everywhere else, and a
     transform paints at the new size while reserving the old one — the same
     fault as the BACK button in §1X.4 — so on a phone the enlarged pad hung
     over the Score button under it by about 10px (`fit-430-std.png`,
     §1AA.11; measured 3 Sep at 430, 390 and 360). `zoom` lays out at the size
     it paints. */
  html.big-print .dock-pad .score-pad { zoom: .94; transform: rotate(-1.2deg); }
  /* The arrow that says the score is a door rather than a label. */
  .ti-swap { display: inline; margin-left: 8px; opacity: .55; font-size: 12px; }
  .dock-pad { flex-direction: column; align-items: center; gap: 6px; }
  #screen-42 .my-dock.pad-open .pad-close {
    display: block; background: rgba(0,0,0,.4); color: var(--cream);
    border: 1px solid rgba(255,255,255,.18); border-radius: 20px;
    padding: 5px 16px; font-size: 13px; min-height: 30px;
  }

  /* **Big print goes back to one column on a phone**, and that is the right
     answer rather than a retreat. Side by side, big bones leave the pad about
     seventy pixels, and a pad shrunk to fit seventy pixels is unreadable — so
     the layout would be spending its whole budget making the one thing big
     print exists for compete with the one thing it can afford to make small.
     Bones as large as they go, pad underneath at a size somebody can actually
     read. Caught by `fitcheck.js`, which now checks the pad is on the screen
     at all — the dock clips what will not fit, so a pad running off the edge
     is neither an overlap nor a page overflow. It simply vanishes. */
  html.big-print .dock-mid {
    display: flex; flex-direction: column; align-items: center;
  }
  /* The hand stays a four-column grid even here. Letting it wrap gave 5/2,
     which is the reshuffling hand the fixed split exists to prevent — big
     print does not get to opt out of that. */
  html.big-print .my-hand { justify-content: center; }
  /* Three across rather than four. Once the bone is its cell, four across is
     the same size for everybody and Big print has no way to be bigger — which
     `fitcheck` refuses, and rightly: the whole point of the setting is larger
     bones. A third fewer to a row is a third wider each. */
  html.big-print .my-hand { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  html.big-print .dock-pad { margin-top: 8px; justify-content: center; width: 100%; }
  html.big-print .dock-pad .score-pad { zoom: .8; }
  /* **Fives has no pad on a phone.** Mike's call, 15 Aug, and the shape of the
     game is the argument: the Fives pad is a running column of Xs that grows
     down the screen all game, where the 42 pad is one word that never gets
     taller. 42's fits in the band under the hand and swaps in and out of it;
     Fives' does not, at any zoom worth reading — it was already down at .42 on
     a narrow phone, which is small enough to be decoration rather than a
     score. The running total stays on the status line where it always was, so
     nothing is lost but the paper. The pad is still there on a laptop, where
     there is height to spare. */
  #screen-fives .dock-pad { display: none; }}
@media (max-width: 380px) {
  /* The narrowest phone anybody still carries. It used to need its own two
     bone sizes; it does not any more, because the formula above is already a
     function of the screen and lands on 85 here without being told. Two
     numbers deleted rather than kept in step. */
  .dock-pad .score-pad { zoom: .55; }}

/* ---------- the turn token ----------
   The complaint was that play felt jerky: you tapped, four things changed at
   once, and the turn had moved on with nothing to show it. Nothing was
   actually fast — what was missing was continuity. A token that slides from
   seat to seat gives the eye something to follow, and turns four instant state
   changes into one movement you can watch.

   It is deliberately a small brass marker rather than an arrow or a glow: at a
   real table something physical usually marks who is up. */
/* `.turn-token` stood here: a small brass marker that slid from seat to seat,
   so that the turn changing was something you watched rather than something
   you noticed afterwards. The idea was right and the mechanism was not. It
   was placed by measuring the seat it was going to, and the seats changed
   shape three times in one week — so it landed on Bobo's DOWN chip, on
   Becky's stack of backs, and once in open felt anchored to nothing.

   Whose turn it is is a notch inside the seat now: `.su-turn`, drawn with the
   seat, at the seat's left edge. It cannot go stale because there is no
   measurement, and it cannot land on anything because it is part of the thing
   it would have landed on. BACKLOG 1M.10, 1S.2, 1R.8. */

/* A bone arriving on the table settles rather than appearing. Small, and it is
   most of the difference between "things changed" and "somebody played". */
.trick-slot .domino { animation: bone-land 260ms cubic-bezier(.2,.9,.3,1) both; }
@keyframes bone-land {
  from { opacity: 0; transform: translateY(-11px) scale(.94); }
  to   { opacity: 1; transform: none; }}

/* A trick row is four bones wide and has to fit the box without clipping the
   points off the end, so the gaps are tight and the box is sized from the
   content rather than the content squeezed into the box. */
/* Room to breathe. The bones used to sit flush against each other, which read
   as one long domino rather than four. */
/* Room to breathe, scaled with the bones. */
.trick-record .tr-row { gap: 5px; padding: 3px 6px; }
.trick-record .tr-bones { gap: 4px; align-items: stretch; }
.trick-record .tr-stack { gap: 2px; }
/* A row is exactly as tall as an upright bone, so a stack of two flat ones
   fills it and a row with no counts in it is no shorter.

   That equality used to hold by arithmetic on fixed numbers — an upright bone
   was 56 tall and two flats of 28 with a 2px gap came to 58, near enough that
   nothing noticed. Once the bone scales with the column (see `--rec-bone`) the
   two drift apart properly: at 1500px an upright is 67.75 and a stack is
   69.75, and `pagecheck` caught rows at two different heights.

   So it is stated rather than arrived at. The row is one bone tall, and the
   two flats in a stack are each half of that *less half the gap between them*
   — so a stack is exactly a bone tall as well, and every row matches whatever
   is in it. The gap survives, because bones flush against each other read as
   one long domino, which is what it was put there for. */
.trick-record .tr-bones { min-height: var(--rec-bone, 38px); }
.trick-record:not(.tr-chip) .tr-stack .domino.small.h {
  height: calc((var(--rec-bone) - 2px) / 2);
}
.trick-record:not(.tr-chip) .tr-stack .domino.small .half {
  height: calc((var(--rec-bone) - 2px) / 2);
}
.trick-record .tr-num { min-width: 13px; }
.trick-record .tr-pts { min-width: 17px; text-align: right; }


/* The status line carries one line or two depending on what just happened, and
   the felt sat under it — so the whole table stepped up and down as the
   commentary changed length. Pinned. */
.status-line { min-height: 66px; display: flex; flex-direction: column;
               align-items: center; justify-content: center; gap: 4px; }

/* Both plates and the commentary line are pinned to a height that fits their
   fullest state. Every one of these was found by a test that plays a hand and
   asserts nothing moves: a badge appearing, or the turn strip coming back after
   "Buck takes the trick", was enough to step the whole felt three pixels. Three
   pixels, four times a trick, is what "jerky" was made of. */
.status-line { height: 70px; min-height: 70px; }
/* Three plates were pinned here, at three sizes, arrived at on three separate
   days: the side seats 124px tall, the north seat 300x78, and a comment on
   each explaining which badge had made which thing move. They were right about
   the fault every time and each fix reached one seat. The unit reserves all of
   it, once. BACKLOG 1M.2, 1M.3, 1R.5, 1R.8.

   What is still needed is the seat's width, because the side columns have to
   be wide enough for a name: 92px truncated "Darlene" to "Darl..." and that is
   the number this has to stay clear of. */
/* Wide enough for a name AND its pencil on one line, which is the whole of
   what this number is for. At 148/96 the pencil was pushed onto a line of its
   own and sat under the name like a stray mark; the column then wanted four
   rows and overflowed the three it reserved. Measured at all six widths:
   the desktop column needs 168 and the phone's needs 118. */
.seat-w, .seat-e { width: 168px; }
@media (max-width: 899px) { .seat-w, .seat-e { width: 118px; }}

/* ==========================================================================
   Mark B, sunk into the table
   ==========================================================================
   The badge belongs on the table the way a hall's name belongs on its felt:
   big enough to be the table's, quiet enough that nobody looks at it twice.
   It covers roughly half the open middle where the bones are played, sits
   under everything, and takes no clicks.

   Two copies are rendered. On three of the four tables the second is hidden;
   on Northgate it's the other half of a carved letter — a dark cut with a
   lit lower lip, which is the same trick the carved names use. */
.table-mark {
  /* Centred in the open middle, not in the whole felt. The felt includes your
     partner's seat along its top edge, so centring against it put the mark hard
     up under his bones with all the slack underneath — three pixels above and a
     hundred and thirteen below. Starting the box below his seat puts the same
     air on both sides. The seat is a fixed height, which is what makes this a
     number rather than a measurement taken at runtime. */
  position: absolute; inset: var(--mark-top, 124px) 0 0;
  display: grid; place-items: center;
  pointer-events: none; overflow: hidden;
}
/* Fives seats its three opponents in a single shallow row rather than one seat
   across the top, so the open middle starts higher and the number that clears
   it is smaller. Same rule, measured against this table instead of that one. */
#tf-felt { --mark-top: 96px; }
@media (max-width: 899px) { .table-mark { inset: 0; }}
/* ---------- the mark on the felt, on a phone ----------

   Reported 14 August: on a phone the mark is distracting on every table except
   **The Hall**, where it belongs. On a desktop it is right on all four and
   stays — the complaint was specifically about the small screen, where the felt
   is mostly hand and the mark ends up sitting under the bones rather than
   behind a table.

   The Hall is the exception on purpose: it is named for a real place, the seal
   is a different mark from the others, and it is part of why somebody picks
   that table. */
@media (max-width: 560px) {
  body:not(.theme-kc) .table-mark { display: none; }
}
.table-mark svg {
  grid-area: 1 / 1;
  /* Sized against the felt rather than the window, so it stays the same share
     of the open middle whatever else is on screen. It was pinned to viewport
     units, and when the two corners moved off the felt and into the band below
     it the felt got shorter — the mark stayed the same and went from covering
     half the table to almost all of it. */
  /* Sized from the table's **width**, which is the one dimension that does not
     move during a hand. Height was wrong because the felt's height changes as
     the board grows and the dock fills — Mike, 22 Aug: *"the table watermark is
     changing sizes as the table does, needs to be static."* And viewport units
     were wrong in the other direction, which is what the paragraph above
     records: when the record and the talk moved off the felt the felt got
     shorter, the mark did not, and it went from covering half the table to
     almost all of it. Width is the answer to both. BACKLOG 1N.9. */
  width: min(56%, 680px); height: auto; max-height: 92%;
  color: var(--tm-ink, #ffffff);
  opacity: var(--tm-alpha, .055);
}
/* The second copy is the carving's lower lip and is off unless a table asks. */
.table-mark svg + svg { display: none; }

/* Line art is thin by design — it is drawn to be stitched or cut, so there is
   no fill anywhere in it and every ring, letter and rule is a stroke. At the
   alpha a shaded badge wants, it very nearly vanished. Same apparent weight:
   more opacity, far less to be opaque. */
.table-mark svg.mark-b-line { opacity: var(--tm-emboss-alpha, .22); }

/* The Hall's is the seal itself, in its own red and navy. No `color`, no ink
   variable, no carve — only enough alpha to sit behind a hand of dominoes
   without competing with it. Colour on a bright steel table reads far stronger
   than one pale ink does, so this number is well under the others. */
.table-mark svg.mark-b-full,
.table-mark svg.mark-b-seal { opacity: .085; }

/* One mark, one ink, and each room says what the ink is. Three shades used to
   be set here for the flag inside the old shaded badge — that badge is gone,
   and the emboss deliberately doesn't want them: a single colour is what makes
   a mark read as pressed into a surface rather than printed on it. */
body {
  --tm-ink: #eafff2; --tm-alpha: .13;
}
body.theme-slate {
  --tm-ink: #cfe4f7; --tm-alpha: .12;
}
/* The Hall gets no ink of its own — its mark is the seal, in the seal's
   colours — but the variable stays defined for anything else that reads it. */
body.theme-kc {
  --tm-ink: #ffffff; --tm-alpha: .14;
}

/* ---------- Northgate: cut into the wood ----------
   A carve is not a colour, it is two edges: the cut face in shadow and the
   lower lip catching the light from above. Two copies of the same mark,
   offset by a pixel and a half, do it — the same way the names carved into
   the bar are done a few hundred lines up. */
body.theme-northgate .table-mark svg {
  color: #170c02; opacity: .3;
  transform: translateY(-1px);
  filter: blur(.2px);
}
body.theme-northgate .table-mark svg + svg {
  display: block;
  color: #ffdda6; opacity: .22;
  transform: translateY(1.5px);
  filter: blur(.35px);
}

/* The mark sits behind its siblings by going one layer below them rather than
   by pushing everything else up. The first attempt did the opposite — a blanket
   `position: relative` on every sibling — which quietly un-absoluted the two
   bottom corners, put them back in the flow, and grew the table by 210px
   part-way through a hand. The stability test caught it; the lesson is that a
   rule broad enough to be convenient is broad enough to hit something you
   weren't thinking about. */
.table-mark { z-index: -1; }

/* Two badges plus a name inside a side seat is more than fits across, and BID
   was getting its D clipped off at the edge of the table. The column width is
   set by the bones and those are the right size, so the width is not available.

   That was first answered by shrinking the badges — 9px type, which is smaller
   than anything else on the table — and it was the wrong axis. Reported 16 Aug:
   the opponents' badges are too small to read, and they should stack rather
   than sit in a row the way yours does, because your own plate has the width
   for a row and a 92px column never will.

   So spend height instead of width. Stacked, each badge has the whole column
   to itself, which buys the type back to 12px — larger than the 10.5px base
   rather than half of it — and BID cannot be clipped by anything.

   The reserved height is the part to be careful with, and it is the same rule
   as `.seat-badges` above: badges come and go during a hand, and a plate that
   changes height moves the bones underneath it while somebody is reading them.
   Two rows are reserved, which covers every ordinary hand — SHAKER with DOWN,
   or BID with DOWN.

   The honest limit: three can co-occur, when the same opponent shook, holds
   the bid and is on turn, and that hand gets a third row and shifts that one
   seat's bones down by ~24px. Reserving for three costs ~69px of permanently
   empty felt under every opponent for a case most hands never see, and on a
   phone that is the more expensive mistake. Recorded rather than discovered. */
/* The side seats' badges stacked into a column, reserving two rows out of a
   possible three and recording the third as an honest limit. The unit lays
   them across in a reserved box instead, which fits all three at every width
   the side column has, so the limit is gone rather than documented. */

/* ---------- naming and sending a table ---------- */
.name-row { align-items: center; gap: 10px; }
.name-input {
  background: rgba(0,0,0,.35); border: 1px solid rgba(224,170,62,.4);
  border-radius: 8px; color: var(--cream); padding: 9px 12px;
  font-family: inherit; font-size: 15px; flex: 1 1 190px; min-width: 150px;
}
.name-input::placeholder { color: rgba(245,239,224,.4); }
.send-btn { flex: none; white-space: nowrap; }
.invite-box {
  width: 100%; background: rgba(0,0,0,.4); color: var(--cream);
  border: 1px solid rgba(224,170,62,.35); border-radius: 9px;
  padding: 11px 13px; font-family: 'Courier New', monospace; font-size: 13px;
  line-height: 1.6; margin: 8px 0 14px; resize: vertical;
}

/* ---------- only three turned up ----------
   Sat where the problem is felt rather than on the home screen, where nobody
   is yet thinking about how many people are coming. */
.short-nudge {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border: 1px solid rgba(224,170,62,.28); border-radius: 10px;
  background: rgba(0,0,0,.2); padding: 11px 13px; margin: 4px 0 12px;
}
.short-nudge b { color: var(--pill-hi); }
.short-nudge span { flex: 1 1 240px; font-size: 13.5px; opacity: .86; line-height: 1.5; }
.short-nudge .ghost-btn { flex: none; }

/* The table's name, at the top of the table it belongs to. */
.table-called {
  text-align: center; font-size: 13px; letter-spacing: .09em;
  text-transform: uppercase; opacity: .62; color: var(--cream);
  padding: 2px 10px 0;
}

/* How much room is left in a table name. Silent until it matters — a counter
   that is always on reads as a restriction, one that appears at eight
   characters left reads as help. */
.name-count { flex: none; font-size: 12px; opacity: .55; min-width: 46px; }
.name-count.tight { opacity: .95; color: var(--pill-hi); }

/* The caption can't break the table even if a name somehow gets past the
   limit — a stored name from an older release, or a browser that ignores
   maxlength. One line, always. */
.table-called {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  max-width: 100%;
}

/* A note somebody left, as opposed to a score somebody kept. Brighter and less
   worn than the running tally, because the point of these is that you can
   almost read them — rubbed to the edge of legible rather than past it. */
.chalk.chalk-note {
  color: rgba(240, 246, 251, .5);
  letter-spacing: 1px;
  -webkit-mask-image: linear-gradient(76deg, #000 0 42%, rgba(0,0,0,.34) 62%, #000 84%);
          mask-image: linear-gradient(76deg, #000 0 42%, rgba(0,0,0,.34) 62%, #000 84%);
}

/* Between a phone and a wide desktop there is room for the tricks beside your
   hand but not for an empty chat panel as well. The talk column is the one that
   goes, because it is holding space for something that doesn't exist yet and
   the hand is holding actual bones. */
@media (min-width: 900px) and (max-width: 1240px) {
  .my-dock { grid-template-columns: minmax(150px, var(--dock-side)) minmax(0, 1fr); }
  .dock-right { display: none; }
}

/* ---------- the setup screen, tightened ----------
   This is the one screen everybody scrolls through before every single game,
   so the two boxes on it that earn their place — who's playing, and the answer
   when only three turned up — should say their piece and get out of the way.
   Same content, less height. */
.rules-panel .seat-row { padding: 7px 10px; margin-bottom: 8px; }
.rules-panel .seat-row .rule-name small { line-height: 1.3; }
.rules-panel .name-row { padding: 7px 10px 6px; gap: 8px; }
.rules-panel .name-row .rule-note { margin: 4px 0 0; font-size: 12.5px; line-height: 1.45; }
.short-nudge { padding: 8px 11px; margin: 2px 0 10px; gap: 8px; }
.short-nudge span { font-size: 12.5px; line-height: 1.4; flex: 1 1 200px; }
.short-nudge .ghost-btn { padding: 6px 13px; font-size: 13px; }

/* ---------- the table picker, on the home screen ----------
   The four tables carry most of this app's personality and they were behind a
   button and a modal, which is a lot of ceremony for a choice you might make on
   a whim. Four names in a row with the current one lit shows you what else
   there is without opening anything, and the lit block slides rather than
   jumping so switching reads as one selection moving.

   The blurb appears on hover or keyboard focus. Four descriptions permanently
   on screen would shout louder than the rooms they describe. */
/* Label and slider are one object, centred together — so the slider itself
   sits a little right of centre, which is what you want when something is
   introducing it. */
.theme-row {
  display: flex; align-items: center; justify-content: center;
  gap: 14px; flex-wrap: wrap;
  margin: 24px auto 20px; max-width: 760px;
}
.theme-lead {
  flex: none; font-size: 14.5px; letter-spacing: .02em;
  color: var(--ts-lead, var(--table-text)); opacity: .85; white-space: nowrap;
}
.theme-slider {
  position: relative;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 2px; padding: 4px;
  flex: 0 1 520px; min-width: 0;
  border: 1px solid var(--ts-edge, var(--link-brd)); border-radius: 26px;
  background: var(--ts-bg, rgba(0, 0, 0, .18));
  /* Nothing inside this row may widen the page. See the note on `.ts-tip`:
     a hidden tooltip was doing exactly that and made the whole app zoomable.
     `clip` and not `hidden` — `hidden` would force the vertical axis to
     scroll and trap the bubble inside the strip. */
  overflow-x: clip; overflow-y: visible;
}
/* One block, moved — not four blocks switched on and off. */
.ts-thumb {
  position: absolute; top: 4px; bottom: 4px; left: 4px;
  width: calc((100% - 8px) / var(--ts-n, 4));
  transform: translateX(calc(100% * var(--ts-i, 0)));
  border-radius: 22px;
  background: var(--ts-lit, rgba(224, 170, 62, .22));
  box-shadow: inset 0 0 0 1px var(--ts-lit-edge, rgba(224, 170, 62, .55));
  transition: transform 280ms cubic-bezier(.32, .78, .3, 1);
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) { .ts-thumb { transition: none; }}

.ts-opt {
  position: relative; z-index: 1;
  background: none; border: none; color: var(--ts-ink, var(--table-text));
  font-family: inherit; font-size: 14px; padding: 9px 6px;
  border-radius: 22px; min-height: 38px;
  opacity: .72;
  /* Not `overflow: hidden`. It was, to keep a long name from bursting its
     column — and it clipped the tooltip, which lives above this button and is
     therefore outside it. Nothing in the computed styles says so: the tooltip
     reads as visible, opacity 1, sitting at a sensible place on screen, and is
     simply not painted. The truncation moved to the label, which is the thing
     that actually needed truncating. */
  overflow: visible;
}
.ts-name {
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ts-opt.on { opacity: 1; font-weight: bold; }
.ts-opt:hover { opacity: 1; }

/* The blurb. Sits above the row so it never covers the row itself, and is
   pinned to the middle so a long one can't push the page sideways. */
/* The tooltip is centred on its chip, and on a phone the end chips are close
   enough to the edges that a 260px bubble hangs off both sides. It is
   `visibility: hidden` until you hover — but hidden is not gone: an absolutely
   positioned element still extends the page's scrollable area, so an invisible
   tooltip was making the home screen **450px wide on a 390px phone**.

   That is the whole of the Android zoom-out, and it is the missing half of
   §1B.10. A page wider than the glass can be pinched out; every panel is then
   centred on 450px rather than on the screen, so it sits off to one side with
   its far edge past the edge of the phone. Nothing was ever wrong with the
   panels, which is exactly why nothing about the panels ever measured wrong.

   Two changes, and the second is the one that stops it coming back. The bubble
   is bounded to the row rather than to the chip below 620px — it spans the
   strip, which reads better on a narrow screen anyway. And the slider clips
   horizontally, so no future decoration inside it can widen the page. `clip`
   rather than `hidden` on purpose: `overflow-x: hidden` forces the other axis
   to `auto` and would trap the bubble inside the row, while `clip` leaves
   `overflow-y: visible` free to let it rise above. */
.ts-tip {
  position: absolute; left: 50%; bottom: calc(100% + 10px);
  transform: translateX(-50%) translateY(4px);
  width: max-content; max-width: min(300px, 78vw);
  background: rgba(18, 14, 10, .96); color: var(--cream);
  border: 1px solid rgba(224, 170, 62, .38); border-radius: 10px;
  padding: 9px 12px; font-size: 12.5px; line-height: 1.5; font-weight: normal;
  white-space: normal; text-align: left;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .45);
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 140ms ease, transform 140ms ease;
}
.ts-opt:hover .ts-tip, .ts-opt:focus-visible .ts-tip {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
}
@media (max-width: 520px) {
  .theme-slider { max-width: 100%; }
  .ts-opt { font-size: 12.5px; padding: 8px 3px; }
  /* Bounded to the strip rather than to the chip. `.theme-slider` is the
     positioned ancestor, so pinning both edges keeps the bubble inside the row
     however near the end its chip sits. */
  .ts-tip {
    left: 0; right: 0; transform: translateY(4px);
    width: auto; max-width: none;
  }
  .ts-opt:hover .ts-tip, .ts-opt:focus-visible .ts-tip { transform: translateY(0); }}

/* ---------- the picker takes the room's colours ----------
   The Hall is a bright speckled tabletop and the default treatment — pale type
   on a dark wash — was washed out on it. Each room gets ink and a lit block
   that work against its own surface rather than one set that mostly works. */
/* The green table is the default and carries no class of its own — applyTheme
   only *adds* a class for the other three. `body.theme-standard` therefore
   never matched anything, and every variable set under it was quietly doing
   nothing. Defaults belong on `body`. */
body {
  --ts-lead: #f0e6cf; --ts-ink: #f5efe0;
  --ts-bg: rgba(0,0,0,.22); --ts-edge: rgba(224,170,62,.34);
  --ts-lit: rgba(224,170,62,.26); --ts-lit-edge: rgba(224,170,62,.6);
}
body.theme-northgate {
  --ts-lead: #ffdda6; --ts-ink: #fbe9cd;
  --ts-bg: rgba(28,14,3,.34); --ts-edge: rgba(255,221,166,.3);
  --ts-lit: rgba(255,221,166,.24); --ts-lit-edge: rgba(255,221,166,.62);
}
body.theme-kc {
  --ts-lead: #2f2419; --ts-ink: #2b2118;
  --ts-bg: rgba(255,255,255,.5); --ts-edge: rgba(60,45,30,.35);
  --ts-lit: rgba(141,26,18,.22); --ts-lit-edge: rgba(141,26,18,.75);
}
body.theme-kc .ts-opt.on { color: #7d1610; }
body.theme-slate {
  --ts-lead: #cfe4f7; --ts-ink: #e8eef5;
  --ts-bg: rgba(255,255,255,.07); --ts-edge: rgba(158,200,240,.3);
  --ts-lit: rgba(158,200,240,.2); --ts-lit-edge: rgba(158,200,240,.65);
}

/* Every table's cards are the same height, so the picker underneath them sits
   at the same place whichever room you are in. The Hall's cards were shorter
   and the row visibly stepped up when you chose it. */
.game-card { min-height: 232px; }
@media (max-width: 560px) { .game-card { min-height: 0; }}

/* ---------- a room changes the colours, not the layout ----------
   Every table had drifted to its own padding and border width — 16/15 and 2px
   in the Hall, 22/24 and 3px on Northgate, 1px on the slate — and with
   border-box sizing that moves the contents. Switching rooms nudged the game
   names up and down by a few pixels each time, which is the sort of thing you
   feel before you can name it.

   The box is fixed here, last, so no room can take it back. Weight and colour
   still belong to the room; they are drawn with inset shadows, which cost no
   layout. */
.game-card { padding: 22px 24px; border-width: 2px; }

/* ============ the lobby ============
   Four chairs round a table, drawn from above. The thing being decided here is
   not "am I in" but "who am I playing with", so the picture has to make the
   partnerships visible before anybody taps: the chair across from you is your
   partner, and that is a fact about the shape rather than a label you read.

   Reservation layout, as everywhere in this project — the table holds its full
   size from the first paint, so chairs filling up moves nothing. A lobby that
   reflows every time somebody walks in is a lobby where you tap the wrong
   chair. */
.lobby-wrap {
  --panel-pad-x: 16px; --panel-pad-t: 8px;
  max-width: 760px; margin: 0 auto;
  padding: var(--panel-pad-t) var(--panel-pad-x) 28px;
}
/* Two columns on a laptop (Mike, 11 Sep): the table on the left, the chat
   and the host's controls on the right, so the whole screen fits 768 tall.
   One column under 900px — a phone reads top to bottom, chairs first. */
.lobby-cols { display: block; }
@media (min-width: 900px) {
  .lobby-wrap { max-width: 1000px; }
  .lobby-cols { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 0 24px; align-items: start; }
  .lobby-side { padding-top: 14px; }
  .lobby-side .lobby-chat { margin-top: 0; }
  .lobby-side .lobby-lines { height: 220px; }
}
.lobby-sub { margin-bottom: 8px; }
/* The guest's one-line code under the table: the word, the code, Copy and
   Send in a row, smaller than the host's block because it is not the
   guest's job. */
.lobby-code.compact { margin: 4px 0 10px; gap: 8px; justify-content: center; }
.lobby-code.compact code { font-size: 14.5px; padding: 5px 9px; }
.lobby-code.compact .ghost-btn { padding: 7px 11px; font-size: 13.5px; }
.lobby-code-word { font-size: 13.5px; opacity: .75; }
.lobby-partner-note { text-align: center; margin: 2px 0 8px; font-size: 13.5px; opacity: .8; }
.lobby-code {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 6px 0 14px;
}
.lobby-code code {
  font-size: 19px; letter-spacing: .5px; color: var(--gold);
  background: rgba(0,0,0,.32); border: 1px solid rgba(224,170,62,.35);
  border-radius: 8px; padding: 7px 13px; user-select: all;
}
/* `.ghost-btn` is a full-width block everywhere else in the app, which inside
   this flex row would put each button on a line of its own and push the code
   away from the thing you do with it. Here they sit beside it. */
.lobby-code .ghost-btn { width: auto; flex: 0 0 auto; margin-top: 0; padding: 10px 14px; }
/* One button standing in for a name field, a fold and a trip to the profile. */
.lobby-me {
  display: flex; align-items: center; gap: 9px; text-align: left;
  width: 100%; margin-bottom: 10px;
}
.lobby-me .lm-face { font-size: 22px; line-height: 1; }
.lobby-me .lm-name { font-weight: bold; }
.lobby-me small { margin-left: auto; opacity: .6; font-size: 12.5px; }
.lobby-me.needs-name { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(224,170,62,.18); }
/* The prompt under the chairs that offers to start with bots. Shaped like the
   other full-width lobby buttons, and gold-edged because it is the one thing on
   the screen a host waiting on a fourth actually wants. */
.lobby-fill {
  display: block; width: 100%; text-align: center; margin: 2px 0 12px;
  border-color: var(--gold); font-weight: bold;
}
.lobby-fill small {
  display: block; margin-top: 3px; opacity: .6; font-size: 12.5px; font-weight: normal;
}
.lobby-state { font-size: 14px; opacity: .78; }
.lobby-state.is-off { color: #e08a62; opacity: 1; }

/* The square that holds the table. It exists only to bound the width, and it
   has to exist: `padding-top: 100%` resolves against the *containing block's*
   width, not against the element's own — so a 460px-wide table inside a 672px
   panel came out 672px tall. A circle on a phone and a clear oval on a laptop,
   which is exactly what was reported. Capping the width here means the
   percentage has the right number to work from. */
.lobby-table-wrap { width: 100%; max-width: 420px; margin: 0 auto; }
.lobby-table {
  position: relative;
  width: 100%;
  /* Square, so the four chairs sit at true north/south/east/west whatever the
     screen is. A percentage-padding box rather than aspect-ratio, because the
     latter is the one thing in this file an older tablet still gets wrong. */
  padding-top: 100%;
}
.lobby-felt {
  position: absolute; inset: 15%;
  border-radius: 50%;
  background: var(--felt, #1d5c3a);
  border: 3px solid rgba(0,0,0,.35);
  box-shadow: inset 0 0 40px rgba(0,0,0,.35);
  display: grid; place-items: center; text-align: center;
  padding: 12px;
}
.lobby-felt b { display: block; font-size: 15px; color: var(--cream); }
.lobby-felt span { font-size: 12.5px; opacity: .72; }

.lobby-chair {
  position: absolute;
  width: 30%; min-height: 96px;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 3px; padding: 8px 6px;
  border-radius: 12px;
  border: 2px dashed rgba(224,170,62,.35);
  background: rgba(0,0,0,.28);
  color: var(--cream); font-family: inherit; font-size: 13.5px;
  cursor: pointer; text-align: center;
  transition: border-color .15s, background .15s, transform .12s;
}
/* An open chair glows, slightly, and breathes — so a person arriving at a
   table knows the chairs are waiting for them (§1AF.39, Mike, 9 Sep: "glow
   slightly on the dotted yellow border so that it's obvious they need to
   select a seat"). Slighter than `.you`'s glow below, so the chair you are
   in still reads strongest; still, not breathing, for anyone who asked
   their device for less motion. Only a chair that can be taken: a taken
   one is solid, yours is brighter, a disabled one is not on offer. */
.lobby-chair:not(.taken):not(.you):not(:disabled) {
  border-color: rgba(224,170,62,.6);
  box-shadow: 0 0 10px rgba(224,170,62,.28);
  animation: chair-breathe 2.4s ease-in-out infinite alternate;
}
@keyframes chair-breathe {
  from { box-shadow: 0 0 6px rgba(224,170,62,.18); border-color: rgba(224,170,62,.5); }
  to   { box-shadow: 0 0 16px rgba(224,170,62,.42); border-color: rgba(224,170,62,.8); }
}
@media (prefers-reduced-motion: reduce) {
  .lobby-chair:not(.taken):not(.you):not(:disabled) { animation: none; }
}
.lobby-chair:hover:not(:disabled) { transform: scale(1.03); border-color: var(--gold); }
.lobby-chair:disabled { cursor: default; }
.lobby-chair.taken { border-style: solid; border-color: rgba(224,170,62,.55); background: rgba(0,0,0,.42); }
.lobby-chair.you { border-color: var(--gold); background: rgba(224,170,62,.16); box-shadow: 0 0 14px rgba(224,170,62,.25); }
.lobby-chair.away { opacity: .55; }
.lobby-chair .lc-face { font-size: 26px; line-height: 1; }
.lobby-chair .lc-name { font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lobby-chair .lc-tag { font-size: 11.5px; letter-spacing: .4px; text-transform: uppercase; opacity: .72; }
.lobby-chair.you .lc-tag { color: var(--gold); opacity: 1; }

/* North, east, south, west — the four positions the chairs are drawn in. Which
   seat number goes in which position is worked out per player, so that your own
   chair is always the near one and your partner is always the far one. */
.lobby-chair.at-n { top: 0; left: 35%; }
.lobby-chair.at-e { right: 0; top: 50%; transform: translateY(-50%); }
.lobby-chair.at-e:hover:not(:disabled) { transform: translateY(-50%) scale(1.03); }
.lobby-chair.at-s { bottom: 0; left: 35%; }
.lobby-chair.at-w { left: 0; top: 50%; transform: translateY(-50%); }
.lobby-chair.at-w:hover:not(:disabled) { transform: translateY(-50%) scale(1.03); }

.lobby-chat {
  margin: 18px 0 0; border: 1px solid rgba(224,170,62,.25);
  border-radius: 12px; background: rgba(0,0,0,.28); overflow: hidden;
}
.lobby-lines { height: 148px; overflow-y: auto; padding: 10px 12px; font-size: 14px; }
.lobby-lines p { margin: 0 0 6px; line-height: 1.4; }
.lobby-lines b { color: var(--gold); font-weight: 600; }
.lobby-lines .lc-empty { opacity: .6; font-style: italic; }
.lobby-say { display: flex; border-top: 1px solid rgba(224,170,62,.2); }
.lobby-say input {
  flex: 1; background: transparent; border: 0; color: var(--cream);
  font-family: inherit; font-size: 15px; padding: 11px 12px;
}
.lobby-say input:focus { outline: 2px solid rgba(224,170,62,.5); outline-offset: -2px; }
.lobby-say button {
  background: transparent; border: 0; border-left: 1px solid rgba(224,170,62,.2);
  color: var(--gold); font-family: inherit; font-size: 15px; padding: 0 16px; cursor: pointer;
}
@media (max-width: 420px) {
  .lobby-chair { min-height: 82px; font-size: 12.5px; }
  .lobby-chair .lc-face { font-size: 22px; }}

/* A name in the chat is a button, because muting somebody has to be reachable
   at the moment they are being unpleasant — not three screens away in a
   settings panel you would have to know exists. */
.lobby-lines .lc-who {
  background: none; border: 0; padding: 0; margin: 0 4px 0 0;
  font-family: inherit; font-size: inherit; font-weight: 600;
  color: var(--gold); cursor: pointer;
}
.lobby-lines .lc-who::after { content: ':'; }
.lobby-lines .lc-who:disabled { cursor: default; opacity: .75; }
.lobby-lines .lc-who:hover:not(:disabled) { text-decoration: underline; }

/* Six things people actually say while they wait. Most of the talking at a
   lobby is one of these, said with a phone in one hand — and a table where the
   quick way to speak is a fixed list is a table with far less to moderate. */
.lobby-quick {
  display: flex; flex-wrap: wrap; gap: 6px;
  padding: 8px 10px; border-top: 1px solid rgba(224,170,62,.16);
}
.lobby-quick button {
  background: rgba(224,170,62,.1); border: 1px solid rgba(224,170,62,.3);
  border-radius: 999px; color: var(--cream); font-family: inherit;
  font-size: 13px; padding: 5px 12px; cursor: pointer;
}
.lobby-quick button:hover { background: rgba(224,170,62,.2); border-color: var(--gold); }
.lobby-say input:disabled { opacity: .6; }
.lobby-say button:disabled { opacity: .45; cursor: default; }
.lobby-mute-note { margin: 8px 2px 0; font-size: 12.5px; opacity: .7; }

/* A row that folds. Passing one phone round a table is a real way to play and
   a minority one — it had a four-button row and a paragraph above the house
   rules, which everybody setting up an ordinary game had to read past. Shut,
   it says what it is in one line. Open, it is what it always was.

   Built on <details> rather than a class and a click handler, so it keeps its
   keyboard behaviour and its screen-reader announcement for free. */
.fold-row { margin: 6px 0 10px; }
.fold-row > summary {
  cursor: pointer; list-style: none;
  display: flex; flex-direction: column; gap: 2px;
  padding: 11px 14px;
  border: 1px solid rgba(224,170,62,.28); border-radius: 10px;
  background: rgba(0,0,0,.22);
  font-weight: 600; font-size: 15px; color: var(--cream);
  /* SHOW/HIDE is an absolutely positioned `::after`, and without this it had
     no positioned ancestor to hang from — so `right` was measured from the
     page rather than from this row, and the word sat outside the box it
     belongs to. Reported 16 Aug on the 42 setup screen and on Fives, which is
     the same screen with `is42` switched off. It was not width-dependent and
     it never had been; a full-width panel merely put the page's right edge
     near enough to the row's that it looked deliberate. */
  position: relative;
}
.fold-row > summary::-webkit-details-marker { display: none; }
.fold-row > summary small {
  display: block; font-weight: 400; font-size: 12.5px; opacity: .7;
}
.fold-row > summary::after {
  /* 14px is the summary's own right padding, so the word lines up with the
     edge of the text above it. It was 28 — a number that only made sense while
     this was being measured from the page. */
  content: 'Show'; position: absolute; right: 14px; top: 11px;
  font-size: 12.5px; font-weight: 600; letter-spacing: .4px;
  text-transform: uppercase; color: var(--gold); opacity: .85;
}
.fold-row[open] > summary::after { content: 'Hide'; }
.fold-row > summary:hover { border-color: var(--gold); }
.fold-row > summary:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
.fold-row[open] > summary { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.fold-row[open] > .rule-row {
  border: 1px solid rgba(224,170,62,.28); border-top: 0;
  border-radius: 0 0 10px 10px; margin-top: 0;
}

/* The name three strangers are about to see, changeable from the one screen
   where you notice it is wrong. */
.lobby-you { display: flex; align-items: center; gap: 8px; margin: 0 0 14px; }
.lobby-you > span { font-size: 22px; line-height: 1; }
.lobby-you input {
  flex: 1; min-width: 0; background: rgba(0,0,0,.35);
  border: 1px solid rgba(224,170,62,.4); border-radius: 8px;
  color: var(--cream); font-family: inherit; font-size: 15px; padding: 8px 12px;
}
.lobby-you .ghost-btn { margin: 0; width: auto; padding: 8px 14px; font-size: 14px; }

/* Four words with no label read as decoration — people were asking what they
   were for. */
.lobby-code-label { margin: 4px 0 6px; font-weight: 600; font-size: 15px; }
.lobby-code-label small { display: block; font-weight: 400; font-size: 12.5px; opacity: .72; }

/* Somebody who has never set a name shows up to three strangers as "Player".
   The box asks once, plainly, rather than letting it happen quietly. */
.lobby-you.needs-name input { border-color: var(--gold); box-shadow: 0 0 0 2px rgba(224,170,62,.18); }
.lobby-name-nudge { margin: -8px 0 12px; font-size: 13px; }

/* Nothing to look at — it exists so the panel has a height to scroll against.
   The record itself is absolutely positioned so it can be scaled from its top
   corner, and an absolute child is out of flow: without this the box reports
   itself as empty and `overflow-y: auto` has nothing to do. */
.hr-sizer { width: 1px; pointer-events: none; }

/* Table talk, at an online table. Small, out of the way, and it changes colour
   when somebody has said something you have not looked at — a table where you
   have to keep opening a drawer to check is a table nobody talks at. */
.talk-btn {
  position: absolute; top: 8px; right: 10px; z-index: 6;
  background: rgba(0,0,0,.42); border: 1px solid rgba(224,170,62,.35);
  border-radius: 999px; color: var(--cream); font-family: inherit;
  font-size: 13.5px; padding: 6px 13px; cursor: pointer;
}
.talk-btn:hover { border-color: var(--gold); }
/* ---------- and on a phone, under the record rather than over the rules ----------

   Reported 14 August: the bubble sat on the Rules button. Measured at 390px it
   genuinely overlapped — the bubble ran 304 to 380 and Rules ran 284 to 349,
   both of them at the top right, because each was written as "tuck it in the
   corner" without either knowing about the other.

   It goes under the trick record on the left instead, which is the space the
   record chip already owns and where the eye is between tricks. */
/* Gone from a phone since 11 Sep: it sat under the header in the top-left
   and Mike did not like it there; the chip in the seventh bone's cell is
   the phone's way in (§1AF.52, below). The laptop keeps the pill until the
   panel beside the table takes over. */
@media (max-width: 899px) {
  .talk-btn { display: none; }
}
.talk-btn.unread { border-color: var(--gold); background: rgba(224,170,62,.2); }
/* How many, not just whether. A dot says something happened; a number says
   whether it is worth stopping mid-bid for, which is the question somebody
   actually has. `attr()` reads the count the button already carries, so there
   is no second element to keep in step with it. */
.talk-btn.unread::after {
  content: attr(data-unread);
  position: absolute; top: -7px; right: -7px; min-width: 19px; height: 19px;
  padding: 0 4px; border-radius: 999px;
  background: var(--red); color: #fff;
  font-size: 11.5px; font-weight: bold; line-height: 19px; text-align: center;
  box-shadow: 0 1px 3px rgba(0,0,0,.55);
}
#screen-42 { position: relative; }

/* The look picker, folded away in the lobby. Same folding row as the setup
   screen, so there is one of these rather than two that drift. */
.lobby-look { margin: 0 0 14px; }
.lobby-look .rule-row { border: 1px solid rgba(224,170,62,.28); border-top: 0; margin-top: 0; }
.lobby-look .rule-row:last-child { border-radius: 0 0 10px 10px; }

/* ---------- the side seats on a phone ----------
   A block of overrides stood here: the side heads stacked, the name shrunk to
   13.5px and set to break anywhere, the badges argued about, the token nudged
   below the name because a stacked seat has no beside. All of it was making
   one seat survive a width that a row-shaped seat could not.

   `.seat-unit.su-col` is column-shaped at every width, so there is nothing to
   switch to and nothing to squeeze — the two things the block still does are
   about the felt, not the seat, and are what is left. BACKLOG 1R.8. */
@media (max-width: 700px) {
  /* The seats keep their own side of the felt rather than growing into the
     middle, which is what pushed the east seat off the screen edge.

     30 rather than 27, and the three points are a name. At 27% of 430 the side
     column gives its name 46px and "Becky" wants 44 — so she rendered as
     "Be..." in the shot grid, which is the same three characters that made the
     92px seats too narrow for "Darlene" back in August. A name is not the thing
     to give up. BACKLOG 1M.2.

     32 rather than 30, because 30 fixed 430 and left 360 still cutting her:
     the column is a name over a pencil, so what the name gets is the column
     less the pencil, and at 360 that came to 38px against the 42 "Becky"
     wants. 32% gives it 45 and the trick area keeps 36% of the row.

     A genuinely long name still ellipsizes here and that is the ellipsis
     doing its job — the line is a name rendered at no width, not a name
     rendered short. */
  .seats-mid > .seat { flex: 0 0 32%; min-width: 0; }
  .seats-mid > .trick-area { flex: 1 1 auto; min-width: 0; }
}

/* "trick 4 of 7" is the one line on the table that answers "how much of this
   hand is left", and it was 12px and dimmed to 60% — smaller than everything
   around it and the first thing to disappear on a phone. */
/* The trick label is the other half of the message above it, so it is set the
   same way: same colour, same weight, same shadow. They sit one on top of the
   other in the middle of the table and were arriving in different colours —
   *"those are all different colors... keep everything the same color and
   format."* One declaration for the pair; `.st-standing` above carries the
   identical values. BACKLOG 1V.7. */
.tp-c, .mn-empty, .tk-empty {
  font-size: 14.5px; font-weight: bold; letter-spacing: .2px;
  color: var(--status-text); text-shadow: 0 2px 6px rgba(0,0,0,.55);
}

/* §1F.6. The desktop got this at 19px in §3.19a and the phone kept 13.5,
   which is where it was reported from: *"trick 6 of 7 shown in the middle of
   the table. The text is way too small and it should all be on one line."*

   The wrap is the worse half. A two-line label sitting in the middle of the
   felt reads as a mistake rather than as small type, so `nowrap` matters more
   than the size does. `clamp` rather than a flat number because 360 in Big
   print is where the felt has least room for a line that cannot break. */
@media (max-width: 700px) {
  .tp-c, .mn-empty, .tk-empty { font-size: clamp(15px, 4.2vw, 18px); white-space: nowrap; }
}

/* ---------- names on a dark panel, in a light room ----------
   The Hall's accent is a deep navy, because its tabletop is pale — the right
   colour against that felt and the wrong one on the chat panel, which is dark
   in every room. Navy on near-black is invisible, and it made the names in
   table talk unreadable in that room only. Reported from a real table.

   So the pieces that sit on the dark panel get their own accent rather than
   the room's. It is stated once here and inherited by everything in the panel,
   which is what stops the next thing added to it from repeating the bug. */
.lobby-chat, .lobby-code, .chat-corner { --on-dark: #e8c46a; }
.lobby-lines b,
.lobby-lines .lc-who,
.chat-corner .cc-line i,
.lobby-code code,
.lobby-say button,
.lobby-quick button { color: var(--on-dark); }
.lobby-quick button { border-color: rgba(232,196,106,.35); background: rgba(232,196,106,.1); }
.lobby-quick button:hover { border-color: var(--on-dark); background: rgba(232,196,106,.2); }
.lobby-code code { border-color: rgba(232,196,106,.35); }
.lobby-chair.you .lc-tag { color: var(--on-dark, var(--gold)); }

/* ---------- table talk, in the corner it was reserved for ----------
   It was a button at the top of the screen that opened a drawer: one tap and
   one modal in front of saying a sentence, sitting where nothing else on this
   screen sits. The lobby puts chat in the bottom right, so people already look
   there. This is the same thing, in the same place, on the table. */
.chat-corner.live {
  display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start;
  gap: 6px; opacity: 1; pointer-events: auto; text-align: left;
  border-style: solid; border-color: rgba(224,170,62,.22);
  background: rgba(0,0,0,.28); padding: 9px 10px; cursor: default;
}
.chat-corner.live b { align-self: center; opacity: .75; }
.chat-corner .cc-lines {
  flex: 1; min-height: 0; overflow-y: auto; font-size: 12.5px; line-height: 1.4;
  scrollbar-width: thin;
}
.chat-corner .cc-lines p { margin: 0 0 4px; }
.chat-corner .cc-lines .lc-empty { opacity: .6; font-style: italic; }
/* **One row.** Mike, 15 Aug. Six phrases wrapped onto three lines in a column
   this narrow and ate the space the conversation itself goes in. They scroll
   sideways instead, so the panel keeps its shape however many phrases there
   are — and there will be more. */
.chat-corner .cc-quick {
  display: flex; flex-wrap: nowrap; gap: 4px;
  overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none;
}
.chat-corner .cc-quick::-webkit-scrollbar { display: none; }
.chat-corner .cc-quick button { flex: none; white-space: nowrap; }
.chat-corner .cc-quick button {
  background: rgba(232,196,106,.1); border: 1px solid rgba(232,196,106,.3);
  border-radius: 999px; color: var(--on-dark, var(--cream));
  font-family: inherit; font-size: 11px; padding: 3px 8px; cursor: pointer;
}
.chat-corner .cc-quick button:hover { background: rgba(232,196,106,.22); }
.chat-corner .cc-say { display: flex; border-top: 1px solid rgba(224,170,62,.2); }
.chat-corner .cc-say input {
  flex: 1; min-width: 0; background: transparent; border: 0; color: var(--cream);
  font-family: inherit; font-size: 13px; padding: 7px 4px;
}
.chat-corner .cc-say input:focus { outline: 1px solid rgba(224,170,62,.5); outline-offset: -1px; }
.chat-corner .cc-say button {
  background: transparent; border: 0; color: var(--on-dark, var(--gold));
  font-family: inherit; font-size: 13px; padding: 0 8px; cursor: pointer;
}
.chat-corner { --on-dark: #e8c46a; }

/* ---------- the panel on the felt, a laptop's table talk ---------- §1AF.52

   The corner, grown to the right third of the felt — placed by `render42`
   from the felt's and the east seat's measurements, the way the played-hand
   record is placed on the left. Drawn as a panel lying on the table, like
   the record; bigger type than the corner because there is room for it. */
.talk-panel { position: absolute; z-index: 6; display: none; }
.talk-panel.live {
  display: flex;
  background: rgba(12, 27, 20, .94);
  border: 1px solid rgba(224, 170, 62, .22); border-radius: 14px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .45);
  padding: 12px 14px; gap: 8px; opacity: 1;
}
.talk-panel.live b { font-size: 13px; letter-spacing: .1em; }
/* The lines gather at the foot, by the box you type in, the way a chat
   reads; the first line's `margin-top: auto` pushes a short conversation
   down without breaking the scroll of a long one. */
.talk-panel .cc-lines { font-size: 14px; line-height: 1.45; display: flex; flex-direction: column; }
.talk-panel .cc-lines > :first-child { margin-top: auto; }
.talk-panel .cc-lines p { margin: 0 0 6px; }
.talk-panel .cc-quick { flex-wrap: wrap; overflow: visible; gap: 6px; }
.talk-panel .cc-quick button { font-size: 12.5px; padding: 5px 11px; }
.talk-panel .cc-say input { font-size: 14px; padding: 9px 6px; }
.talk-panel .cc-say button { font-size: 14px; }

/* The button is the fallback for screens with no corner to put this in — the
   dock drops its side columns below 1240px. Above that the corner is there and
   a second way in is clutter. */
@media (min-width: 1241px) { .talk-btn { display: none !important; } }

/* ---------- the tables you keep going back to ----------
   Above the game cards, because for somebody who plays with the same four
   people this is the button they came for. Quieter than a game card on
   purpose: it is a way back, not an invitation to start something. */
/* Three rows, then a scroll. Ten tables can be saved and the list used to draw
   all of them, pushing the four game cards off the bottom of a phone — which is
   backwards, because somebody with no saved tables should see exactly the home
   screen they always saw, and somebody with eight should still see the games.

   The height comes from the rows rather than a magic number, so Big print gets
   taller rows and still shows three of them. */
.saved-tables { margin: 0 auto 14px; max-width: 560px; width: 100%; }
.saved-list {
  max-height: calc(3 * var(--saved-row-h, 86px));
  overflow-y: auto; overscroll-behavior: contain;
  /* A hint that there is more below, rather than a hard edge that looks like
     the end of the list. */
  mask-image: linear-gradient(to bottom, #000 calc(100% - 14px), transparent);
}
html.big-print .saved-list { --saved-row-h: 104px; }
.saved-h {
  font-size: 12.5px; letter-spacing: .12em; text-transform: uppercase;
  opacity: .7; margin: 0 0 7px 2px; font-weight: bold;
}
.saved-table {
  display: block; width: 100%; text-align: left; margin-bottom: 7px;
  background: rgba(0,0,0,.26); border: 1px solid rgba(224,170,62,.34);
  border-radius: 11px; padding: 10px 14px; color: var(--table-text, var(--cream));
  font-family: inherit; cursor: pointer;
}
.saved-table:hover { border-color: var(--gold); background: rgba(224,170,62,.14); }
/* Two lines a row. The name and the game share the first — they are what
   somebody is looking for, and "the Fives one with the Rad Trads" is how a row
   actually gets found. The code and the last time you sat share the second, in
   small grey type. It was three lines; the list shows three rows at a time and
   scrolls, so the line saved is most of another table visible. */
.st-name {
  display: flex; align-items: baseline; gap: 8px; min-width: 0;
  font-size: 16px; font-weight: bold;
}
.st-name .st-game {
  flex: none; font-size: 12.5px; font-weight: normal; opacity: .7;
  text-transform: uppercase; letter-spacing: .06em;
}
.st-sub {
  display: flex; align-items: baseline; gap: 8px;
  font-size: 12.5px; opacity: .72; margin-top: 2px; min-width: 0;
}
/* The code, quiet. The name is what somebody recognises and it keeps the big
   type; this is only here to tell two tables apart when the name cannot —
   which happens the moment the same crowd plays two games, because the name
   comes from whoever opened the table. */
.st-code {
  flex: none; font-size: 11.5px; letter-spacing: .04em;
  opacity: .72; font-family: 'Courier New', monospace;
}
/* The row and its one destructive control, side by side. The × is set apart and
   kept small on purpose: the row is what people mean to press, and a delete
   that is easy to hit by accident costs somebody the table they were about to
   sit down at. It still has to clear the 24px minimum for a touch target. */
.saved-row { display: flex; align-items: stretch; gap: 6px; }
.saved-row .saved-table { flex: 1; min-width: 0; }
/* A long table name truncates rather than shoving the game label out of the
   row — "Thursday Night at the Hall" in Taroky is the case that decides this. */
.st-name > :first-child, .saved-table .st-name {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.saved-forget {
  flex: 0 0 auto; align-self: center; width: 32px; height: 32px; margin-bottom: 7px;
  background: none; border: 1px solid rgba(245,239,224,.22); border-radius: 9px;
  color: var(--cream); opacity: .55; font-size: 17px; line-height: 1; cursor: pointer;
}
.saved-forget:hover { opacity: 1; border-color: var(--red); color: #ff9d92; }

/* ---------- the hand you just played, on a phone ----------
   The laptop panel is laid over the open half of the felt. A phone has no open
   half, so it was hidden below 900px and the feature simply did not exist on
   the device most people play on. This is the phone answer: the same record,
   full screen, opened deliberately and dismissed the same way.

   The button is hidden where the panel already works, so nobody is offered two
   ways to look at one thing. */
.hr-open { display: none; }
@media (max-width: 899px) { .hr-open { display: block; }}

.hr-sheet {
  position: fixed; inset: 0; z-index: 90;
  /* Opaque, not nearly-opaque. At 96% the card underneath showed through and
     "Bid made!" sat across the middle of trick two, which reads as a rendering
     fault rather than as depth. There is nothing behind this worth seeing. */
  background: #0a1710;
  display: flex; flex-direction: column;
  padding: calc(8px + env(safe-area-inset-top)) 8px calc(8px + env(safe-area-inset-bottom));
  color: var(--cream);
}
.hr-sheet-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 2px 4px 8px; flex: none;
  font-size: 12.5px; letter-spacing: .1em; text-transform: uppercase;
  color: var(--heading, #e0aa3e);
}
/* Scrolls, because seven tricks in a column is taller than a phone and a hand
   you can only see four tricks of is not a record of the hand. */
.hr-sheet-body {
  flex: 1; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
}
.hr-sheet-body .tr-rows { overflow: visible; }
.hr-sheet-body .trick-record,
.hr-sheet-body .tr-head { background: none; border: none; padding: 0; }

/* ---------- desktop: bigger type, and seats that lie down ----------

   Reported 17 Aug: *"all text on screen is pretty small for people on
   desktop"*, and the Fives seats waste height by stacking a name above a fan
   when there is width going spare either side of them.

   The app is drawn for a phone and the desktop has been inheriting those
   sizes. That is the right default — the phone is where this is played — but a
   1600px screen reading at phone sizes is a screen being used at a quarter of
   its worth. Nothing here changes a layout; it changes what the type is set
   at, and lays the Fives seats along their row so the height they were
   spending on two lines goes back to the felt.

   Kept to one block with one guard so §4.6 does not grow another threshold —
   and everything in it raises a size rather than introducing a rule, so
   nothing here can be the rule that never applies. */
@media (min-width: 900px) {
  /* The seat's own type lives with the seat — see the desktop-row format in
     the component block. What is left here is everything that is not a seat. */
  .status-msg { font-size: 21px; }
  .tr-head { font-size: 13px; }
  .tr-pts { font-size: 14px; }
  .ti-item { font-size: 17px; }
  .ti-item b { font-size: 19px; }
  .ti-lbl { font-size: 12px; }
  .board-empty-hint { font-size: 17px; }

  /* The three Fives seats lay along their row here instead of stacking, in
     four rules that reached into the head, the fan and the count. That shape
     is the unit's desktop row now and every table gets it, which is what
     Fives having it alone was always short of. */
}


/* ---------- the option screens use the desktop they are on ----------

   — *"When I'm on the desktop I want to be able to use the space."*

   Measured first, because §3.21 says to. At 900x700 the 42 house rules were
   1,506px tall in a 700px window — 806px of scrolling — and the profile was
   2,918px, over three screenfuls. The panel itself was 620px wide inside a
   900px window, so 280px of the desktop was doing nothing while the content
   below it ran off the bottom.

   Two columns, and the width to hold them. Halving the number of switch rows
   roughly halves the height, and it cannot make a phone worse because the
   phone never sees this rule.

   **The descriptions stay.** §3.21 argued this out and the answer was no:
   *"4 doubles · partner names trump · all 7 tricks"* under Plunge is the only
   place a beginner is told what Plunge is while deciding whether to switch it
   on, and hiding it behind a hover puts it out of reach on the device this is
   actually played on. So they are kept and set smaller and lighter, which is
   what there is room for on a desktop and what was asked for.

   **Only the switch rows pair up.** Headings, the paragraphs under them, the
   back picker and every button keep the full width, and so does any row
   carrying a note underneath it — a two-line explanation in a half-width
   column is taller than the row it explains.

   `min-width`, and 900, because the file's own rule at the top says a rule
   written for a wide screen must carry one, and 899/900 is the documented
   complementary pair. The sticky Back uses the 899 half.

   **Scoped to `.setup-panel` on 19 Aug, which is the house-rules screen and
   nothing else.** It applied to every panel drawn into `#screen-rules`, which
   is also the profile, the Taroky intro and the lobby — and on the profile
   it was both unwanted and wrong. `flex: 0 0 auto` below stops a control
   shrinking, which is right for a label and a segment; the profile's Avatar
   row is eight face buttons in a `.seg` that then could not shrink, so it
   overflowed the half-width column and the last face was cut off at the
   panel's edge. Shipped in v1.66.0 and reported the same day. The profile is
   one column again and the height comes off it another way — BACKLOG 3.33. */
@media (min-width: 900px) {
  #screen-rules .setup-panel {
    max-width: 880px;
    display: grid; grid-template-columns: 1fr 1fr;
    column-gap: 28px; align-content: start;
  }
  #screen-rules .setup-panel > *:not(.rule-row) { grid-column: 1 / -1; }
  #screen-rules .setup-panel > .rule-row.wrap { grid-column: 1 / -1; }
  #screen-rules .rule-name small { font-size: 12px; opacity: .55; margin-top: 1px; }
  /* And the switches stay on the line with the thing they switch.

     Two columns made every row narrower, and `.rule-row` wraps — so the
     control dropped underneath its own label and each row grew a line. Nine
     rows at 64px and 108px instead of 59px each: the pairing was saving height
     and the wrapping was giving most of it straight back, which is why this is
     measured rather than assumed. `min-width: 0` is the half that matters —
     without it a flex item refuses to shrink below its content and wraps
     anyway. */
  #screen-rules .setup-panel > .rule-row { flex-wrap: nowrap; align-items: center; }
  #screen-rules .setup-panel > .rule-row.wrap { flex-wrap: wrap; }
  #screen-rules .setup-panel > .rule-row > .rule-name { flex: 1 1 auto; min-width: 0; }
  #screen-rules .setup-panel > .rule-row > .seg { flex: 0 0 auto; }
  /* The secondary buttons pair up too. §3.21: if it still scrolls, the next
     thing to give up is the spacing rather than the words — and four
     full-width buttons stacked down an 864px panel is spacing. The primary
     one keeps the whole width, because it is the one being aimed at. */
  #screen-rules .setup-panel > .ghost-btn { grid-column: auto; }
}

/* The pledge line in the Support panel. Bigger and bold because it is the most
   important sentence in there — asked for on 19 Aug. The link wraps the words
   that say where it goes rather than the word "here": link text is the one
   piece of copy that has to make sense pulled out of its sentence. */
.support-pledge {
  display: block; font-size: 1.12em; font-weight: bold; line-height: 1.5;
  color: var(--cream);
}

/* The go-to rules control, on the setup screen. A link rather than a row: it is
   a thing you do occasionally to a screen you are already leaving, not one of
   the three ways off it. §3.22. */
.save-goto {
  display: block; margin: 2px auto 10px; padding: 6px 10px;
  background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 13.5px; color: var(--pill-hi);
  text-decoration: underline; text-underline-offset: 3px; opacity: .85;
}
.save-goto:hover, .save-goto:focus-visible { opacity: 1; }

/* The setup screen's secondary buttons, brought down in weight. `.ghost-btn`
   is 16px type in 14px of padding against `.big-btn`'s 19 in 17 — near enough
   the same button in two colours, which is why a column of them read as three
   equal choices. 1G.5 settled that a screen has one primary; this is that rule
   applied to the one screen everybody passes through before every game.
   Scoped here rather than changed globally: the ghost button is used in
   thirty-nine places and most of them are not in a stack. BACKLOG 1L.6. */
#screen-rules .ghost-btn { padding: 11px; font-size: 15px; margin-top: 8px; }

/* ---------- what the table said, and where it says it ----------

   A second lane, below the game's own. Chat previews used to share `.toast`
   with the running commentary, so "Buck: two minutes" and "Buck bids 30"
   landed on the same pixels and replaced each other — and the one you needed
   was whichever arrived second. §1.3.

   Below rather than above: the commentary is about the hand being played and
   wins the better spot. Stacked rather than replaced, because three people
   talking at once is normal at a table. Quieter than a toast, because this is
   passive — it reads and goes without asking for anything, which is what keeps
   chat from taking the screen off somebody mid-bid. */
.said-lane {
  position: fixed; top: 112px; left: 50%; transform: translateX(-50%);
  z-index: 59;                      /* under the game's toasts, over the felt */
  display: flex; flex-direction: column; gap: 5px; align-items: center;
  pointer-events: none; max-width: 92vw;
}
.said {
  background: rgba(0,0,0,.74); border: 1px solid rgba(224,170,62,.34);
  color: var(--cream); border-radius: 18px; padding: 6px 14px;
  font-size: 13.5px; line-height: 1.35; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  animation: saidIn .18s ease;
}
.said b { color: var(--pill-hi); font-weight: bold; }
@keyframes saidIn { from { opacity: 0; transform: translateY(-5px); }}

/* ---------- a panel that comes up from the bottom ----------

   Table talk on a phone. The thumb is at the bottom and the keyboard is about
   to be, so the sheet meets both and leaves the table visible above it.
   Phone only: on a desktop the panel is fine where it is and a full-width
   sheet would be worse. §1.3. */
/* The middle of a phone's table has to hold a sentence.

   `fit-430-std.png`, v1.77.0: "Your play.", "Everyone passed — reshuffle",
   "Becky bids 30" and "Becky names Blanks" piled into a 54px column between
   the two side bones and overprinted the east one (§1AA.6). The centre cell
   is `auto` and the side cells `1fr`, so with a bone in each side cell the
   middle got whatever was left — 54px at 430. Give it a floor: 120px leaves
   two 72px bones their room at 360 (360 − 144 − gaps = 204) and wraps the
   sentence at three or four words rather than one. The stack inside is capped
   to the cell (`.tp-mid .status-stack` above), so it cannot spill sideways. */
@media (max-width: 699px) {
  #screen-42 .tp-mid { min-width: 120px; }
  #screen-42 .trick-slot { min-width: 0; }
}

@media (max-width: 620px) {
  .overlay-sheet { align-items: flex-end; padding: 0; }
  .overlay-sheet .panel {
    width: 100%; max-width: none; max-height: 76vh;
    border-radius: 16px 16px 0 0; border-bottom: 0;
    padding-bottom: calc(14px + env(safe-area-inset-bottom));
    animation: sheetUp .2s ease;
  }
  @keyframes sheetUp { from { transform: translateY(14px); opacity: .6; } }
}

/* ---------- the desktop table, second pass ---------- §3.19a

   §8B.4 raised the type and §3.19 widened the record; both helped and neither
   went far enough. Two things, measured at 1280x800 before and after.

   **"trick 4 of 7" was the smallest text on the felt** — 14.5px against a 16px
   body and 19px seat names — and it is the one line answering "how much of this
   hand is left". It had already been raised once, from 12px at 60%, and was
   still bottom of the pile. It is a peer of the seat names now, because that is
   what it is: a piece of table information, not a footnote.

   **The other players' bones were 68px against your 112.** Mike asked for them
   the same size as your own. They cannot be, and the reason is worth writing
   down rather than rediscovering: the east and west seats are **height-bound,
   not width-bound**. Seven bones stack down a 294px column, so the bone can be
   at most about 80px wide before the seventh runs off the bottom — nothing to
   do with the 118px the seat is wide. 112px there would need six of the seven
   bones or a second column, and both are worse than a slightly smaller bone.

   So 80px, which is the ceiling rather than a guess, and the same 80 across all
   three: the north seat has room for more, and three opponents drawn at two
   different sizes reads as a mistake. A quarter larger than they were.

   `min-width`, per the rule at the top of this file. */
@media (min-width: 900px) {
  /* 26, not 19. The other half of the stack in the middle of the table, and
     it takes the same size as the sentence sitting on top of it — *"'trick 5
     of 7' is TOO SMALL."* They are one thing to read.

     This is the FOURTH copy of this size in the file (12 base, 14.5, a phone
     clamp, and this) and the only one that governs a desktop. A 26px rule was
     briefly added higher up and this one silently beat it on source order,
     which is the tie this file has now lost four separate times. It is set
     here, where it wins, and the others are the phone's. */
  .tp-c, .mn-empty, .tk-empty { font-size: 26px; }
  /* The centre of the table is a reserved hole, not whatever is left over.

     Two things live in it and never at the same time: "trick N of 7" before a
     bone is down, and the table's standing message once one is. The four played
     bones ring it. Sized so the message fits between the west and east bones
     and under the north one — without this the ring closes up around an empty
     cell and the sentence lands on a domino, measured at 1913x908.

     `clamp` because the trick area is 1554px wide between the side seats at
     1900 and 528 at 900; a flat 380 fits the first and overflows the second. */
  #screen-42 .tp-mid { min-width: clamp(180px, 24vw, 400px); min-height: 66px; }
  /* Seven tricks without scrolling, and the height comes out of the spacing
     rather than the bones.

     Measured at 1600x900: a row is 62px, the gaps 5px, the heading and key 48,
     so seven tricks want 512px in a panel that has 477 — it scrolled by 35.
     The obvious lever is the bone, and it is the wrong one: §3.19 raised these
     deliberately and the whole complaint around them is that the desktop draws
     things too small. Two pixels a row off the gap and one off the padding is
     28px, and eight more come from the panel's own margin below. That covers
     the 35 with nothing shrinking that anybody looks at. */
  .tr-rows { gap: 2px; }
  .tr-row { padding: 1px 5px; }
  /* The panel's bottom margin is 14px of nothing at the foot of the record —
     it is there to separate the panel from what follows it inside an overlay,
     and inside the hand review there is nothing following it. */
  .hand-review .tr-panel { margin: 2px 0 0; max-height: none; }
}

/* ---------- the full-screen record sheet, on a phone ---------- §1G.2, §1G.3

   **The bones.** `--rec-bone` is tuned for the corner record beside the felt,
   where seven tricks have to fit a column and height is the binding constraint
   — §3.19a has just finished making that work. The sheet has no such
   constraint and was inheriting a number chosen for a different container:
   measured at 430x932, seven tricks used 464px of 880 and left **47% of the
   screen empty**, with 28px bones. It gets its own size.

   **The way out.** Close sat top-right, measured at 372,10 on a 430 screen —
   the furthest point on the phone from a thumb, at the top of a sheet you read
   downwards. §3.29 settled this on 18 Aug for the option screens and this was
   a fourth screen with the same problem. Bottom-left, with the safe-area inset
   the same way `.back-sticky` handles it. */
@media (max-width: 899px) {
  /* The sheet's rows are `.tr-rows` with no `.trick-record` wrapper around
     them, so the corner record's `--rec-bone` rules never matched here at all
     — the bones fell through to the default `.domino.small`, 56x28. Setting
     the variable did nothing for the same reason. Sized directly. */
  .hr-sheet-body .domino.small.h { width: var(--sheet-bone); height: calc(var(--sheet-bone) / 2); }
  .hr-sheet-body .domino.small.v { width: calc(var(--sheet-bone) / 2); height: var(--sheet-bone); }
  .hr-sheet-body .domino.small .half { width: calc(var(--sheet-bone) / 2); height: calc(var(--sheet-bone) / 2); }
  .hr-sheet { --sheet-bone: clamp(56px, 21vw, 92px); }
  .hr-sheet-body { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
  .hr-sheet-bar .exit-btn {
    position: fixed; z-index: 3;
    left: 12px; right: auto; top: auto;
    bottom: calc(12px + env(safe-area-inset-bottom));
    background: rgba(20, 14, 8, .94); color: var(--cream);
    border: 1px solid var(--pill-hi); border-radius: 24px;
    padding: 12px 22px; min-height: 46px; font-size: 16px; font-weight: bold;
    box-shadow: 0 6px 18px rgba(0, 0, 0, .45); backdrop-filter: blur(4px);
  }
}

/* ---------- you, at your end of the table ---------- §1E.2, §1F.1

   The name plate was the top row of the dock, so its height came off the
   bones; the turn token was already hovering in the felt just above the dock,
   which is the space this now sits in. Together they read as one thing, and
   the hand gets the row back.

   Centred, with the token to its left — `placeTurnToken` measures this plate
   for your own seat rather than guessing at the middle of the felt. */
.me-plate {
  position: absolute; left: 50%; bottom: 4px; transform: translateX(-50%);
  z-index: 3; pointer-events: none; max-width: calc(100% - 24px);
}
/* Moon and Taroky carry the plate in flow, as the felt's last child. The
   absolute version above is 42's, where the felt is tall and its bottom is
   empty — D2 shrank these two felts to their contents, so "the bottom of the
   felt" and "the trick band" became the same place and the plate sat on the
   played bones. In flow, the felt grows by the plate's height and the two
   cannot meet. This is 1N.6's lane reduced to its essence: one element in
   normal flow, no wrapper. BACKLOG 1Q.6. */
#screen-moon .me-plate, #screen-taroky .me-plate {
  position: static; transform: none; width: max-content;
  margin: 6px auto 10px; max-width: calc(100% - 24px);
}
/* Moon's standing message sits in the centre of the seat row — its two
   opponents seat at the thirds, so the middle of that band is the one spot
   on a shrunk felt that is reliably empty. The 150px default was chosen
   against 42's tall felt and landed on Moon's played bones. BACKLOG 1Q.7. */
/* `--stack-top` and Moon's override of it stood here. Both are gone: the
   stack is anchored to the middle of the felt now and has no top offset to
   set. Moon's felt is short and its middle is the same middle. */
/* Desktop only: on a phone the seat band runs edge to edge with no empty
   middle, and 16px put the sentence across the faces. The 150px default
   lands below the pills there, which is the room a phone actually has. */
/* The seats hug their names, which is what makes that spot empty. An is-turn
   seat used to stretch its pill across the whole grid column — edge of the
   screen to the middle of the table, the banner Mike circled — and it was
   fixed on Taroky, then on Moon, separately. The unit hugs by construction:
   its grid columns are the notch, the identity and the pile, and none of them
   is a `1fr`. BACKLOG 1Q.7. */
#screen-moon .mn-seat { width: max-content; max-width: 100%; margin: 0 auto; }
#screen-taroky .tk-seat { max-width: 100%; margin: 0 auto; }
/* Three lying down across the top on a desktop — see `seatTaroky`. Centred
   in their columns, and the card may not be wider than the column. */
@media (min-width: 900px) {
  #screen-taroky .tk-seat .tok.tok-h { max-width: 100%; margin: 0 auto; }
}
/* Nothing on your own plate is clickable — the rename pencil never appears on
   your own seat — so the whole thing stays click-through. It carried
   `pointer-events: auto` on the name until 21 Aug, which cost nothing on 42
   where the plate floats over empty felt, and broke Fives and Moon the moment
   they grew plates of their own (1J.1): there the same spot sits over the
   board, and three suites failed with the plate swallowing clicks meant for
   what was underneath it. */
.me-plate, .me-plate .name { pointer-events: none; }

/* ---------- the reserved lane at the foot of the felt ---------- §1N.6

   Fives only, for now. The plate stops being an absolute box floating over
   whatever is underneath and becomes an item in a row that owns its own
   height — so the board cannot grow into it, the Knock button cannot land on
   it, and neither has to know about the other.

   `flex: none` is the whole mechanism: the board's wrap is `flex: 1` and takes
   what is left, so the lane's height is subtracted from the board's before the
   board scales itself. That is also why this should settle 1N.7 — the page
   grew a scrollbar because the board was measured against a wrap that did not
   know about a plate sitting on top of it. */
.felt-lane {
  flex: none; display: flex; align-items: center; justify-content: center;
  gap: 12px; flex-wrap: wrap; padding: 4px 8px 6px; min-height: 48px;
}
/* In the lane it is laid out rather than positioned. The pointer-events rule
   above still applies and still matters: nothing on your own plate is
   clickable, and the Knock button is right beside it now. */
#screen-fives .me-plate {
  position: static; transform: none; bottom: auto; left: auto; max-width: none;
}
/* The button sits in the row rather than filling it. `.pass-btn` is a full
   width block elsewhere. */
.felt-lane .pass-btn { width: auto; margin: 0; }
/* Fives on a phone: you at the left of the lane, the table's sentence at the
   right, and the partner directly under the header.

   Mike, 2 Sep, off a 430 shot: *"my icon/name to move to bottom left, not be
   center justified. then in bottom right put the current status info 'your
   play - tap a bone'. that way we can then move Partner icon/name/domino up
   to right below the header. Keep Ed/Becky exactly as they are."* Fives has
   no stack in the middle of its felt — its sentence was the line above the
   felt, which is what held the partner's seat down. So on a phone that line
   is visually hidden (it stays the live region, exactly as 42 and Moon do
   it at `#screen-42 .status-line .status-msg`) and its words are shown in
   the lane, by `renderFives`. The lane is `flex: none`, so the board still
   scales into what is left. The desktop is untouched: its plate lives in the
   dock and its sentence at the top. Fives only, by his call. §1AC.6. */
.lane-status { display: none; }
@media (max-width: 899px) {
  #screen-fives .status-line .status-msg {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  /* `height: auto` as well as the floor: the line carries a flat `height:
     70px` from the desktop block (`.status-line { height: 70px }`), and a
     rule that only zeroes `min-height` leaves the box exactly as tall as it
     was — measured at 70 with the sentence hidden, the partner 70px under the
     header, and nothing on the felt to show for it. */
  #screen-fives .status-line { min-height: 0; height: auto; padding: 0; }
  #screen-fives .felt-lane { justify-content: space-between; flex-wrap: nowrap; gap: 8px; padding: 4px 10px 6px; }
  #screen-fives .felt-lane .me-plate { margin: 0; flex: none; }
  .lane-status {
    display: block; flex: 1 1 auto; min-width: 0; text-align: right;
    font-size: 15px; font-weight: bold; line-height: 1.25; color: var(--status-text);
    text-shadow: 0 2px 6px rgba(0,0,0,.55);
  }
}
/* Taroky's lane, at every width (§1AC.20 the phone, §1AC.26 the desktop —
   *"move my icon and name slightly to the left, and the status to the same
   row, to the right"*). The status line under the header is visually hidden
   everywhere on this table — it keeps being the live region — and its words
   are drawn at the lane's right end by `renderTaroky`. On the desktop the
   plate is a little left of centre rather than in the corner: a felt 1250
   wide with one card hard against its edge reads as a stray, so the lane
   takes a fifth of the width as padding each side there. This also hands the
   desktop's short-window ladder (§1AB.2) its 44px status line back. */
#screen-taroky .status-line .status-msg {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
#screen-taroky .status-line { min-height: 0; height: auto; padding: 0; }
#screen-taroky .felt-lane { justify-content: space-between; flex-wrap: nowrap; gap: 8px; padding: 4px 10px 6px; }
#screen-taroky .felt-lane .me-plate { margin: 0; flex: none; }

/* ---- the player's own name must not eat the table's sentence ---- §4.21

   This lane is the plate at one end and the sentence at the other, `nowrap`.
   The plate is `flex: none` and carries the name, so it grows and never
   gives; `.lane-status` is `flex: 1 1 auto; min-width: 0` and takes what is
   left. Measured at 360 with "Bartholomew Smith": the plate went 135 -> 276
   of the lane's 340, **the sentence got 56px and wrapped to eight lines**,
   the lane doubled 80 -> 160, the trick band gave up what it could (164 ->
   104, its floor) and the last 20px scrolled the page.

   `#prof-name` allows 20 characters and seventeen already does it, so this
   is a name the app invites you to type, on the commonest phone width there
   is. It is what had been failing `pagecheck`'s Taroky check at random since
   17 Sep — an earlier test leaves a long name in the profile, so the check
   was reporting this and blaming the deal.

   **A proportion, because no constant can be right at both ends.** The cap
   was a flat 190px, chosen against a 546px desktop lane; on a 360px phone
   lane it leaves 56. `min(190px, 30vw)` is the same 190 from 634px up and
   comes down with the phone. Same fault, same shape, as 42's lane at 1366
   (see `.lane-desk` below, "found by the suite's own hostile-name profile")
   — 42's was fixed by widening the lane; these two never were.

   **Capping the plate itself was tried first and is wrong**: `max-width` on
   `.me-plate` does not shrink what is inside it, so the name kept its 190
   and was drawn straight across the sentence. Every number said the page
   fitted — overflow adds nothing to `scrollHeight` — and only a screenshot
   showed it. The cap has to reach the name, which is why this sets the
   token the name reads (`.tok.tok-h .pname { max-width: var(--sc-name-max-h) }`)
   rather than a box around it.

   **Fives as well as Taroky, and NOT Moon.** Fives' lane is declared with
   the same three properties and had it worse — the sentence measured 13px
   wide at 360 in Big print. Moon's lane is `justify-content: center` with
   the plate alone in it and no sentence beside it, so a cap there would
   shorten the name and buy nothing. Moon's own 27px overflow at 360 in Big
   print is real, is NOT this, and is filed separately: it happens with a
   four-letter name too.

   Measured across eight cells, two names: short names move nothing anywhere,
   desktop moves nothing, and the phone cells lose the collision. */
#screen-fives .felt-lane .me-plate .tok,
#screen-taroky .felt-lane .me-plate .tok {
  --sc-name-max-h: calc(min(190px, 30vw) * var(--sc-scale));
}
#screen-taroky .lane-status {
  display: block; flex: 1 1 auto; min-width: 0; text-align: right;
  font-size: 15px; font-weight: bold; line-height: 1.25; color: var(--status-text);
  text-shadow: 0 2px 6px rgba(0,0,0,.55);
}
@media (min-width: 900px) {
  #screen-taroky .felt-lane { padding: 6px 30% 10px; }   /* §1AC.28: one number, four tables */
  #screen-taroky .lane-status { font-size: 21px; }
  /* The sentence does not size the lane — Fives' and Taroky's, the same
     rule Moon's stack got the same morning (4 Sep, *"review the other
     games for similar issues"*): a sentence wrapping to three lines made
     Fives' lane 121px instead of 96 and Taroky's 121 instead of 80, and the
     plate beside it rode up 12 and 20px. A fixed two-line box, the text
     hanging from its bottom edge, and a third line goes upward over the
     felt where nothing is in flow. `pagecheck` wraps a sentence to three
     lines on both tables and holds plate, disc and lane. */
  #screen-fives .lane-status, #screen-taroky .lane-status {
    display: flex; flex-direction: column; justify-content: flex-end;
    height: calc(2 * 1.25em); flex: 1 1 auto; overflow: visible; align-self: center;
  }
}

/* The rename pencil on a bot's seat. Quiet until looked for: it is a thing you
   do once, and the seat it sits on is something you read every trick. § asked
   20 Aug. */
.seat-edit {
  background: none; border: 0; cursor: pointer;
  color: #fff; opacity: .8; font-size: .9em; line-height: 1;
  font-family: inherit; vertical-align: baseline;
  /* 24x24 is the floor `a11ytest` holds every control to, and a pencil drawn
     at its glyph size is 17x12. The target is padded out to the floor while
     the mark stays small — quiet to read, still reachable with a thumb. */
  min-width: 24px; min-height: 24px; padding: 0 0 0 5px;
  display: inline-flex; align-items: center; justify-content: center;
}
.seat-edit:hover, .seat-edit:focus-visible { opacity: 1; }

/* ---------- one size for every bone that is not in your hand ---------- §1E.4, §1F.5

   *"All of the other player's dominoes as well as the ones in the middle
   slightly bigger and all the same size."* They were three sizes: the north
   seat 42px at 360, the two side seats 54, and the played bones in the middle
   larger again. Your own hand stays the exception — it fills the bottom of the
   screen four across (§1E.1).

   **The number comes from the tightest fit, which is your partner.** Seven
   bones in a line across the top is what made his the smallest; laid four over
   three they need about half the width, and the size that then fits all three
   seats and the middle is this one. Derived from the felt rather than the
   window, for the same reason the hand is (§1E.1): the seats live in the felt
   and that is the box the sum is about. */
/* **42 only.** Fives seats all three opponents in one row across the top, so a
   size derived from "four across the partner's edge" is meaningless there and
   pushed its seats clean off a phone — 536px of seats in a 430px screen, caught
   by `fitcheck`. Fives keeps its own sizing. */
#screen-42 .felt { container-type: inline-size; }
/* The played bones in the middle keep this derivation. The seats do not: a
   size derived from "four of the partner's bones across his edge" described a
   fan that no longer exists, and the seats take the unit's one scale. */
#screen-42 .trick-area {
  --seat-bone: min(110px, calc((100cqi - 26px) / 4.6));
  /* The middle column must be allowed to be narrower than what sits in it.
     The base rule's `auto` track sizes itself to the widest thing in the
     column, and at 360 that is the status stack — "Ed bids 30 / Ed names
     fives" is 120px in a trick area whose inside is 105 — so the grid
     overflowed to the right and the north bone, centred in that column, sat
     12px right of the felt's centre. That was the last of §1T.2: "the first
     lead in middle is off center", fixed at 430 by the seat unit and still
     off at 360 for this different reason. Symmetric outer columns were
     tried first and changed nothing, which is how the middle one was found.
     `minmax(0, auto)` lets the track shrink to the space and the text wraps
     inside it; measured at 360 and 430, both print sizes, the north bone's
     centre is the felt's to the pixel. `fitcheck` holds it. */
  grid-template-columns: minmax(0, 1fr) minmax(0, auto) minmax(0, 1fr);
}
#screen-42 .trick-slot .domino.h { width: var(--seat-bone); height: calc(var(--seat-bone) / 2); }
#screen-42 .trick-slot .domino.v { width: calc(var(--seat-bone) / 2); height: var(--seat-bone); }
#screen-42 .trick-slot .domino .half { width: calc(var(--seat-bone) / 2); height: calc(var(--seat-bone) / 2); }

/* ---------- the trick stays between the seats on a phone ---------- §1AF.51

   Mike's screenshot, 11 Sep: the east bone under Big Tuna's name and back.
   The middle band is seat, trick, seat, and on a phone each seat column is
   118px, so the trick has what is left — about 150px on a 390 screen. Each
   played bone was a seat bone, 80px, and the west-centre-east row wanted
   250; the east slot's bone, pulled toward the middle and 80px wide in a
   column with room for 38, overflowed into the seat. It happened at every
   phone width and any name — the width is the cause.

   So the played bones take their size from the room the trick has, the way
   the seat pile derives its step from its bone: the trick area is a size
   container, the side bones stand upright (turned in `paintTrick42`), and
   the row of three is then two bones' width plus the gaps. No number here
   is typed against a seat. A floor keeps the pips readable on the narrowest
   phone; `pagecheck` faked a full trick at three widths in both print sizes
   and holds that no played bone touches a seat, and that the check fires. */
@media (max-width: 899px) {
  #screen-42 .trick-area { container-type: inline-size; }
  /* Not `min(var(--seat-bone), …)`: a container unit resolves where the
     value is USED, so the felt-derived seat bone, read inside the trick
     area, re-derived itself from the trick area's 155px and came out at
     28 — every bone sat on the 44px floor (measured, 11 Sep). The cap is
     typed instead, at what the felt derivation gives a wide phone. */
  #screen-42 .trick-slot { min-width: 0; --trick-bone: max(44px, min(84px, calc((100cqi - 28px) / 2))); }
  /* The middle column's 120px minimum (typed for the desktop's status lane,
     which a phone does not show) took 120 of the trick's 155 and left the
     side columns 14px each, so the upright bones overflowed anyway. With it
     gone the column is as wide as the north and south bones, and the sides
     get the rest — measured at 390: 36px each for a 31px bone. */
  #screen-42 .tp-mid { min-width: 0; }
  html.big-print #screen-42 .trick-slot { --trick-bone: max(48px, min(94px, calc((100cqi - 28px) / 2))); }
  #screen-42 .trick-slot .domino.h { width: var(--trick-bone); height: calc(var(--trick-bone) / 2); }
  #screen-42 .trick-slot .domino.v { width: calc(var(--trick-bone) / 2); height: var(--trick-bone); }
  #screen-42 .trick-slot .domino .half { width: calc(var(--trick-bone) / 2); height: calc(var(--trick-bone) / 2); }
}

/* ---------- the Talk chip, in the seventh bone's cell ---------- §1AF.52

   The phone's hand is four across, four over three, and the empty cell at
   the end of the second row is "a really good spot" (Mike, 11 Sep). Pinned
   to that cell, so it does not slide left as bones leave; a bone's size and
   shape, dashed so it is not mistaken for one; the unread count in the
   corner, red, the way the pill did it. The preview of a line arriving sits
   over the GAME and HAND chips band under the hand, never over the bones,
   and tapping it opens the drawer. */
.talk-chip { display: none; }
@media (max-width: 899px) {
  .my-hand .talk-chip {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    position: relative; box-sizing: border-box; margin: 0; padding: 0;
    background: rgba(0,0,0,.38); border: 2px dashed rgba(224,170,62,.5); border-radius: 10px;
    color: var(--cream); font-family: inherit; font-size: 15px; cursor: pointer;
  }
  /* 42: a bone's cell, a bone's shape. */
  #screen-42 .my-hand .talk-chip { grid-column: 4; grid-row: 2; justify-self: stretch; flex-direction: row; width: 100%; aspect-ratio: 2 / 1; }
  /* Taroky: a card's size, the words stacked; on a phone the eighth cell
     of the second row, which twelve cards leave empty (the hand is eight
     across under 480px, six across above it, where the chip follows the
     cards instead). §1AF.52, 12 Sep. */
  #screen-taroky .tk-hand .talk-chip { flex-direction: column; gap: 2px; width: var(--pcard-w); height: calc(var(--pcard-w) * 1.435); border-radius: 6px; font-size: 13px; }
  .my-hand .talk-chip span { font-weight: bold; letter-spacing: .02em; }
  .my-hand .talk-chip.unread { border-style: solid; border-color: var(--gold); background: rgba(224,170,62,.18); }
  .my-hand .talk-chip.unread::after {
    content: attr(data-unread);
    position: absolute; right: -6px; bottom: -6px; min-width: 20px; height: 20px;
    padding: 0 5px; border-radius: 999px;
    background: var(--red); color: #fff;
    font-size: 12px; font-weight: bold; line-height: 20px; text-align: center;
    box-shadow: 0 1px 3px rgba(0,0,0,.55);
  }
  #screen-42 .dock-info, #screen-taroky .dock-head { position: relative; }
  #screen-42 .talk-preview, #screen-taroky .talk-preview {
    position: absolute; inset: 0; z-index: 5;
    display: flex; align-items: center; justify-content: center; gap: 6px;
    background: rgba(12,10,8,.92); border: 1px solid rgba(224,170,62,.45); border-radius: 999px;
    color: var(--cream); font-family: inherit; font-size: 14px; padding: 0 14px; margin: 0;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: pointer;
    animation: saidIn .18s ease;
  }
  #screen-42 .talk-preview b, #screen-taroky .talk-preview b { color: var(--pill-hi); }
}
@media (max-width: 480px) {
  /* 38 by 55: the phone card's own numbers, typed where `.pcard` types
     them (the eight-across block above) rather than derived from
     `--pcard-w`, which that block does not set — the chip came out at
     the 46px default beside 38px cards and stood proud of the row. If the
     card's numbers move, these move with them. */
  #screen-taroky .tk-hand .talk-chip { grid-column: 8; grid-row: 2; width: 38px; height: 55px; font-size: 11px; }
}
/* The Taroky screen positions its talk panel the way the 42 screen does. */
#screen-taroky { position: relative; }

/* The partner's four-above-three fan stood here — a width of four bones, a
   height of "four fifths of a bone plus twelve", and a comment saying the two
   numbers were derived from each other. They were, once. Then v1.72.0 gave the
   fan a 3px gap for daylight and did not give the width the 9px that gap costs,
   so four bones stopped fitting a row: the fan wrapped 3-3-1 instead of 4-3,
   the third row landed on his face, and the reserved height still said two
   rows. That is §1R.1, and it is the exact failure mode the unit exists to
   make impossible — the pile states its step once and derives everything else
   from it, so there is no second number to fall out of step with the first.

   What is lost is the shape Mike sketched on 20 Aug (§1E.4), and it is worth
   naming rather than slipping past: his partner holds a pile now, like every
   other chair at every other table. That is the trade §1R.8 asks for. */
/* Air between the table's contents and its hard edges: the side piles sat
   flush against the screen and the partner's fan against the header bar,
   and both read as clipped even when they were not. Pic 1, 23 Aug. */
#screen-42 .seats-mid { padding-left: 18px; padding-right: 18px; }
#screen-42 .seat.seat-n { margin-top: 10px; }

/* Kept at the END of the file on purpose: `#screen-taroky .me-plate` is set
   unguarded at its own home above, and a media-guarded rule earlier in the
   file loses to it on source order — the tie `csslint` exists to catch. */
/* ---------- Taroky on a short desktop: the screen owns the viewport ---------- §1AB.2

   Measured 3 Sep at 1893x908, Mike's own window, at 100%: top bar 96 + status
   line 70 + felt 429 (seats 175, trick band 158, plate 80) + dock 438 (score,
   two rows of 72px cards, a 118px action reserve) = 1,033 — so the hint line
   and the bid row were below the window, and at 1366x768, the laptop a Taroky
   player is likeliest to open this on, the hand itself was. §1S.1's rule is
   that the screen owns the viewport and one thing is the designated shrinker;
   Taroky had no shrinker, because every height here was reserved for the
   tallest state on a tall screen.

   Two tiers by height, and the deck is what gives. Cards at two fixed sizes
   read as two decks (see `--pcard-w` above), which is why these are steps and
   not a clamp: 62px under 1000 tall, 52px under 830. The reserves come down
   with them — the trick band to what a card at that size needs, the action
   row to its buttons, the status line to one line — and nothing is unreserved,
   so the table still does not step. Checked by `pagecheck`: every bid control
   inside the viewport at 1893x908 and 1366x768, both print sizes. */
@media (min-width: 900px) and (max-height: 1000px) {
  :root { --pcard-w: 62px; }
  /* Your own plate has no hand slot on it any more (§1AA.2), so it need not
     reserve the height of one. */
  #screen-taroky .me-plate .tok.tok-h { height: auto; }
  #screen-taroky .status-line { height: 44px; min-height: 44px; }
  #screen-taroky .tk-trick { min-height: 138px; padding: 8px 12px; }
  #screen-taroky .tk-seats { padding: 6px; }
  #screen-taroky .me-plate { margin: 0 auto 4px; }
  #screen-taroky #tk-actions { min-height: 96px; }
  #screen-taroky .my-dock { min-height: 0; }
}
@media (min-width: 900px) and (max-height: 830px) {
  :root { --pcard-w: 52px; }
  #screen-taroky .tk-trick { min-height: 116px; padding: 6px 12px; }
  #screen-taroky #tk-actions { min-height: 84px; }
  #screen-taroky .tk-hand { gap: 4px; }
  #screen-taroky .status-line { height: 36px; min-height: 36px; }
}
/* A 720-tall window, or 768 with Big print on: the last step the deck can
   take and still carry a rank at 15px. Below this the page scrolls, and the
   bid row is the thing that goes under — `pagecheck` measures 1366x768 in
   both print sizes as the floor this must hold. */
@media (min-width: 900px) and (max-height: 760px) {
  :root { --pcard-w: 46px; }
  #screen-taroky .tk-trick { min-height: 104px; }
  #screen-taroky .tk-seats { padding: 2px 6px; }
}
html.big-print #screen-taroky .my-dock { padding-top: 6px; padding-bottom: 6px; }

/* ---------- 42 and Fives on a desktop: you at the left, the sentence at the right ---------- §1AC.21, §1AC.24

   Mike, 2 Sep, off two 1254-wide screenshots, the same shape twice: *"take my
   icon and my name off the dock and put it onto the table … put the status on
   the right side … that way the dock only has the score and then our
   dominoes."* The phone already does this (§1AC.6, §1AC.20); this is the
   desktop tier of the same idea, and it lives at the end of the file for the
   reason the Taroky short-window block does — everything it overrides is
   above it, so a media-guarded rule here cannot lose to an unguarded one.

   **42.** The side seats stand in columns now (`.side-col`, `template.html`),
   so the plate sits under the west seat and a second stack under the east
   one, both IN FLOW — the CLAUDE.md rule against positioning a box against a
   seat by a typed offset, kept. `statusStack()` picks the east stack on a
   desktop and the middle one on a phone; the plate has three homes and the
   stylesheet shows one. The dock's head row keeps its height with the plate
   gone from it (§1V.9): the bid chip alone is `bidding…` and then nothing.

   **Fives.** The lane the phone uses — plate at the left, the words at the
   right, the status line under the header hidden — at every width; the dock
   plate goes. The lane sits under the board by construction (`flex: none`
   takes its strip off the board's space, §1N.6), which is what *"so it will
   hopefully avoid the dominoes"* asks for. The seats hug the header; the pad
   is as tall as two rows of bones. */
.side-col { display: flex; flex-direction: column; align-items: center; min-width: 0;
             container-type: inline-size; container-name: seatcol; }
/* ---- and a width of its own between 701 and 899 (§4.20) ----

   Everywhere else this column is told how wide it is: a flex share under
   700 (32%, 28% under 431) and a flat 168px from 900. In the band between,
   nothing said — so its basis was `auto`, which is the seat inside it, and
   it could shrink. Measured 19 Sep: at 760, 820, 860 and 899 the column
   came out **76px** and the seat card 60, narrower than the 91px it gets on
   a 360px phone. The window was getting wider and the seat was getting
   smaller, and the stylesheet already said a seat in this band is 118
   (`.seat-w, .seat-e` under 899) — the column simply never carried it.

   It carries it now, and does not shrink. Two things follow: the band
   draws a seat the size the file always said it was, and the column has a
   width that does not depend on its contents — which is what a container
   query needs (§4.20; the seats collapsed to 16px when this was contained
   without it). The trick area gives up 84px of its middle here and keeps
   more than 450 at 760, which is twice what three bones need. */
@media (min-width: 701px) and (max-width: 899px) {
  .seats-mid > .side-col { flex: 0 0 118px; }
}
.side-col > .seat { width: 100%; max-width: 100%; }
.side-slot, .status-stack.stack-e, .status-stack.stack-lane, #screen-42 .felt-lane.lane-desk { display: none; }
/* The desktop's copy of GAME and HAND is nothing on a phone. Left visible
   there it drew a second score band inside the head row, the dock grew by
   it, and at 360x780 with Big print the bid panel's slot shrank until 42
   and Pass were below it — `pagecheck` caught it on the first cut. */
.dock-info-desk { display: none; }
/* Moon's lane on a phone is only the plate's box, in the felt's column
   where the plate already stood (`order: 3`, above). */
#screen-moon .felt-lane { display: flex; justify-content: center; order: 3; }
@media (max-width: 700px) {
  .seats-mid > .side-col { flex: 0 0 32%; min-width: 0; }
  /* 28% under 431px, not 32 (§1AE.65, 7 Sep): at 390 — the iPhone's width,
     never measured until Mike's screenshot — 32% is 125px a side and leaves
     the trick 140px of middle for three slots that need about 196, so the
     side bones spilled onto the seats' faces. The seat card is about 100px;
     28% is 109. The trick's own gap comes down with it (below). Held by
     `fitcheck` at 390 and 430, both prints: no played bone on a seat card. */
  @media (max-width: 430px) {
    .seats-mid > .side-col { flex: 0 0 28%; }
    /* And the trick sits at the FOOT of its area rather than its middle.
       Measured at 390: the area is 156px wide between two 109px columns,
       its side slots are 79px in 14px tracks and overhang into the seats
       whatever the columns do — there is no width for three bones beside
       two seat cards on a 390px glass. But the seat cards hang from the
       top of their columns (137px tall from y220 at 390x780) and the trick
       was centred at y355, level with them. At the foot of the area the
       side bones are below the cards at every height a phone has, and the
       lane is right under them, which is where the eye is anyway. */
    #screen-42 .trick-area {
      gap: 2px; padding: 4px 2px 6px;
      grid-template-rows: auto auto auto; align-content: end;
    }
    #screen-42 .trick-slot { min-height: 30px; }
  }
  /* A short phone — an iPhone with its bars showing is 390x664, and Big
     print on a 780 is the same squeeze — has a felt band about 160px tall
     between the header's seat and the lane, and a seat card is 137 of them
     (152 in Big print). The card and the trick cannot both fit that band,
     so the card's decorative back (the T★B tile under the name) goes on
     short phones only; the face and the name stay, and the bones sit below
     them. Measured, not guessed: with the back the east bone lay across the
     name at 390x664 (bone y287..324, name y293..320). */
  /* The whole hand slot, not the back alone: the card reserves the slot's
     height whether or not there is anything in it (rule 1 of the seat unit),
     so hiding the back left a 40px empty row and moved nothing. 42 shows no
     count there since §1AE.39, so the slot holds only the back. */
  @media (max-width: 430px) and (max-height: 700px) {
    #screen-42 .tok .hand { display: none; }
    /* The partner's lying-down card has a FIXED height (`calc(var(--sc-stack)
       + 4px)`, the seat unit's rule for the pair), so hiding its hand slot
       moved nothing above the band; measured, the band started at y216
       either way. Let it be as tall as its face and name here. */
    #screen-42 .seat-n .tok.tok-h { height: auto; min-height: 0; }
  }
  @media (max-width: 430px) and (max-height: 820px) {
    html.big-print #screen-42 .tok .hand { display: none; }
    html.big-print #screen-42 .seat-n .tok.tok-h { height: auto; min-height: 0; }
  }
  /* The lane over the felt's foot rather than in flow. §1AE.54 put the
     plate and the sentence in a lane at the felt's bottom, and in flow it
     cost the band above it 60px — on a 664px phone the band between the
     partner and the lane was 162px for a 103px seat card and a 128px
     trick. The lane floats over the foot instead (the felt is positioned;
     `.me-plate` did exactly this before §1AE.54) and the trick keeps its
     south slot clear of the plate and the sentence by sitting between them:
     the plate is at the left edge, the sentence at the right, the south
     bone is centred. */
  @media (max-width: 430px) {
    #screen-42 .felt-lane.lane-desk { position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; pointer-events: none; }
    #screen-42 .trick-area { padding-bottom: 6px; }
  }
}
@media (min-width: 900px) {
  .seats-mid > .side-col { width: 168px; flex: none; }
  /* The side slots and the east stack were v1.80.0's homes for the plate
     and the sentence; Mike's marks the next morning put both in a lane a
     third in from each side (§1AC.28), so the slots stay empty here and the
     lane below takes over. */
  .side-col .side-slot, .status-stack.stack-e { display: none; }
  #screen-42 .tp-mid .status-stack:not(.stack-e):not(.stack-lane) { display: none; }
  #screen-42 .dock-plate { display: none; }
  /* One row above the hand — the bid chip, GAME and HAND — at one height
     and one type size, centred on the hand's column (§1AC.30). The phone's
     score band under the hand is hidden here; its desktop copy
     (`#t42-info-desk`, spans only) joins the head row as `display: contents`
     so its two chips are the row's own items. §1V.2's rule, applied to
     these three: `--dock-chip-h` and `--dock-chip-text`, everything inside
     the chip the same size, heights measured on the element that carries
     the background. */
  #screen-42 .dock-head { min-height: var(--dock-chip-h); justify-content: center; gap: 12px; }
  #screen-42 .dock-info { display: none; }
  #screen-42 .dock-info-desk { display: contents; }
  #screen-42 .dock-head .ti-item {
    display: inline-flex; align-items: center; gap: .3em;
    min-height: var(--dock-chip-h); box-sizing: border-box;
    padding: 0 14px; font-size: var(--dock-chip-text); line-height: 1;
  }
  #screen-42 .dock-head .ti-item b,
  #screen-42 .dock-head .ti-item small,
  #screen-42 .dock-head .ti-lbl,
  #screen-42 .dock-head .ti-trump { font-size: var(--dock-chip-text); }
  #screen-42 .dock-head .ti-item small { opacity: .6; }
  #screen-42 .dock-head .ti-lbl { margin-right: 2px; }
  #screen-42 .dock-head .ti-trump { padding: 2px 10px; margin-left: 4px; }
  /* 42's lane and Moon's: shown here, the plate at the left end and the
     sentence at the right, 30% of the felt's width in from each side. */
  #screen-42 .felt-lane.lane-desk {
    display: flex; justify-content: space-between; align-items: center;
    flex: none; flex-wrap: nowrap; gap: 12px; padding: 6px 30% 10px;
  }
  /* 25% a side, not 30%, on a screen under 1600: the lane is 546px at
     1366 and a plate carrying a long name is 284 of it (the name itself is
     capped at 190px), which left the sentence 250px and "You won the bid —
     name your trumps." on two lines — the one case §1AF.38's fix above did
     not reach, found by the suite's own hostile-name profile. At 25% the
     lane is 683 and the longest sentence (373px) has its room beside the
     widest plate. Above 1600 the 30% lane is wide enough as it is. */
  @media (max-width: 1599px) {
    #screen-42 .felt-lane.lane-desk { padding-left: 25%; padding-right: 25%; }
  }
  #screen-42 .felt-lane .side-slot { display: flex; width: auto; padding: 0; }
  .status-stack.stack-lane {
    display: flex; position: static; transform: none; left: auto; top: auto;
    width: auto; align-items: flex-end; text-align: right;
    /* No max-width here: the width is the lane's, set with the 900px rule
       above (§1AF.38). A second copy of that number is how the 60/40 split
       went wrong. */
    /* The stack does not size the lane. It was in flow, so three passing
       messages made the lane 76px taller and the plate — centred in it —
       rode up with every bid and back down as they faded: *"my name and
       icon and the status move slightly when things are happening … makes
       it seem cheap"* (4 Sep, Moon; 42's lane is the same lane). The box
       has no height of its own and hangs from the lane's floor: whatever
       is in it — the standing sentence, a passing message with no sentence
       under it, three of them — sits on that floor and stacks upward over
       the felt, where there is room and nothing in flow to push.
       `column-reverse` is what puts the newest message on top and keeps
       the standing line still; the markup order (standing first, then
       passing) is unchanged.

       The first cut of this was a two-line box centred beside the plate,
       and a one-line sentence sat on that box's floor, mid-lane, while a
       passing message alone sat there too — the bottom edge of the text
       moved between states, *"it still looks weird as it's going … it
       should always be bottom and right justified"* (5 Sep, §1AE.24).
       Measured by `pagecheck` with three messages injected: plate, disc
       and lane do not move a pixel, in either print size. */
    height: 0; min-height: 0; overflow: visible; align-self: flex-end;
    flex-direction: column-reverse; justify-content: flex-start;
    /* `flex: 1 1 0; min-width: 0`, not `flex: none`. With no max-width the
       stack is as wide as its longest sentence, and a flex item that may
       not shrink is pushed onto a second row of the lane when it does not
       fit — Moon's lane grew 12px (one gap) under a 68-character sentence
       at 1280 the night the max-width came off (§1AF.38). Grow into what
       is left after the plate, shrink to it, wrap the words inside. */
    flex: 1 1 0; min-width: 0;
  }
  #screen-moon .felt-lane {
    display: flex; justify-content: space-between; align-items: center;
    flex: none; flex-wrap: nowrap; gap: 12px; padding: 6px 30% 10px;
  }
  /* The phone's bid chip (§1AE.48) is not a desktop thing: the head row is
     the score's, and the shield on the bidder's disc carries the bid. */
  #screen-moon .dock-bid { display: none; }
  #screen-moon .felt-lane .me-plate { margin: 0; }
  #screen-moon .status-stack:not(.stack-lane) { display: none; }
  /* Fives: the lane, shown; the dock plate, gone; the status line, a live
     region only. */
  #screen-fives .me-plate { display: flex; }
  #screen-fives .dock-plate { display: none; }
  #screen-fives .dock-head { min-height: var(--dock-chip-h); }
  /* And Moon's: its head row was 1px taller with a bid chip in it than
     without (4 in Big print), and the lane above the dock — plate, name,
     status — moved by that much at the first bid. One floor, whatever the
     row holds (4 Sep, the *"makes it seem cheap"* report). */
  #screen-moon .dock-head { min-height: var(--dock-chip-h); box-sizing: border-box; }
  #screen-fives .status-line .status-msg {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  #screen-fives .status-line { min-height: 0; height: auto; padding: 0; }
  /* 42 and Moon too, from 4 Sep. Their status line kept its pinned 70px on
     a desktop (`.status-line { height: 70px }`, written for the phone's
     commentary and winning by source order) while the sentence itself
     lives in the lane — so there was a 70px band of nothing between the
     header and the partner's seat: *"too much space between header bar and
     my partner's icon/name"*. The live region stays, off screen, as on
     Fives. On 42 the trick area keeps its place — see `.seats-mid` below —
     so the room goes between the partner's back and the north bone, which
     was four pixels. */
  #screen-42 .status-line .status-msg, #screen-moon .status-line .status-msg {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  #screen-42 .status-line, #screen-moon .status-line { min-height: 0; height: auto; padding: 0; }
  /* And its sides: the row starts to the right of the record's box — the
     box's width (3.2 bones and 76), its 10px inset and 8px of daylight —
     and pads the east side the same so the trick stays on the felt's
     centre (§1AE.29, 5 Sep: *"you forgot to shift left player towards the
     middle"*). `--rec-bone` is the screen's, declared with the box.
     `pagecheck` holds the seat's edge clear of the box and the north bone
     on the felt's centre at five widths. */
  #screen-42 .seats-mid {
    padding-top: 66px;
    padding-left: calc(var(--rec-bone) * 3.2 + 94px);
    padding-right: calc(var(--rec-bone) * 3.2 + 94px);
  }
  /* A third in from each side — the same number on all four tables, from
     his marks on the 42 and Fives shots (§1AC.28). */
  /* 16% and 20%, from 30% each side (4 Sep): *"my icon and name a little
     further to the left to give the game board more space … my icon/name
     could be closer to the dock"*, and the commentary *"a little more to
     the right"*. The lane's padding comes down to 2 and 4 for the same
     reason. */
  #screen-fives .felt-lane { justify-content: space-between; flex-wrap: nowrap; gap: 12px; padding: 2px 20% 4px 16%; }
  /* The seats hug the header: half the gap they had (*"push the other
     player's icons just a tad closer to the top header … perhaps 50%"*). */
  #screen-fives .fives-seats { padding-top: 2px; padding-bottom: 0; }
  #screen-fives .felt-lane .me-plate { margin: 0; flex: none; }
  #screen-fives .lane-status {
    display: block; flex: 1 1 auto; min-width: 0; text-align: right;
    font-size: 21px; font-weight: bold; line-height: 1.25; color: var(--status-text);
    text-shadow: 0 2px 6px rgba(0,0,0,.55);
  }
  .fives-seats { align-items: flex-start; padding-top: 2px; }
  /* *"It could be as tall as the two rows of the dominoes"* — and on 4 Sep,
     more exactly: *"make the scorepad top the same as the top of the top
     domino row and the bottom of the scorepad the same as the bottom of the
     bottom domino row."* The 132 used to be the DRAWING's height, with a
     head and a total stacked outside it, so the pad ran to 207 and the
     pad's column set the dock's grid row: 68 for the head row plus 220 for
     the pad's box, against 200 for the head row plus the hand. The 88px
     between was dead felt under your bones. Now 132 is the PAD's height —
     the hand's two rows at this deck, 56 a bone and a 20px gap — and the
     drawing takes what is left between a smaller head and total. The pad
     column's bottom padding goes for the same reason. `pagecheck` holds the
     pad's top and bottom to the hand's rows at 930, 1254 and 1913. */
  #screen-fives .dock-pad { padding-bottom: 0; }
  #screen-fives .fives-pad { height: 132px; box-sizing: border-box; display: flex; }
  #screen-fives .fives-pad .pad-cols { flex: 1 1 auto; min-height: 0; align-items: stretch; }
  #screen-fives .fives-pad .pad-col { display: flex; flex-direction: column; min-height: 0; }
  #screen-fives .fives-pad .pad-col-head { margin-bottom: 3px; padding-bottom: 1px; font-size: 13px; }
  #screen-fives .fives-pad .pad-total { margin-top: 2px; font-size: 14px; }
  #screen-fives .fives-pad svg { flex: 1 1 0; min-height: 0; height: auto; width: auto; max-width: none; }
  /* `max-width: none`, for §1AC.23's reason: the pad is `max-content`-sized
     and its only sized content was the labels, so a percentage cap resolved
     against 56px and squeezed the drawing to a sliver at 1254. The drawing
     is 192 wide at this height and the side column is 263. */
}

/* ---------- the brand's typeface, at last ---------- §1Y.3

   TeX Gyre Bonum Bold, subset by `fontbuild.js` to what the app sets in it
   and served as one 29KB WOFF2 from `fonts/`. The stack has led with this
   name since v1.77.0 and nothing was ever downloaded, so every phone fell to
   Georgia; with this, headings, buttons, chips and the tag are the brand's
   face everywhere. `font-display: swap` — the words are on the screen in the
   fallback and swap when the file lands, which on a return visit is at once
   (the service worker holds it). GUST Font License; the text is beside the
   font in brand/fonts/tex-gyre/. Bold only, which is what the stack asks
   for; Regular is a second line here if the rule book's prose ever wants
   it. */
@font-face {
  font-family: 'TeX Gyre Bonum';
  /* Relative to this sheet's own folder — it is served from
     `assets/app.<hash>.css`, so a bare `fonts/` resolved to `assets/fonts/`
     (a 404 in every browser suite on the first run), and a root-relative
     `/fonts/` broke the suites that open `site/index.html` from disk, where
     `/` is the drive. `../fonts/` is right over http and from a file. The
     single file swaps this for a data URI (`build.js`, `single`). */
  src: url('../fonts/bonum-bold.woff2') format('woff2');
  font-weight: bold;
  font-style: normal;
  font-display: swap;
}

/* ---------- §1AA.1: the PLAYED record's shapes, behind a dev flag ----------
   Three heights for the corner record, so Mike can point at one instead of
   describing it. Reached only with `?played=a|b|c` (`ui.js`, boot); nothing
   here applies without the class. The record is the box in the dock's left
   column that runs from the column's top to its bottom; these give it a
   height of its own and size the bones from seven rows, as the item asks.
     a — the free height on the dock's side: the column, plus what the felt
         has under the west seat, drawn as 120px of climb (a shape, not a
         rule — CLAUDE.md's typed-offset warning stands; this is for a
         photograph).
     b — a fixed 520.
     c — a fixed 680.
   A row is one upright bone plus 4, and the panel's chrome is about 60, so
   the bone is (height - 60) / 7 - 4: 38, 61, 84. */
@media (min-width: 900px) {
  #screen-42.played-a .my-dock, #screen-42.played-b .my-dock, #screen-42.played-c .my-dock { overflow: visible; }
  #screen-42.played-a .dock-side.dock-left .trick-record,
  #screen-42.played-b .dock-side.dock-left .trick-record,
  #screen-42.played-c .dock-side.dock-left .trick-record {
    top: auto; bottom: 0; z-index: 6; width: max-content; max-width: 340px;
  }
  #screen-42.played-a .dock-side.dock-left .trick-record { height: calc(100% + 120px); --rec-bone: 38px; }
  #screen-42.played-b .dock-side.dock-left .trick-record { height: 520px; --rec-bone: 61px; }
  #screen-42.played-c .dock-side.dock-left .trick-record { height: 680px; --rec-bone: 84px; }
  #screen-42.played-a .trick-record:not(.tr-chip) .domino.small.h,
  #screen-42.played-b .trick-record:not(.tr-chip) .domino.small.h,
  #screen-42.played-c .trick-record:not(.tr-chip) .domino.small.h { width: var(--rec-bone); height: calc(var(--rec-bone) / 2); }
  #screen-42.played-a .trick-record:not(.tr-chip) .domino.small.v,
  #screen-42.played-b .trick-record:not(.tr-chip) .domino.small.v,
  #screen-42.played-c .trick-record:not(.tr-chip) .domino.small.v { width: calc(var(--rec-bone) / 2); height: var(--rec-bone); }
  #screen-42.played-a .trick-record:not(.tr-chip) .domino.small .half,
  #screen-42.played-b .trick-record:not(.tr-chip) .domino.small .half,
  #screen-42.played-c .trick-record:not(.tr-chip) .domino.small .half { width: calc(var(--rec-bone) / 2); height: calc(var(--rec-bone) / 2); }
}

/* ---------- the lane on a phone: 42 and Moon (§1AE.46, §1AE.54, 5 Sep) ----------

   *"Move my name and icon to the bottom left and the status to the bottom
   right. Should be bottom and right justified."* Moon first, then *"just
   like we're doing with Moon"* for 42. The desktop's lane (above, ≥900) is
   the same shape a third in from each side; the phone has no thirds to
   spare, so the plate sits at the left edge and the sentence at the right,
   both on the lane's floor. 42's centred plate (`.me-plate`, absolute at the
   felt's foot) and its middle stack are hidden here, and `statusStack()`
   picks the lane's stack on every width now. The stack keeps the desktop's
   zero-height trick so passing messages climb over the felt and nothing in
   the lane moves. Fives and Taroky keep their own `.lane-status`. */
@media (max-width: 899px) {
  #screen-42 .felt-lane.lane-desk {
    display: flex; justify-content: space-between; align-items: flex-end;
    flex: none; gap: 10px; padding: 2px 10px 6px;
    /* `nowrap`, against the base lane's `wrap`: at 360 a long sentence
       ("Becky is thinking about a bid…") made the stack wider than the
       room beside the plate and the whole stack dropped to a second line,
       left-aligned — 139px in from the right, 10px under the plate's floor
       (`pagecheck`, first cut). The stack shrinks and the words wrap
       inside it instead. */
    flex-wrap: nowrap;
  }
  #screen-42 .felt-lane .side-slot { display: flex; width: auto; padding: 0; }
  #screen-42 .me-plate { display: none; }
  #screen-42 .tp-mid .status-stack:not(.stack-e):not(.stack-lane) { display: none; }
  #screen-moon .felt-lane {
    justify-content: space-between; align-items: flex-end;
    gap: 10px; padding: 2px 10px 6px; flex-wrap: nowrap;
  }
  #screen-moon .felt-lane .me-plate { margin: 0; }
  #screen-moon .status-stack:not(.stack-lane) { display: none; }
  .status-stack.stack-lane {
    display: flex; position: static; transform: none; left: auto; top: auto;
    width: auto; max-width: 62%; min-width: 0; align-items: flex-end; text-align: right;
    height: 0; min-height: 0; flex: 0 1 auto; overflow: visible; align-self: flex-end;
    flex-direction: column-reverse; justify-content: flex-start;
  }
  .stack-lane .st-standing { text-align: right; font-size: 16px; line-height: 1.2; overflow-wrap: anywhere; }
  /* TABLE RULES: two lines of chip words in LEAVE's box, top right. The box
     itself is set from LEAVE's measurement in `matchRulesToLeave`; this is
     the lettering — stacked, no gap, no side padding to spend. §1AE.45. */
  .table-top .rules-btn { flex-direction: column; gap: 1px; padding: 0 4px; line-height: 1; flex: none; }
  /* Its box is LEAVE's, so it may not be shrunk to fit: `flex: none` above,
     because on 42 and Fives the chip sits in a `.tt-side` flex wrapper
     (`min-width: 0`) that was quietly taking 6px off it at 360. What gives
     instead is the bar's own air: at 360 the two chips (94 each) and the
     wordmark (158) come to 346, and 14px is all that is left for padding
     and gaps — 5 + 5 and 2 + 2, exactly. From 400 there is room to spare.
     LEAVE keeps its size; it moves 3px left, which is the bar's, not its. */
  @media (max-width: 399px) {
    .table-top { padding-left: 5px; padding-right: 5px; gap: 2px; }
  }
  /* Moon's plate first in the lane. The older phone rule above (`order: 3`,
     which put the plate last among the felt's children when the lane was
     only the plate's box) carried into the lane and put the plate on the
     RIGHT of the sentence; measured 5 Sep, plate 225..350, sentence 18..140. */
  #screen-moon .felt-lane .me-plate { order: 0; }
}
