/* Kings and Queens of Pluck.
   The look comes from what Sumner actually described: a real card table that his circle
   plays at all day, not a casino. Deep felt green, warm wood around the edge, brass and
   gold for the crown, and cards big enough to read at arm's length on a phone. He is 51
   and plays on a handset, so nothing here is smaller than it needs to be. */

:root {
  --felt:        #10422f;
  --felt-deep:   #0a2d20;
  --felt-lit:    #17573d;
  /* The wood was one flat mid brown, which is the colour of cheap laminate and read as
     muddy against the felt. Real wood is dark, and it is never one colour: it is a deep
     base with grain running through it and light catching the top edge. */
  --wood:        #26170e;
  --wood-lit:    #3a2416;
  --wood-dark:   #150c06;
  --grain:       rgba(0,0,0,.22);
  --grain-lit:   rgba(196,150,96,.055);
  --brass:       #d8a94b;
  --brass-lit:   #f0cd7d;
  --cream:       #f6f1e4;
  --ink:         #17140f;
  --muted:       #b9c9bf;
  --red:         #c02a2a;
  --line:        rgba(246,241,228,.16);
  --panel:       rgba(0,0,0,.24);
  --radius:      14px;
  --shadow:      0 10px 30px rgba(0,0,0,.35);
  --card-w:      clamp(58px, 14.6vw, 88px);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; overflow-x: clip; }
body {
  /* Sumner sent the AI Factory Spades app and asked for the whole interface to look more
     eye catching like it. What does the work in that app is not the colour, it is DEPTH:
     a lit pool in the middle of a dark, misty room, everything else falling away.
     So the felt stays his (it is a card table, and it is what he described), but it is now
     a deep table lit from above with haze hanging over it rather than a flat green field. */
  background:
    radial-gradient(80% 46% at 50% 0%,  rgba(255,232,178,.13) 0%, transparent 62%),
    radial-gradient(58% 34% at 50% 34%, rgba(140,255,205,.055) 0%, transparent 70%),
    repeating-linear-gradient(45deg,  rgba(255,255,255,.013) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(-45deg, rgba(0,0,0,.055) 0 1px, transparent 1px 3px),
    radial-gradient(135% 100% at 50% -6%, #1a6647 0%, var(--felt) 34%, var(--felt-deep) 74%, #05190f 100%);
  background-attachment: fixed;
  color: var(--cream);
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  font-size: 17px;
  line-height: 1.5;
  min-height: 100vh;
  -webkit-text-size-adjust: 100%;
  position: relative;
}
/* Dust in the light over the table. Two pinned layers of soft specks, the thing that stops
   a dark background reading as an empty rectangle. Never intercepts a tap. */
body::before {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0; opacity: .5;
  background-image:
    radial-gradient(1.6px 1.6px at 12% 22%, rgba(255,240,205,.55), transparent 60%),
    radial-gradient(1.3px 1.3px at 78% 14%, rgba(255,240,205,.42), transparent 60%),
    radial-gradient(1.8px 1.8px at 62% 62%, rgba(255,240,205,.30), transparent 60%),
    radial-gradient(1.2px 1.2px at 27% 74%, rgba(255,240,205,.38), transparent 60%),
    radial-gradient(1.5px 1.5px at 88% 47%, rgba(255,240,205,.28), transparent 60%),
    radial-gradient(1.1px 1.1px at 41% 38%, rgba(255,240,205,.34), transparent 60%),
    radial-gradient(1.4px 1.4px at 8% 56%, rgba(255,240,205,.26), transparent 60%),
    radial-gradient(1.2px 1.2px at 94% 82%, rgba(255,240,205,.30), transparent 60%);
}

/* Vignette. Pinned, never scrolls, never intercepts a tap. */
body::after {
  content: ''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background: radial-gradient(115% 80% at 50% 38%, transparent 45%, rgba(0,0,0,.42) 100%);
}
.wrap, .top, .table-wrap { position: relative; z-index: 1; }

h1, h2, h3 { font-family: Georgia, "Times New Roman", serif; letter-spacing: .2px; margin: 0 0 8px; }
/* Every section heading is struck gold, not plain cream. This is the single change that
   makes the whole app read closer to the app Sumner sent: in it, nothing important is
   flat text. */
h2, h3 {
  background: linear-gradient(180deg, #fff4d2 8%, #edcb7c 46%, #c69a3c 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  text-shadow: none;
  filter: drop-shadow(0 1px 0 rgba(0,0,0,.45));
}
/* The crown. Struck gold with a dark bevel under it, which is the whole trick in the
   Spades app Sumner sent: the letters look pressed into the surface rather than typed on. */
.crown {
  font-family: Georgia, "Times New Roman", serif; font-weight: 700;
  text-transform: uppercase; letter-spacing: clamp(2px, 1.2vw, 7px);
  background: linear-gradient(180deg, #fff2c8 4%, #e6bf62 34%, #a9772a 62%, #f2d68d 96%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 2px 0 rgba(0,0,0,.55)) drop-shadow(0 5px 14px rgba(0,0,0,.45));
  line-height: 1.05;
}
.crown .sx { -webkit-text-fill-color: #d8a94b; color: #d8a94b; font-size: .8em; }
h1 { font-size: clamp(28px, 6vw, 44px); }
h2 { font-size: clamp(21px, 4vw, 28px); }
a { color: var(--brass-lit); }
.muted { color: var(--muted); }
.wrap { max-width: 1080px; margin: 0 auto; padding: 0 16px; }
.center { text-align: center; }

/* ---- Chrome ---------------------------------------------------------- */
.top {
  /* Grain runs along the plank, so on a bar it runs across. Two repeating gradients at
     slightly different periods keep it from reading as stripes. */
  background:
    repeating-linear-gradient(179deg, var(--grain) 0 1px, transparent 1px 4px),
    repeating-linear-gradient(181deg, var(--grain-lit) 0 1px, transparent 1px 9px),
    linear-gradient(180deg, var(--wood-lit) 0%, var(--wood) 62%, var(--wood-dark) 100%);
  box-shadow:
    inset 0 1px 0 rgba(226,182,120,.16),        /* light catching the top edge */
    0 2px 0 rgba(0,0,0,.4), 0 6px 18px rgba(0,0,0,.35);
  border-bottom: 1px solid rgba(216,169,75,.35);  /* brass hairline against the felt */
  position: sticky; top: 0; z-index: 40;
}
.top .wrap { display: flex; align-items: center; gap: 14px; height: 58px; }
.logo {
  font-family: Georgia, serif; font-weight: 700; font-size: 19px;
  color: var(--brass-lit); text-decoration: none; display: flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
.logo .crown { font-size: 17px; }
/* The bar must never widen the page. It was the one element quietly wider than narrow
   phones (five admin items plus three switches), which scrolled the whole document
   sideways and made the fixed call sheet look clipped off screen (Sumner's Samsung,
   2026-08-03). Overflow now stays inside the nav, swipeable, invisible scrollbar. */
.top nav { margin-left: auto; display: flex; gap: 4px; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.top nav::-webkit-scrollbar { display: none; }
.top nav a {
  color: var(--cream); text-decoration: none; padding: 7px 12px; border-radius: 9px;
  font-size: 15px; opacity: .82; white-space: nowrap;
}
/* Long labels by default, short ones on a narrow phone, where the full wording wraps and
   pushes the bar to three lines. The default has to come FIRST or it wins the cascade
   against the media query and the nav disappears entirely. */
.top nav a .short { display: none; }
@media (max-width: 560px) {
  .top .wrap { gap: 6px; }
  .top nav { gap: 1px; }
  .top nav a { padding: 7px 9px; font-size: 14px; }
  .top nav a .long { display: none; }
  .top nav a .short { display: inline; }
}
.top nav a:hover { background: rgba(0,0,0,.2); opacity: 1; }
.top nav a.on { background: rgba(0,0,0,.3); opacity: 1; color: var(--brass-lit); }

/* Desktop gets the side menu the doctrine asks for; phones keep the top bar. */
@media (min-width: 900px) {
  body.has-rail { padding-left: 214px; }
  .rail {
    position: fixed; inset: 0 auto 0 0; width: 214px; z-index: 50;
    /* Standing plank, so the grain runs vertically. */
    background:
      repeating-linear-gradient(91deg, var(--grain) 0 1px, transparent 1px 5px),
      repeating-linear-gradient(89deg, var(--grain-lit) 0 1px, transparent 1px 11px),
      linear-gradient(100deg, var(--wood-lit) 0%, var(--wood) 55%, var(--wood-dark) 100%);
    box-shadow:
      inset -1px 0 0 rgba(226,182,120,.1),
      2px 0 0 rgba(0,0,0,.4), 6px 0 22px rgba(0,0,0,.34);
    border-right: 1px solid rgba(216,169,75,.3);
    padding: 18px 12px; display: flex; flex-direction: column; gap: 4px;
  }
  .rail .logo { font-size: 20px; padding: 0 8px 16px; display: block; line-height: 1.25; }
  .rail a.nav {
    color: var(--cream); text-decoration: none; padding: 11px 12px; border-radius: 10px;
    display: flex; align-items: center; gap: 10px; opacity: .85;
  }
  .rail a.nav:hover { background: rgba(0,0,0,.2); opacity: 1; }
  .rail a.nav.on { background: rgba(0,0,0,.32); color: var(--brass-lit); opacity: 1; }
  .rail .foot { margin-top: auto; font-size: 13px; }
  body.has-rail .top { display: none; }
}
@media (max-width: 899px) { .rail { display: none; } }

/* ---- Bits ------------------------------------------------------------- */
.panel {
  background:
    linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,0) 38%),
    radial-gradient(120% 80% at 50% -10%, rgba(216,169,75,.07), transparent 60%),
    var(--panel);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px;
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.09),
    inset 0 0 34px rgba(0,0,0,.22),
    0 14px 34px rgba(0,0,0,.4);
}
.btn {
  appearance: none; border: 0; cursor: pointer; font: inherit; font-weight: 700;
  /* The PLAY! button in his reference is a struck plate: a bright lip along the top, a
     deep bevel under it, and a hard shadow it sits on. Three layers, not one gradient. */
  background:
    linear-gradient(180deg, rgba(255,255,255,.5) 0 1px, transparent 1px),
    linear-gradient(180deg, #ffeec3 0%, var(--brass-lit) 30%, var(--brass) 72%, #b98a2c 100%);
  color: #2a1d05; padding: 14px 22px; border-radius: 12px;
  letter-spacing: .2px;
  text-shadow: 0 1px 0 rgba(255,255,255,.35);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.75),
    inset 0 -2px 0 rgba(0,0,0,.22),
    inset 0 0 0 1px rgba(120,88,20,.55),
    0 4px 0 #8a6720,
    0 10px 22px rgba(0,0,0,.45);
  transition: transform .06s, box-shadow .06s, filter .12s;
}
.btn:hover { filter: brightness(1.05); }
.btn:active { transform: translateY(3px); box-shadow: inset 0 1px 0 rgba(255,255,255,.55), inset 0 0 0 1px rgba(120,88,20,.55), 0 1px 0 #8a6720; }
.btn:disabled { opacity: .5; cursor: default; transform: none; }
.btn.full { display: block; width: 100%; }
.btn.ghost {
  background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(0,0,0,.14));
  color: var(--cream); text-shadow: none;
  border: 1px solid rgba(216,169,75,.34);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.09), 0 3px 10px rgba(0,0,0,.3);
}
.btn.ghost:active { transform: translateY(2px); box-shadow: inset 0 1px 0 rgba(255,255,255,.09); }
.btn.sm { padding: 8px 13px; font-size: 14px; border-radius: 9px; }
.field { margin-bottom: 13px; }
.field label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 5px; }
.field input, .field select {
  width: 100%; font: inherit; padding: 12px; border-radius: 10px;
  border: 1px solid var(--line); background: rgba(0,0,0,.3); color: var(--cream);
}
.row { display: flex; align-items: center; gap: 10px; }
.right { margin-left: auto; }
.grid { display: grid; gap: 13px; }
.pill {
  display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px;
  border: 1px solid var(--line); background: rgba(0,0,0,.25); font-weight: 700;
}
.pill.gold { color: var(--brass-lit); border-color: var(--brass); }
.empty { text-align: center; color: var(--muted); padding: 34px 16px; }
.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%) translateY(20px);
  background: var(--ink); color: var(--cream); padding: 12px 18px; border-radius: 11px;
  border: 1px solid var(--line); opacity: 0; pointer-events: none; transition: .18s; z-index: 90;
  max-width: 88vw; text-align: center; box-shadow: var(--shadow);
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* ---- Cards ------------------------------------------------------------ */
.card {
  width: var(--card-w); aspect-ratio: 5 / 7;
  border-radius: clamp(3px, calc(var(--card-w) * 0.055), 9px);
  /* Real card stock: warm cream, a little light off the top left, never flat white. */
  background:
    radial-gradient(120% 100% at 30% 10%, #fffefa 0%, var(--cream) 55%, #e9dfc9 100%);
  color: var(--ink); position: relative;
  /* The gold hairline is what makes the AI Factory cards read as a deck rather than tiles,
     and it is the one detail that survives being drawn 50 pixels wide. */
  box-shadow:
    0 4px 12px rgba(0,0,0,.45),
    inset 0 0 0 1px rgba(255,255,255,.75),
    inset 0 0 0 2.5px rgba(176,138,58,.5);
  padding: 3px 4px; font-weight: 800; line-height: 1; user-select: none;
  border: 1px solid rgba(0,0,0,.32); overflow: hidden;
}
/* A real printed card. One sprite holds all 52 (4 suit columns, 13 rank rows), so a
   whole hand costs one cached request and every card is proper deck artwork, courts
   included. The .card rules above still supply the stock, the corner radius and the
   shadow; the sprite only supplies the face. */
.card.sprite {
  /* The printed face is inset so the card's own edge and gold hairline never crop the
     index (David 2026-08-03: "numbers and letters are right against the edge"). The
     sprite is positioned against the CONTENT box, so the percentages still land on the
     right cell; the cream stock fills the whole card behind it, which is exactly the
     white margin a real card has around its face. */
  padding: 0;
  background:
    url('/cards.webp') no-repeat
    calc(var(--col) * 33.3333%) calc(var(--row) * 8.3333%) / 400% 1300%,
    radial-gradient(120% 100% at 30% 10%, #fffefa 0%, var(--cream) 55%, #e9dfc9 100%);
  /* The printed card already carries its own white margin, so the stock's gold hairline
     and inner ring are what crowded the index (David 2026-08-03: "right against the
     edge... an interior border doing that"). A real card has one clean edge. Insetting
     the ART instead was worse: in a fan you only see a sliver, and the index moved out
     of it. */
  border-color: rgba(0,0,0,.24);
  box-shadow: 0 4px 12px rgba(0,0,0,.45);
}
/* The two jokers out of the same deck. Red is the Big, black is the Little. */
.card.sprite-joker {
  padding: 3px 4px;
  background:
    url('/jokers.webp') no-repeat 0 0 / 200% 100%,
    radial-gradient(120% 100% at 30% 10%, #fffefa 0%, var(--cream) 55%, #e9dfc9 100%);
  border-color: rgba(0,0,0,.24);
  box-shadow: 0 4px 12px rgba(0,0,0,.45);
}
.card.sprite-joker.lil { background-position: 100% 0, 0 0; }
.card.sprite-joker .ix b {
  font-size: clamp(8px, calc(var(--card-w) * 0.17), 13px); letter-spacing: .5px;
  background: rgba(255,254,250,.86); border-radius: 3px; padding: 0 2px;
}
.card.sprite-joker.big .ix b { color: var(--red); }
.card.sprite-joker .ix { width: auto; }
/* The corner index. Everything you need to identify the card lives in this strip, which is
   the part that stays visible when the next card overlaps it. Both corners, like a real
   deck, so a hand fanned the other way still reads. */
.card .ix { display: flex; flex-direction: column; align-items: center; width: calc(var(--card-w) * 0.38); position: relative; z-index: 2; }
/* Sized off the CARD, never the viewport. The old vw clamps meant a shrunken fan card
   still wore a full-size index, and on Sumner's Samsung the indexes and pips of
   neighbouring cards painted over each other (2026-08-03). */
.card .ix b { font-size: clamp(13px, calc(var(--card-w) * 0.32), 25px); font-style: normal; letter-spacing: -.5px; }
.card .ix i { font-size: clamp(10px, calc(var(--card-w) * 0.26), 20px); font-style: normal; margin-top: -2px; }
.card .ix.br {
  position: absolute; right: 4px; bottom: 3px; transform: rotate(180deg);
}
/* In the fanned hand the rotated corner is noise: eighteen overlapped cards turn it into
   a row of upside-down glyphs along the bottom (Sumner's screenshot, 2026-08-02). A fan
   only ever reads from the top-left corner, so the second corner stays on table cards
   and disappears in the hand. */
.hand .card .ix.br { display: none; }
/* A fanned card shows ONLY its corner index. Overlapped cards each painting a full pip
   field tiled into one unbroken carpet of symbols with no visible card edges (Sumner's
   fourth screenshot, 2026-08-03) — a printed face needs the whole card, and a sliver
   is not a whole card. The one fully exposed card on the right keeps its face, and
   cards on the table always show theirs. */
.hand .card .face { display: none; }
.hand .card:last-child .face { display: block; }
.hand .card .face .court { display: none; }
.hand .card:last-child .face .court { display: flex; }
/* The pip field. Absolute so the layout is the printed one, not a flex approximation. */
/* The pip field sits in the middle band, clear of both corner indices, the way a printed
   card does it. Sized off the card itself so a 9 and a 10 never touch at any width. */
.card .face { position: absolute; inset: 21% 19% 21% 19%; z-index: 1; }
.card .face .pp {
  position: absolute; transform: translate(-50%, -50%);
  font-size: calc(var(--card-w) * .15); font-style: normal; line-height: 1;
}
.card .face .pp.flip { transform: translate(-50%, -50%) rotate(180deg); }
.card .face .court {
  /* A bare giant letter read as unfinished next to the corner index (Sumner, 2026-08-02).
     The medallion makes the court card a printed design instead of a duplicate glyph. */
  position: absolute; inset: 8% 6%; display: flex; align-items: center; justify-content: center;
  font-family: Georgia, "Times New Roman", serif; font-size: clamp(15px, calc(var(--card-w) * 0.42), 30px);
  font-weight: 700; letter-spacing: -1px;
  border: 1.5px solid rgba(176, 138, 58, .55); border-radius: 50%;
  background: radial-gradient(circle at 35% 25%, rgba(255,255,255,.55), rgba(176,138,58,.10));
  box-shadow: inset 0 0 0 3px rgba(255, 254, 250, .6);
}
.card .face .court i { font-style: normal; font-size: .5em; margin-left: 2px; opacity: .85; }
.card .face .jk {
  position: absolute; inset: -16% -18%; display: flex; align-items: center; justify-content: center;
  font-style: normal; font-size: clamp(16px, calc(var(--card-w) * 0.44), 38px);
}
.card.red { color: var(--red); }
/* The jokers carry the deck's own artwork now (see .card.sprite-joker above); this rule
   is only the fallback tint for a joker drawn before that image loads. */
.card.joker:not(.sprite-joker) {
  background: radial-gradient(120% 100% at 30% 10%, #fffdf4 0%, #ffeec2 60%, #f3dc9d 100%);
  color: #8a6410;
}
.card.joker:not(.sprite-joker) .ix { width: 2.2em; }
.card.joker:not(.sprite-joker) .ix b { font-size: clamp(8px, calc(var(--card-w) * 0.16), 12px); letter-spacing: .5px; }
.card.back {
  background-image: repeating-linear-gradient(45deg, var(--deck-ink, #9d2426) 0 6px, var(--deck-edge, #d4585a) 6px 12px);
  border: 2px solid var(--deck-edge, #d4585a);
}
/* In a fanned hand every card overlaps the one before it, so the card drawn on top owns
   that patch of screen and owns the tap. Left alone that means a legal card hiding under an
   illegal one: you tap what looks like the ace you want to play, and the dead card lying
   over it swallows it.
   So the cards you are allowed to play come forward, above the ones you are not, and stand
   proud of the fan. On a phone there is no hover, so this lift is the only thing telling
   you which cards are live, and it doubles as making them the ones you can actually hit. */
.card.play {
  cursor: pointer; transition: transform .1s, box-shadow .1s;
  z-index: 5; transform: translateY(var(--lift, -10px)); box-shadow: 0 8px 16px rgba(0,0,0,.45);
}
.card.play:hover { --lift: -18px; box-shadow: 0 14px 22px rgba(0,0,0,.55); }
/* Dead cards stay full strength. Sumner wants every card in the fan as bold as the live
   ones: the fan is his whole read of the hand, and a ghosted card is a card he cannot
   count. The lift, spread and shadow on .play carry the which-cards-are-live signal on
   their own. */
.card.dead { cursor: not-allowed; z-index: 1; }
.card.dead:hover { transform: none; }

/* The hand fans out and overlaps, because eighteen cards do not fit on a phone otherwise. */
/* One row, never two: fitHand() in table.html scales --card-w until the fan fits. If a
   hand still cannot fit at the 44px floor, it swipes inside its own row; the auto margins
   keep it centered when it does fit (justify-content:center would clip the left edge of
   an overflowing flex row). */
/* THE CIRCULAR FAN. Sumner photographed his real hand and asked for exactly this
   (2026-08-03): every card visible at once, no sliding, arced the way a hand is
   actually held. Each card pivots around a point well below the screen; fitFan() in
   table.html computes the per-card rotation and offset so the whole arc fits the
   width of any phone. The rotation is the trick that keeps tight packing readable:
   the corners fan APART as the bottoms tuck together. */
/* The fan. #hand is the real one, pinned to the bottom of the screen so your cards
   never scroll away or hide behind a sheet; a .hand inside a sheet (the pluck picker)
   stays in the flow of that sheet. */
.hand { position: relative; display: block; padding: 0; }
/* Above the scrim on purpose: the popup dims the table, never your own cards, because
   calling high or low means reading what you are holding. */
#hand { position: fixed; left: 0; right: 0; bottom: 0; z-index: 68; }
.hand .card, .hand .card.play {
  position: absolute; left: 50%; top: 6px; margin: 0;
  transform: translateX(-50%) translate(var(--tx, 0px), var(--ty, 0px)) rotate(var(--rot, 0deg)) translateY(var(--lift, 0px));
  transform-origin: 50% 100%;
}
/* Your playable cards ride a little higher out of the pack, along their own tilt,
   which is both the highlight and the fatter tap target. */
.hand .card.play { --lift: -12px; }
.hand .card.play:active { --lift: -22px; }

/* ---- The table -------------------------------------------------------- */
.felt {
  border-radius: 22px;
  /* The rail around the table is the same wood as the chrome, grain and all. */
  border: 9px solid transparent;
  background:
    repeating-linear-gradient(45deg,  rgba(255,255,255,.016) 0 1px, transparent 1px 3px) padding-box,
    repeating-linear-gradient(-45deg, rgba(0,0,0,.055) 0 1px, transparent 1px 3px) padding-box,
    radial-gradient(70% 60% at 50% 38%, var(--felt-lit), var(--felt-deep)) padding-box,
    repeating-linear-gradient(120deg, var(--grain) 0 1px, transparent 1px 5px) border-box,
    linear-gradient(150deg, var(--wood-lit), var(--wood) 60%, var(--wood-dark)) border-box;
  box-shadow:
    inset 0 0 70px rgba(0,0,0,.5),
    inset 0 1px 0 rgba(255,255,255,.05),
    0 1px 0 rgba(216,169,75,.18),
    var(--shadow);
  padding: 14px; margin: 12px 0;
}
.seats { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; margin-bottom: 10px; }
/* The table the way Sumner drew it: opponents across from you at the top corners, you at
   the bottom in front of your own hand. */
.seats-top { display: flex; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.seats-top .seat { flex: 1 1 0; max-width: 47%; }
.seat-me { display: flex; justify-content: center; margin-top: 10px; }
.seat-me .seat { flex: 0 1 62%; min-width: 150px; }
.seat .scoreline { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: .4px; }
.seat .scoreline i { font-style: normal; font-family: Georgia, serif; font-size: 16px; color: var(--brass-lit); letter-spacing: 0; }
.seat {
  background: rgba(0,0,0,.26); border: 1px solid var(--line); border-radius: 12px;
  padding: 9px 8px; text-align: center; min-width: 0;
}
.seat.turn { border-color: var(--brass); box-shadow: 0 0 0 2px rgba(216,169,75,.34); }
.seat .who {
  font-weight: 800; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.seat .books { font-family: Georgia, serif; font-size: 21px; color: var(--brass-lit); }
.seat .need { font-size: 12px; color: var(--muted); }
.trick {
  display: flex; justify-content: center; align-items: center; gap: 20px;
  min-height: calc(var(--card-w) * 1.4 + 22px); padding: 14px 8px 8px;
}
.trick .slot { position: relative; }
/* Name tags are wider than the cards they label, so without a ceiling two of them collide
   into an unreadable smear. Clamp to the card and let a long name ellipsis. */
.trick .slot { padding-top: 22px; }
.trick .tag {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 3;
  font-size: 10px; background: var(--ink); padding: 1px 6px; border-radius: 999px;
  border: 1px solid var(--line); white-space: nowrap;
  max-width: calc(var(--card-w) + 16px); overflow: hidden; text-overflow: ellipsis;
}
.calltag {
  display: flex; justify-content: center; gap: 8px; flex-wrap: wrap; margin-bottom: 6px;
}
.scorebar {
  display: flex; gap: 8px; justify-content: center; flex-wrap: wrap;
  font-size: 14px; padding: 8px 0;
}
/* The call sheet is a POPUP (David 2026-08-03: "could just be a popup that darkens
   everything behind it until you choose"). It floats over a dimmed table, sized to its
   content, never full bleed and never inner-scrolling on a desktop. The one thing the
   scrim does NOT dim is your hand: you cannot call high or low without reading the
   cards you are holding, so #hand sits above the scrim and stays lit. */
.sheet-scrim {
  /* Stops short of the fan rather than relying on z-index to lift the cards out of it:
     the popup dims the TABLE, and your own hand stays fully lit underneath it. */
  position: fixed; inset: 0 0 var(--hand-h, 0px) 0; z-index: 64;
  background: rgba(3, 14, 9, .38);
  animation: fade-in .18s ease both;
}
@keyframes fade-in { from { opacity: 0; } }
.sheet {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(var(--hand-h, 0px) + 14px); z-index: 70;
  width: min(560px, calc(100vw - 24px));
  background: linear-gradient(180deg, #164a34, #0a2d20);
  border: 2px solid var(--brass); border-radius: 18px;
  padding: 18px 18px 20px; box-shadow: 0 18px 50px rgba(0,0,0,.6);
  max-height: calc(100dvh - var(--hand-h, 0px) - 84px); overflow: auto;
}
.sheet h2 { font-size: clamp(19px, 4.4vw, 26px); margin-bottom: 4px; }
.sheet p { margin-bottom: 10px; }
.sheet .inner { max-width: 520px; margin: 0 auto; }
.suitpick { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
.suitpick button {
  font: inherit; font-size: 22px; font-weight: 800; padding: 13px 4px; cursor: pointer;
  border-radius: 11px; border: 1px solid var(--line); background: rgba(0,0,0,.3); color: var(--cream);
}
.suitpick button.on { background: var(--brass); color: #2a1d05; border-color: var(--brass-lit); }
.suitpick button.red { color: #ff8b8b; }
.suitpick button.on.red { color: #5a0f0f; }
.twoup { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; }
.twoup button {
  font: inherit; font-weight: 700; padding: 13px 8px; cursor: pointer; border-radius: 11px;
  border: 1px solid var(--line); background: rgba(0,0,0,.3); color: var(--cream);
}
.twoup button.on { background: var(--brass); color: #2a1d05; border-color: var(--brass-lit); }
/* A light .muted sub-line on a gold button is unreadable (David 2026-08-03: the
   "A, K, Q, J, 10" line). Anything inside a struck-gold button goes dark with it. */
.twoup button.on .muted, .suitpick button.on .muted, .btn .muted { color: rgba(42, 29, 5, .8); }

/* ---- Ladder ----------------------------------------------------------- */
.decks { display: grid; grid-template-columns: repeat(auto-fill, minmax(136px, 1fr)); gap: 12px; }
.deck { text-align: center; padding: 13px 8px; border-radius: 13px; border: 1px solid var(--line); background: var(--panel); }
.deck .chip {
  width: 54px; height: 74px; margin: 0 auto 9px; border-radius: 7px;
  background-image: repeating-linear-gradient(45deg, var(--ink) 0 6px, var(--edge) 6px 12px);
  border: 2px solid var(--edge); box-shadow: 0 3px 8px rgba(0,0,0,.4);
}
.deck.locked { opacity: .42; }
.deck.current { border-color: var(--brass); box-shadow: 0 0 0 2px rgba(216,169,75,.3); }
.bar { height: 9px; border-radius: 999px; background: rgba(0,0,0,.35); overflow: hidden; }
.bar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--brass), var(--brass-lit)); }

/* ---- Motion ------------------------------------------------------------
   A card game is mostly waiting for other people to put something down. If their card
   simply appears, the hand reads as a spreadsheet updating. It has to travel. */

/* A card landing on the table, thrown from the seat that played it. */
@keyframes land-left  { from { transform: translate(-150%, -40%) rotate(-24deg); opacity: 0; } }
@keyframes land-right { from { transform: translate( 150%, -40%) rotate( 24deg); opacity: 0; } }
@keyframes land-mine  { from { transform: translate(0, 130%)     rotate(  4deg); opacity: 0; } }
.trick .slot.from-left  { animation: land-left  .34s cubic-bezier(.2,.8,.3,1) both; }
.trick .slot.from-right { animation: land-right .34s cubic-bezier(.2,.8,.3,1) both; }
.trick .slot.from-mine  { animation: land-mine  .3s  cubic-bezier(.2,.8,.3,1) both; }

/* The book being dragged in by whoever took it. */
.trick.sweep-0 .slot { animation: sweep-mine .42s ease-in both; }
.trick.sweep-1 .slot { animation: sweep-left .42s ease-in both; }
.trick.sweep-2 .slot { animation: sweep-right .42s ease-in both; }
@keyframes sweep-mine  { to { transform: translate(0, 150%)      rotate(-8deg) scale(.7); opacity: 0; } }
@keyframes sweep-left  { to { transform: translate(-170%, -90%)  rotate(-18deg) scale(.7); opacity: 0; } }
@keyframes sweep-right { to { transform: translate( 170%, -90%)  rotate( 18deg) scale(.7); opacity: 0; } }

/* Your own card leaving your hand, before the server has even been asked. */
@keyframes throw-card { to { transform: translateY(-190px) scale(.82) rotate(3deg); opacity: 0; } }
.card.thrown { animation: throw-card .26s ease-in both; pointer-events: none; }

/* Dealing. The hand arrives a card at a time rather than all at once. */
/* Dealing fades the arc in. The old fly-in keyframes fought the fan's inline
   transform and snapped cards out of the arc on the last frame. */
@keyframes deal-in { from { opacity: 0; } }
.hand.dealing .card { animation: deal-in .4s ease both; }

/* The seat that just took a book. */
@keyframes book-bump { 50% { transform: scale(1.32); color: var(--brass-lit); } }
.seat.took .books { animation: book-bump .44s ease-out; }
.seat { transition: border-color .2s, box-shadow .2s; }

/* The sheet coming up from the bottom. */
@keyframes sheet-up { from { transform: translateY(100%); } }
.sheet { animation: pop-in .22s cubic-bezier(.2,.9,.3,1) both; }
@keyframes pop-in { from { opacity: 0; transform: translateX(-50%) translateY(14px) scale(.97); } }

/* The pluck. Cards going one way and coming back heavier. */
@keyframes pluck-out { from { transform: translateX(-40px); opacity: 0; } }
@keyframes pluck-back { from { transform: translateX(40px); opacity: 0; } }
.pluck-row .went { animation: pluck-out .4s ease-out both; }
.pluck-row .came { animation: pluck-back .4s ease-out .18s both; }

.sound-btn {
  background: none; border: 0; color: var(--brass-lit); opacity: .85; cursor: pointer;
  font-size: 17px; padding: 6px 7px; line-height: 1; position: relative;
}
.sound-btn:hover { opacity: 1; }
/* Struck through when off, which is the one visual anybody reads instantly. */
.sound-btn.off { color: var(--muted); opacity: .45; }
.sound-btn.off::after {
  content: ''; position: absolute; left: 3px; right: 3px; top: 50%;
  border-top: 2px solid currentColor; transform: rotate(-24deg);
}
#mscBar { font-size: 21px; }
/* A hairline so the controls read as their own group rather than two more nav items. */
.top nav .sound-btn:first-of-type { margin-left: 7px; border-left: 1px solid var(--line); padding-left: 11px; }

/* Somebody who asked their phone to stop moving things gets a still game. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important; animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th, table.data td { text-align: left; padding: 8px 9px; border-bottom: 1px solid var(--line); }
table.data th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .5px; }
.scroller { overflow-x: auto; }

/* Cards inside the pluck receipt are a reminder, not a hand. Half size. */
.pluck-row .card { --card-w: 40px; }

/* A card ticked for the pluck. Has to read as "chosen" at a glance while you are picking
   four out of eighteen, so it lifts further than a playable card and takes a gold edge. */
.card.picked {
  transform: translateY(-24px);
  box-shadow: 0 0 0 3px var(--brass), 0 12px 20px rgba(0,0,0,.5);
  z-index: 8;
}
.card.picked:hover { transform: translateY(-28px); }

/* Cards in the sheet lift when picked, so the row needs headroom or they climb over the
   line of text above them. Cost me the "Jokers cannot be sent" note on the pluck picker. */
.sheet .hand { padding-top: 30px; }

/* The deck somebody is playing with, on their seat. Small, because it is context, not the
   score, but present because the whole ladder is built on other people being able to see it. */
.seat .deckline {
  font-size: 10px; color: var(--muted); display: flex; align-items: center;
  justify-content: center; gap: 4px; margin-top: 2px; white-space: nowrap;
}
.backchip-sm { width: 9px; height: 13px; border-radius: 2px; border: 1px solid; flex: none; }

/* The turn clock. Only ever a number, because a ring or a bar reads as decoration and this
   is a rule with teeth: run it out and the table plays your card for you. */
.clock {
  min-width: 34px; height: 34px; border-radius: 50%; flex: none;
  display: flex; align-items: center; justify-content: center;
  font-family: Georgia, serif; font-size: 16px; font-weight: 700;
  border: 2px solid var(--line); color: var(--muted); background: rgba(0,0,0,.25);
}
.clock:empty { display: none; }
.clock.mine { border-color: var(--brass); color: var(--brass-lit); }
.clock.hot { border-color: var(--red); color: #ff9a9a; animation: clock-pulse .6s ease-in-out infinite; }
@keyframes clock-pulse { 50% { transform: scale(1.12); } }

/* The score sheet. Pinned under the header so it survives the bottom sheet, because the one
   moment you most need the score is while you are deciding what to send somebody. */
.sheetline {
  position: sticky; top: 0; z-index: 30;
  display: flex; align-items: center; gap: 8px; padding: 7px 10px; margin: 0 0 8px;
  background: rgba(6,26,18,.93); border: 1px solid var(--line); border-radius: 12px;
  backdrop-filter: blur(6px);
}
.sheetline .lbl { font-size: 10px; line-height: 1.15; color: var(--muted); text-transform: uppercase; letter-spacing: .4px; flex: none; }
.sheetline .lbl .to { color: var(--brass); }
.sheetline .sc {
  flex: 1; min-width: 0; text-align: center; border-left: 1px solid var(--line);
  display: flex; flex-direction: column; line-height: 1.1;
}
.sheetline .sc b { font-size: 11px; font-weight: 700; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sheetline .sc i { font-family: Georgia, serif; font-style: normal; font-size: 19px; }
.sheetline .sc.lead i { color: var(--brass-lit); }
.sheetline .sc.hot i { color: #ff9a9a; }

/* A player's face. Same shape everywhere it appears: the table, the standings, their page,
   the chat. Round, so it never gets confused with a card. */
.av {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  border-radius: 50%; border: 2px solid; overflow: hidden; color: #fff;
  box-shadow: 0 2px 6px rgba(0,0,0,.4); line-height: 1; vertical-align: middle;
  text-shadow: 0 1px 2px rgba(0,0,0,.5);
}
.av img { width: 100%; height: 100%; object-fit: cover; display: block; }

.avpick { display: grid; grid-template-columns: repeat(auto-fill, minmax(58px, 1fr)); gap: 10px; }
.avpick button {
  background: none; border: 0; padding: 4px; cursor: pointer; border-radius: 12px;
  display: flex; flex-direction: column; align-items: center; gap: 4px;
  font: inherit; font-size: 10px; color: var(--muted);
}
.avpick button.on { background: rgba(216,169,75,.16); outline: 2px solid var(--brass); }

/* ---- Table talk ------------------------------------------------------- */
.chatbtn {
  position: fixed; right: 14px; bottom: calc(14px + env(safe-area-inset-bottom)); z-index: 60;
  width: 54px; height: 54px; border-radius: 50%; border: 2px solid var(--brass);
  background: linear-gradient(180deg, var(--wood-lit), var(--wood));
  color: var(--brass-lit); font-size: 22px; cursor: pointer; box-shadow: var(--shadow);
}
.chatbtn .badge {
  position: absolute; top: -4px; right: -4px; min-width: 20px; height: 20px; padding: 0 5px;
  border-radius: 999px; background: var(--red); color: #fff; font-size: 11px; font-weight: 800;
  display: flex; align-items: center; justify-content: center; border: 2px solid var(--felt-deep);
}
.chatpanel {
  position: fixed; inset: auto 0 0 0; z-index: 75; max-height: 74vh; display: flex; flex-direction: column;
  background: linear-gradient(180deg, #143c2c, #0a2d20);
  border-top: 3px solid var(--brass); border-radius: 18px 18px 0 0;
  box-shadow: 0 -14px 40px rgba(0,0,0,.5);
  padding: 14px 14px calc(14px + env(safe-area-inset-bottom));
}
.chatpanel .log { overflow-y: auto; flex: 1; margin: 10px 0; display: flex; flex-direction: column; gap: 10px; }
.chatmsg { display: flex; gap: 8px; align-items: flex-start; }
.chatmsg .bub {
  background: rgba(0,0,0,.3); border: 1px solid var(--line); border-radius: 12px;
  padding: 7px 11px; max-width: 78%; overflow-wrap: anywhere;
}
.chatmsg .who { font-size: 11px; color: var(--muted); display: block; margin-bottom: 1px; }
.chatmsg.mine { flex-direction: row-reverse; }
.chatmsg.mine .bub { background: rgba(216,169,75,.16); border-color: rgba(216,169,75,.4); }
.chatrow { display: flex; gap: 8px; }
.chatrow input { flex: 1; font: inherit; padding: 12px; border-radius: 11px;
  border: 1px solid var(--line); background: rgba(0,0,0,.3); color: var(--cream); }

/* ---- Eggs and the Lucky Bonus Wheel ---------------------------------------
   Sumner, 2026-08-02: "A player earns Eggs by daily login, winning games, and Spinning the
   Lucky Bonus Wheel." The wheel is theatre over a result the server already decided, so it
   only ever has to look like it is spinning. */
.eggs { border-color: rgba(216,169,75,.34); }
.eggpill {
  display: inline-flex; align-items: center; gap: 8px;
  background: linear-gradient(180deg, rgba(216,169,75,.22), rgba(216,169,75,.08));
  border: 1px solid rgba(216,169,75,.45); border-radius: 999px;
  padding: 7px 15px 7px 11px; font-family: Georgia, serif;
}
.eggpill .yolk { font-size: 19px; line-height: 1; }
.eggpill b { font-size: 22px; color: var(--brass-lit); }
.chip.sm { width: 14px; height: 20px; display: inline-block; vertical-align: -4px; border-radius: 3px; }

.wheelbox { position: relative; width: 216px; margin: 16px auto 4px; }
.wheel {
  position: relative; width: 216px; height: 216px; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, rgba(0,0,0,.35) 0 26%, transparent 27%),
    conic-gradient(
      #1d6b48 0deg 45deg,  #145136 45deg 90deg,
      #1d6b48 90deg 135deg, #145136 135deg 180deg,
      #1d6b48 180deg 225deg, #145136 225deg 270deg,
      #1d6b48 270deg 315deg, #145136 315deg 360deg);
  border: 6px solid var(--brass);
  box-shadow: 0 0 0 3px rgba(0,0,0,.45), 0 12px 30px rgba(0,0,0,.5), inset 0 0 26px rgba(0,0,0,.45);
  transform: rotate(0deg);
}
.wheel .sl {
  position: absolute; left: 50%; top: 8px; transform-origin: 50% 100px;
  transform: translateX(-50%) rotate(var(--a));
  font-family: Georgia, serif; font-weight: 700; font-size: 15px; color: var(--cream);
  text-shadow: 0 1px 2px rgba(0,0,0,.7);
}
.wheel .hub {
  position: absolute; inset: 50% auto auto 50%; transform: translate(-50%, -50%);
  width: 52px; height: 52px; border-radius: 50%; display: grid; place-items: center;
  background: radial-gradient(circle at 35% 30%, #f3d489, var(--brass) 60%, #8d6a1f);
  border: 2px solid rgba(0,0,0,.4); font-size: 22px;
  box-shadow: 0 3px 10px rgba(0,0,0,.5);
}
.wheelbox .ptr {
  position: absolute; left: 50%; top: -10px; transform: translateX(-50%) rotate(180deg);
  color: var(--brass-lit); font-size: 22px; text-shadow: 0 2px 4px rgba(0,0,0,.6);
}
@media (prefers-reduced-motion: reduce) { .wheel { transition: none !important; } }


/* The big plate on the front page, sized like the PLAY! button in Sumner's reference. */
.btn.play-big {
  text-decoration: none; display: inline-block;
  font-family: Georgia, "Times New Roman", serif;
  font-size: clamp(21px, 5vw, 27px); letter-spacing: 1px;
  padding: 17px 44px; border-radius: 16px;
}

/* ---- Fit the table to the phone -------------------------------------- */
/* Sumner, 2026-08-03: "should just be built to fit the screen". On a short handset the
   felt alone was 543 of 800 pixels, so the page scrolled and the call sheet had to
   fight the fan for room. Everything here trims the table, never the cards: the hand
   is the one thing that stays full size. */
@media (max-height: 920px) {
  .seat { padding: 6px 7px; }
  .seat .scoreline { font-size: 10px; }
  .seat .scoreline i { font-size: 14px; }
  .seat .who { font-size: 13px; }
  .seat .books { font-size: 18px; }
  .seat .need { font-size: 11px; }
  .seat .deckline { font-size: 10px; }
  .seats-top { margin-bottom: 6px; }
  .seat-me { margin-top: 6px; }
  .felt { border-width: 7px; padding: 8px; }
  .trick { min-height: calc(var(--card-w) * 1.4 + 10px); padding: 8px 8px 4px; }
  .calltag { margin: 4px 0; }
}
@media (max-height: 840px) {
  .seat .deckline, .seat .need { display: none; }
  .trick { min-height: calc(var(--card-w) * 1.2); padding: 4px; }
  .seats-top .seat .avatar, .seats-top .seat img { width: 26px !important; height: 26px !important; }
}
@media (max-height: 720px) {
  /* The smallest handsets: the seats keep only what the game needs, name and books. */
  .seat .scoreline { display: none; }
  .sheetline { padding: 5px 8px; font-size: 12px; }
  .sheetline .sc { font-size: 17px; }
  #app > .row { margin-bottom: 4px; }
  .seat-me .seat .avatar, .seat-me .seat img { display: none; }
  .felt { padding: 5px; border-width: 6px; }
  .trick { min-height: calc(var(--card-w)); }
}

/* ---- Phone navigation: a bottom tab bar ------------------------------- */
/* Rebuilt 2026-08-03 (David: "the scroll nav on the top is bad, no app ever uses
   that"). Five destinations and three switches never fit one phone line, so the old
   bar scrolled sideways and hid its own last item. Navigation now lives where every
   app puts it, and the top strip carries only the name and the switches. */
.top .switches { margin-left: auto; display: flex; gap: 2px; }
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; justify-content: space-around; align-items: stretch;
  background:
    repeating-linear-gradient(179deg, var(--grain) 0 1px, transparent 1px 4px),
    linear-gradient(0deg, var(--wood-dark), var(--wood) 62%, var(--wood-lit));
  border-top: 1px solid rgba(216,169,75,.35);
  box-shadow: 0 -3px 14px rgba(0,0,0,.45);
  padding-bottom: env(safe-area-inset-bottom);
}
.tabbar a {
  flex: 1 1 0; min-width: 0; text-decoration: none; color: var(--cream); opacity: .72;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 8px 2px 7px; font-size: 11.5px; letter-spacing: .2px;
}
.tabbar a .ic { font-size: 18px; line-height: 1; }
.tabbar a.on { opacity: 1; color: var(--brass-lit); }
.tabbar a.on .ic { filter: drop-shadow(0 0 6px rgba(240,205,125,.45)); }
/* Room for the bar, so nothing hides behind it. The table opts out: it has no tabs. */
body:not(.at-table) { padding-bottom: 62px; }
@media (min-width: 900px) {
  .tabbar { display: none; }
  body:not(.at-table) { padding-bottom: 0; }
}

/* The three switches, drawn rather than typed, so they read as one set. */
.sound-btn svg { width: 20px; height: 20px; fill: none; stroke: var(--brass-lit); stroke-width: 1.7;
  stroke-linecap: round; stroke-linejoin: round; display: block; }
.sound-btn.off svg { stroke: rgba(246,241,228,.34); }
.sound-btn { display: flex; align-items: center; justify-content: center; }


/* A desktop window is wide but rarely tall. The table compresses the same way a phone
   does so the whole game still lands on one screen. */
@media (min-width: 900px) and (max-height: 1000px) {
  .felt { padding: 6px; }
  #app > .row { margin-bottom: 4px; }
  .sheetline { padding: 6px 10px; }
  .trick { min-height: calc(var(--card-w) * 0.9); padding: 6px; }
  .seats-top { margin-bottom: 4px; }
  .seat-me { margin-top: 4px; }
  .seat { padding: 7px 8px; }
}

/* ---- Data tables on a phone ------------------------------------------ */
/* A wide table in a sideways scroller is the same mistake as the old top nav: it hides
   its own columns behind a swipe nobody discovers (David 2026-08-03: no weird scroll
   bars). Under 700px every row becomes a small card with its labels down the left,
   which is the whole table, readable, with nothing to scroll sideways. */
@media (max-width: 700px) {
  .scroller { overflow-x: visible; }
  table.data, table.data tbody, table.data tr, table.data td { display: block; width: 100%; }
  table.data tr:first-child { display: none; }              /* the header row */
  table.data tr {
    border: 1px solid var(--line); border-radius: 12px; background: rgba(0,0,0,.2);
    padding: 4px 10px; margin-bottom: 10px;
  }
  table.data td { border-bottom: 1px solid rgba(246,241,228,.08); padding: 7px 0; }
  table.data tr td:last-child { border-bottom: 0; }
  table.data td[data-l] { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; }
  table.data td[data-l]::before {
    content: attr(data-l); color: var(--muted); font-size: 11.5px; text-transform: uppercase;
    letter-spacing: .5px; flex: none;
  }
}

/* Your own seat card on a short screen. The score strip at the top already carries your
   score, so the card below the table only needs the two numbers that change in play. */
@media (max-height: 920px) {
  .seat-me .seat { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 6px 10px; }
  .seat-me .seat > div { margin: 0; }
  .seat-me .seat .scoreline, .seat-me .seat .deckline { display: none; }
  /* "You" must never ellipsis to "Y..." in the compact row. */
  .seat-me .seat .who { font-size: 14px; flex: none; overflow: visible; }
  .seat-me .seat .books { font-size: 19px; }
  .seat-me .seat .need { font-size: 11px; }
}


/* The pluck line is one line, always: it sits between the table and the cards and a
   second line pushes the fan off the screen. */
.pluckline {
  font-size: 13px; padding: 5px 12px; max-width: 100%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}


/* ---- The pluck picker ------------------------------------------------- */
.pickgrid {
  display: flex; flex-wrap: wrap; gap: 7px 5px;
  justify-content: center; align-items: flex-start;
  padding: 26px 0 6px;
}
.pickgrid .card {
  position: static; left: auto; top: auto; margin: 0;
  transform: none; width: var(--pick-w, 52px); flex: none;
}
.pickgrid .card .face { display: block; }
.pickgrid .card .face .court { display: flex; }
.pickgrid .card.play { cursor: pointer; }
.pickgrid .card.play:active { transform: translateY(-4px); }
.pickgrid .card.picked {
  transform: translateY(-10px);
  box-shadow: 0 0 0 3px var(--brass), 0 10px 18px rgba(0,0,0,.5);
}
.pickgrid .card.picked:hover { transform: translateY(-13px); }
.pickgrid .card.dead { opacity: .38; filter: grayscale(.5); }
@media (min-width: 700px) { .pickgrid { --pick-w: 64px; } }

/* The countdown, mirrored inside any popup that takes a decision. The table's own clock
   sits behind the dim, and when this one hits zero the server plays the turn for you, so
   it has to be readable from inside the popup itself. */
.sheetclock {
  position: absolute; top: 12px; right: 14px;
  min-width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 700; color: var(--brass-lit);
  border: 1.5px solid rgba(176, 138, 58, .55);
  background: rgba(0,0,0,.28);
}
.sheetclock:empty { display: none; }
.sheetclock.hot { color: #ffd9d0; border-color: #b4442f; background: rgba(150,40,25,.35); }
.sheet .inner { position: relative; }
.sheet h2 { padding-right: 44px; }
.sheet h2 + p { padding-right: 46px; }

/* ---- The strength dial ------------------------------------------------- */
/* Sumner's chess screenshot: arrows either side, the number big in the middle, and a word
   under it saying what that number actually means. The number alone is a setting; the
   number plus the name is a choice. */
.dial { display: flex; align-items: center; gap: 12px; }
.dialbtn {
  flex: none; width: 54px; height: 54px; border-radius: 12px;
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; color: #2a1d05; cursor: pointer; border: 0;
  background: linear-gradient(#f4dc9a, #cfa63f 58%, #a9801f);
  box-shadow: 0 3px 0 #6d5210, 0 6px 12px rgba(0,0,0,.4);
}
.dialbtn:active { transform: translateY(2px); box-shadow: 0 1px 0 #6d5210, 0 3px 8px rgba(0,0,0,.4); }
.dialbtn:disabled { opacity: .3; cursor: not-allowed; transform: none; }
.dialmid { flex: 1; min-width: 0; text-align: center; }
.diallabel { font-size: 11px; letter-spacing: 1.6px; text-transform: uppercase; color: var(--muted); }
.dialnum {
  font-family: Georgia, serif; font-size: clamp(38px, 11vw, 54px); line-height: 1.05;
  color: var(--brass-lit);
}
.dialnum span { font-size: 17px; color: var(--muted); }
.dialname { font-size: 13.5px; color: #e6ecdf; margin-top: 2px; min-height: 19px; }
.dialtrack {
  height: 7px; border-radius: 999px; margin-top: 12px; overflow: hidden;
  background: rgba(0,0,0,.34); border: 1px solid rgba(0,0,0,.3);
}
.dialfill {
  height: 100%; border-radius: 999px; transition: width .18s ease;
  background: linear-gradient(90deg, #7d9a6b, #cfa63f 70%, #f4dc9a);
}
