/* 3/33 — a home game, not a casino.
   The table is an object: green baize inside a brown leather rail, sitting
   in a dark room. Anything that isn't play — the feed, the stakes, the
   utility buttons — lives off the table, out on the room.

   White does the work gold used to: whose turn it is, the poles of the
   track, the line telling you what it costs. Red is confined to card
   faces, where it sits on near-white stock; fine red text on green is
   hard to read, so signals on the baize use yellow instead.

   Numbers are set in mono throughout — this is a game of running totals
   and half points, and it should read like a score pad. */

:root {
  --room: #1a1917;
  --room-rise: #232120;
  --baize: #1f5b3e;
  --baize-deep: #1a4c34;
  --rail: #4a2b24;
  --rail-edge: #35201b;
  --white: #ffffff;
  --card-stock: #f7f4ed;
  --ink: #16150f;
  --pip-red: #b3202e;
  --signal: #e8c547;
  --muted: #9fb6a8;
  --rule: rgba(255, 255, 255, 0.16);

  --display: 'Hoefler Text', 'Baskerville', 'Iowan Old Style', serif;
  --body: -apple-system, BlinkMacSystemFont, 'Avenir Next', 'Segoe UI', sans-serif;
  --data: 'SF Mono', 'Menlo', 'Consolas', monospace;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  min-height: 100vh;
  background: var(--room);
  color: var(--card-stock);
  font-family: var(--body);
  font-size: 20px;
  line-height: 1.55;
}

/* ---------- masthead ---------- */

.topbar { text-align: center; padding: 1.5rem 1rem 0.5rem; }

.topbar h1 {
  font-family: var(--display);
  font-size: 3.1rem;
  font-weight: 400;
  margin: 0;
  color: var(--card-stock);
}

/* The platform's name is words rather than a mark, so it wants a little
   less size and a little more air than "3/33" does at the same weight. */
.topbar-name {
  font-size: 2.6rem;
  letter-spacing: 0.02em;
}

@media (max-width: 620px) {
  .topbar-name { font-size: 2rem; }
}

.topbar .slash { color: var(--muted); margin: 0 0.1em; }

.tagline {
  margin: 0.2rem 0 0;
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- shared ---------- */

.hidden { display: none !important; }

button {
  font-family: var(--body);
  font-size: 1.05rem;
  font-weight: 600;
  padding: 0.85rem 1.6rem;
  border-radius: 4px;
  border: 1px solid var(--card-stock);
  background: var(--card-stock);
  color: var(--ink);
  cursor: pointer;
  min-height: 52px;
  transition: background 0.12s ease, color 0.12s ease;
}

button:hover { background: var(--white); }
button:active { transform: translateY(1px); }
button:focus-visible { outline: 3px solid var(--white); outline-offset: 2px; }
button:disabled { opacity: 0.35; cursor: not-allowed; }

/* ---------- sitting down ---------- */

.panel { max-width: 640px; margin: 0 auto; padding: 1rem; }

#joinPanel {
  display: flex;
  gap: 0.9rem;
  justify-content: center;
  align-items: end;
  flex-wrap: wrap;
  padding-top: 3rem;
}

#joinPanel label,
.lobby-join label,
.ante-control label {
  display: block;
  font-family: var(--data);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  margin-bottom: 0.4rem;
  color: var(--muted);
}

#nameInput,
.ante-control input {
  padding: 0.7rem 0.85rem;
  border-radius: 4px;
  border: 1px solid var(--rule);
  background: rgba(0, 0, 0, 0.3);
  color: var(--card-stock);
  font-size: 1.05rem;
  min-height: 44px;
}

.ante-control input { width: 100%; font-family: var(--data); }

#nameInput:focus,
.ante-control input:focus { outline: none; border-color: var(--white); }

#nameInput::placeholder { color: var(--muted); opacity: 1; }

/* The name box is the only thing there is to do on this screen, so it says
   so. The frame is the same dashed white one that marks your hole card and
   your secret marker on the track; the breathing is the same 2.4s fade that
   marks whose turn it is. Nothing new invented — both were already in the
   room. It shows only while the box is still empty and unfocused, so it
   stops the moment you start typing and doesn't nag once you've answered. */
#nameInput:not(:focus):placeholder-shown {
  outline: 3px dashed rgba(255, 255, 255, 0.6);
  outline-offset: 4px;
}

@media (prefers-reduced-motion: no-preference) {
  #nameInput:not(:focus):placeholder-shown {
    animation: breathe-frame 2.4s ease-in-out infinite;
  }
}

@keyframes breathe-frame {
  0%, 100% { outline-color: rgba(255, 255, 255, 0.35); }
  50% { outline-color: rgba(255, 255, 255, 1); }
}

.join-error {
  color: var(--signal);
  font-size: 0.95rem;
  width: 100%;
  text-align: center;
  margin: 0.5rem 0 0;
}

/* ---------- the landing page ---------- */

/* No table on this page — there's no game yet. It sits plainly on the
   room surface, with one obvious thing to do and a quieter second way in
   for people who were handed a code over the phone. */

.lobby {
  max-width: 560px;
  margin: 0 auto;
  padding: 3rem 1.25rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  text-align: center;
}

.lobby-create {
  width: 100%;
  font-size: 1.3rem;
  padding: 1.2rem 2rem;
  min-height: 68px;
}

.lobby-or {
  margin: 1rem 0 0;
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--muted);
}

.lobby-join {
  display: flex;
  gap: 0.9rem;
  align-items: end;
  flex-wrap: wrap;
  justify-content: center;
  width: 100%;
  margin: 0;
}

/* A code is read out loud and typed back in, so it's set large and wide
   in mono — the same score-pad treatment the numbers get everywhere else.
   The text-indent cancels the trailing letter-space so it sits centred. */
#codeInput {
  font-family: var(--data);
  font-size: 1.8rem;
  letter-spacing: 0.32em;
  text-indent: 0.32em;
  text-align: center;
  text-transform: uppercase;
  width: 100%;
  max-width: 250px;
  padding: 0.7rem 0.85rem;
  border-radius: 4px;
  border: 1px solid var(--rule);
  background: rgba(0, 0, 0, 0.3);
  color: var(--card-stock);
  min-height: 64px;
}

#codeInput:focus { outline: none; border-color: var(--white); }

.lobby-hint {
  margin: 0;
  font-size: 0.95rem;
  color: var(--muted);
}

/* ---------- the room ---------- */

.room {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.25rem;
  width: 100%;
  max-width: 1400px;
  margin: 0 auto;
  padding: 1rem 1.25rem 3rem;
  align-items: start;
}

@media (min-width: 1000px) {
  .room { grid-template-columns: minmax(0, 1fr) 300px; }
}

/* ---------- the table ---------- */

.table-surface {
  background: var(--baize);
  border: 16px solid var(--rail);
  border-radius: 22px;
  box-shadow: inset 0 0 0 1px var(--rail-edge), inset 0 0 60px rgba(0, 0, 0, 0.28);
  padding: 1.5rem 1.5rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
  min-height: 460px;
}

.table-info {
  display: flex;
  align-items: center;
  gap: 1.5rem;
  justify-content: center;
  flex-wrap: wrap;
  padding-bottom: 0.9rem;
  border-bottom: 1px solid var(--rule);
}
/* Hard right, away from the status text it sits beside. */
.table-info .track-toggle { margin-left: auto; }

.pill {
  font-family: var(--data);
  font-size: 0.82rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}


/* ---------- the 3—33 track: the signature ---------- */

.track { position: relative; padding: 0.2rem 0 1rem; }

.track-head { display: flex; align-items: center; gap: 0.7rem; margin-bottom: 0.4rem; }

/* Cycles: everyone -> just you -> hidden.
   No border. A thin outlined box read as an unfinished element rather
   than a choice, so it is set as text in the same monospace as the
   status line it now sits on, and says which state it is in. */
.track-toggle {
  flex: none;
  border: 0;
  background: transparent;
  padding: 0.15rem 0;
  font-family: var(--data);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  cursor: pointer;
}
.track-toggle:hover { color: var(--card-stock); }
.track-toggle:focus-visible { outline: 2px solid var(--signal); outline-offset: 3px; }


/* The scale only labels the three totals that mean anything: the low
   pole, the point past which any card can bust you, and the high pole. */
.track-scale {
  position: relative;
  flex: 1;
  height: 1rem;
  font-family: var(--data);
  font-size: 0.72rem;
  color: var(--muted);
}
.track-scale span { position: absolute; transform: translateX(-50%); }

.lanes { display: flex; flex-direction: column; gap: 0.3rem; }

/* One lane each. Overlap isn't mitigated here, it's impossible. */
.lane { display: flex; align-items: center; gap: 0.6rem; }
.lane-name {
  flex: none;
  width: 4.6rem;
  text-align: right;
  font-size: 0.92rem;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lane.is-me .lane-name { color: var(--card-stock); }
.lane.is-busted { opacity: 0.45; }

.lane-rail { position: relative; flex: 1; height: 1.15rem; border-radius: 3px; overflow: hidden; }

.lane-total {
  flex: none;
  width: 2.4rem;
  font-family: var(--data);
  font-size: 0.95rem;
  color: var(--muted);
}
.lane.is-me .lane-total { color: var(--card-stock); font-weight: 700; }

/* Colour lives on the ground, not on the markers, so the two never
   compete for the eye. */
/* Territory, drawn quietly until it is YOUR territory. A band only
   comes up to full strength on the lane of the player standing in it —
   otherwise six lanes would mean six lit warnings and the colour would
   stop meaning anything. */
.band { position: absolute; top: 0; bottom: 0; transition: background 220ms ease; }
.band-under { background: rgba(232, 197, 71, 0.14); }  /* below 3 — you lose from here */
.band-safe  { background: rgba(255, 255, 255, 0.07); } /* nothing can hurt you */
.band-risk  { background: rgba(179, 32, 46, 0.16); }   /* any card can bust you */

.lane.in-under .band-under { background: rgba(232, 197, 71, 0.62); }
.lane.in-risk .band-risk { background: rgba(179, 32, 46, 0.66); }
/* The boundary sharpens for whoever has crossed it. */
.lane.in-risk .band-edge { opacity: 1; box-shadow: 0 0 6px rgba(232, 197, 71, 0.7); }
.band-edge {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2px;
  background: var(--signal);
  opacity: 0.8;
}

.pip {
  position: absolute;
  top: 50%;
  width: 0.85rem;
  height: 0.85rem;
  margin: -0.425rem 0 0 -0.425rem;
  border-radius: 50%;
  background: var(--card-stock);
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.4);
}
/* Your true total, hole card in. Hollow, because it's the one nobody
   else's browser ever receives. */
.pip.is-secret {
  background: transparent;
  border: 2px dashed var(--white);
  box-shadow: none;
}


/* ---------- the other players ---------- */

/* Your hand, the deal button and the other seats, stacked. */
.table-main { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
.table-main > #startHandButton { align-self: center; }

.opponents {
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
  justify-content: flex-start;
}

.opponent {
  background: rgba(0, 0, 0, 0.22);
  border: 2px solid transparent;
  border-radius: 10px;
  padding: 0.85rem 1rem;
  min-width: 215px;
}

.opponent.is-turn { border-color: var(--white); }

@media (prefers-reduced-motion: no-preference) {
  .opponent.is-turn { animation: breathe 2.4s ease-in-out infinite; }
}

@keyframes breathe {
  0%, 100% { border-color: rgba(255, 255, 255, 0.35); }
  50% { border-color: rgba(255, 255, 255, 1); }
}

/* Two jobs, two marks. The dealer rotates every hand; the host owns the
   money rules and mostly doesn't change. A word for each would crowd the
   name, and at a glance a colour reads faster than a label. */
.seat-dot {
  display: inline-block;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  margin-left: 0.45rem;
  vertical-align: middle;
  border: 1px solid rgba(0, 0, 0, 0.35);
}
.dot-dealer { background: var(--white); }
.dot-host { background: var(--signal); }

/* Seat colour says where a player stands without anyone reading a word.
   These are card FILLS, not text — the palette rule that bans red is
   about fine red text on green, which is a legibility problem these
   don't have. Tried as a tip on a cream name row instead (2026-08-21)
   and reverted the same day: the cream card was handsome and far too
   loud for what a name is worth. This layout is dense already and the
   space belongs to the cards. */
.opponent.state-notready {
  /* Needs to read as RED, not as brown. Anything translucent enough to
     let the baize through lands somewhere muddy, so this is close to
     solid. */
  background: rgba(150, 26, 38, 0.92);
  box-shadow: inset 0 0 0 1px rgba(255, 140, 150, 0.35);
}
.opponent.state-won {
  background: rgba(38, 96, 128, 0.92);
  box-shadow: inset 0 0 0 1px rgba(140, 205, 240, 0.45);
}
/* "Normal" is the green it always was — it sits on green baize and has
   always read fine, so the ordinary state is the one that doesn't shout. */
.opponent.state-normal { background: rgba(0, 0, 0, 0.22); }

/* A name is a label, not a headline. Monospace like every other piece of
   furniture here, and small — this reads like a score pad, and the space
   is the game's. */
.opponent-name {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--data);
  font-size: 0.95rem;
  letter-spacing: 0.04em;
  margin-bottom: 0.5rem;
}

.opponent-name .who { overflow-wrap: anywhere; }

/* No well here, deliberately. The token sits straight on the status card,
   which is dark, and the card-stock rim is what carries it -- three of
   the four fills do not clear 3:1 on these backgrounds on their own, and
   the rim is what rescues them. That is exactly the case the rim was
   drawn for. A well would only be needed on a light card, and there is
   no light card any more. See design/seat-card.html. */
.token { display: block; flex: none; }

/* Everyone can see who they are playing. A flag on the seat, never part
   of the name -- the cleaner strips brackets, the gate's A-Z rule would
   eat it, and above all a human must not be able to type it into a name
   box and sit down looking like one. */
.bot-tag {
  font-size: 0.78em;
  letter-spacing: 0.06em;
  color: var(--muted);
}

.bot-control {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
}

.bot-control .pill-button { width: 100%; }

.bot-hint {
  margin: 0;
  font-family: var(--data);
  font-size: 0.75rem;
  letter-spacing: 0.05em;
  color: var(--muted);
  text-align: center;
}

.opponent-status {
  font-family: var(--data);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
  margin-bottom: 0.5rem;
}

.opponent-status.status-allin,
.opponent-status.status-busted-high,
.opponent-status.status-busted-low,
.opponent-status.status-away { color: var(--signal); }

/* Seat held, nobody home. Faded so they stop competing with live players,
   but still clearly occupied — the chair is not free. */
.opponent.is-away { opacity: 0.55; }

/* Two facts, two rows — "18.5 990" read as a single number before. */
.stat-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-size: 0.95rem;
  color: var(--muted);
}

.stat-row .v {
  font-family: var(--data);
  font-size: 1.25rem;
  color: var(--card-stock);
}

.mini-cards { display: flex; gap: 3px; margin: 0.5rem 0; flex-wrap: wrap; }
.mini-cards .card { width: 58px; height: 82px; }

/* Somebody who keeps hitting ends up holding a lot of cards, and laid
   out flat an eleven-card hand wrapped to four rows and made their seat
   446px tall — taller than the table could carry. Fanned, the seat stays
   one row however many they take.
   Tighter than your own fan on purpose: what matters about an opponent
   is the showing total, which is printed right underneath as a number.
   The cards are texture, not arithmetic. */
.mini-cards.is-fanned { flex-wrap: nowrap; gap: 0; }
/* 30px of each card left showing: measured to clear a "10" at this size,
   the same way the big fan was set. Tighter and the only two-character
   rank loses its tail. */
.mini-cards.is-fanned .card { margin-left: -28px; box-shadow: -2px 0 4px rgba(0, 0, 0, 0.4); }
.mini-cards.is-fanned .card:first-child { margin-left: 0; box-shadow: none; }

/* ---------- your hand ---------- */

#myArea { display: flex; flex-direction: column; align-items: center; gap: 0.5rem; }

/* Hit or stand, sitting under your own cards and laid out across rather
   than down — a row of choices reads as a row of choices. */
/* Your own stack, in the column with the decisions it constrains. Big,
   in mono, like the pot -- this reads as a score pad and the number is
   the point. */
.bank {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--rule);
}

.bank-value {
  font-family: var(--data);
  font-size: 2.4rem;
  line-height: 1.05;
  color: var(--white);
}

.bank-label {
  font-family: var(--data);
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin-top: 0.15rem;
}

/* Nothing left to bet with. Yellow is what signals on this table. */
.bank.is-empty .bank-value { color: var(--signal); }

/* Under the buttons it explains. Text rather than a badge on the raise
   button, because the moment the count matters most is when that button
   is not there. */
.raises-note {
  margin: 0.6rem 0 0;
  font-family: var(--data);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
}

.raises-note.is-spent { color: var(--signal); }

.hand-area { display: flex; flex-direction: column; align-items: center; gap: 0.9rem; }

/* Hole card in its own fixed slot on the left, up cards fanned to the
   right of it. One row, one height, however many cards you're holding. */
.hand-layout {
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 1.6rem;
  width: 100%;
}

/* The slot never moves and never collapses, so the hole card is in the
   same place whether you're holding one card or nine — and, eventually,
   whichever game the table is playing. */
.hole-slot { flex: none; }

.up-fan-wrap { display: flex; flex-direction: column; align-items: flex-start; gap: 0.35rem; min-width: 0; }

/* How much of each card the next one leaves showing. Measured, not
   guessed: at this size a single digit's corner mark ends at 45px, K and
   Q at 58px, and "10" — the only two-character rank — runs to 68px. 78px
   clears the widest with room to spare. */
.up-fan { --fan-show: 78px; display: flex; align-items: flex-start; }

/* Holding a lot of small cards. Tightening the fan clips the tail of a
   "10" and nothing else, because 10 is the ONLY rank with two
   characters — a clipped one still can't be mistaken for anything, and
   the card on top is always whole. Better than a hand that runs off the
   table. */
.up-fan.is-crowded { --fan-show: 56px; }

.fan-card { flex: none; }
.fan-card + .fan-card { margin-left: calc(var(--fan-show) - 148px); }

/* The card on top of the fan is the one most recently taken. A hairline
   down the left edge of each keeps the overlap legible as separate cards
   rather than one wide smear. */
.fan-card + .fan-card .card { box-shadow: -3px 0 6px rgba(0, 0, 0, 0.45); }

/* Fanning buys space, so spend it on the rank — that is the only thing
   this game evaluates, and it is now the only thing most cards show. */
.up-fan .corner { font-size: 40px; }
/* A bigger rank needs the suit pushed further clear of it — the rank
   grows upward from a fixed baseline, but the enlarged suit's cap rises
   to meet it. */
.up-fan .corner-suit { font-size: 26px; transform: translateY(4px); }

.card { width: 148px; height: 209px; }

.card-slot { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; }

.card-tag {
  font-family: var(--data);
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--muted);
}

.card-slot-hole .card { outline: 3px dashed var(--white); outline-offset: 4px; border-radius: 8px; }
.card-slot-hole .card-tag { color: var(--white); }

.corner { font: 700 30px var(--display); }
.corner-suit { font-size: 23px; }
.pip-rank { font: 400 72px var(--display); }

/* The joker's wordmark. Monospace, letterspaced and small enough that the
   large A above it still does the reading — the value is what matters at
   a glance, the identity second. Red is fine here: this is a card face,
   sitting on near-white stock, not fine text on the baize. */
.joker-mark { font: 700 15px var(--data); letter-spacing: 0.18em; }
.back-mark { font: 400 26px var(--display); fill: var(--card-stock); }

.my-total { font-family: var(--data); font-size: 1.5rem; color: var(--card-stock); text-align: center; }

/* ---------- acting ---------- */

.controls {
  display: flex;
  gap: 0.8rem;
  justify-content: center;
  flex-wrap: wrap;
  align-items: center;
}

.controls p { width: 100%; text-align: center; margin: 0; color: var(--muted); }

.bet-summary { width: 100%; text-align: center; margin: 0; font-size: 1rem; color: var(--muted); }

.cost-to-you {
  font-family: var(--data);
  font-size: 1.85rem;
  color: var(--white);
}

.bet-building { color: var(--signal); font-family: var(--data); font-size: 1.1rem; }

.chip-buttons { display: flex; gap: 0.7rem; justify-content: center; flex-wrap: wrap; width: 100%; }

/* 64px, not 76. Three of them plus their gaps now fit one row of the
   230px controls column instead of wrapping to two -- Steve offered the
   size and it is worth taking, though it is a tidy-up rather than the
   fix: the column's height stopped mattering when the seat cards were
   moved out of its row. Still a large tap target, which is the rule
   these were sized by in the first place. */
.chip-btn {
  width: 64px;
  height: 64px;
  min-height: 64px;
  border-radius: 50%;
  padding: 0;
  font-family: var(--data);
  font-size: 1.2rem;
  font-weight: 700;
  border: 3px dashed rgba(255, 255, 255, 0.6);
  color: var(--card-stock);
}

.chip-1 { background: var(--card-stock); color: var(--ink); border-color: rgba(0, 0, 0, 0.25); }
.chip-5 { background: var(--pip-red); }
.chip-10 { background: var(--ink); }
.chip-25 { background: #2f6f8f; }
.chip-100 { background: #4a3a6b; }

.chip-clear {
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--card-stock);
  font-size: 0.9rem;
  padding: 0.5rem 1rem;
}

.waiting-line {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  color: var(--muted);
  font-size: 1.05rem;
  width: 100%;
}

.pulse-dot { width: 11px; height: 11px; border-radius: 50%; background: var(--white); }

@media (prefers-reduced-motion: no-preference) {
  .pulse-dot { animation: pulse 1.8s ease-in-out infinite; }
}

@keyframes pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 1; }
}

.deal-button { align-self: center; font-size: 1.15rem; padding: 0.85rem 2rem; }

/* Offering a Legend is a move, so it lives with the other moves. The
   outline marks it out as the one that ends the hand rather than
   continuing it. */
.legend-action { border-color: var(--white); }

/* Still used by the result panel to explain a Legend split after it
   has happened. */
.legend-note { margin: 0.5rem 0 0; font-size: 0.9rem; color: var(--muted); font-style: italic; }

/* ---------- result ---------- */

.result {
  background: rgba(0, 0, 0, 0.28);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 1.1rem 1.3rem;
  text-align: center;
}

.result h3 { font-family: var(--display); font-size: 1.5rem; margin: 0 0 0.5rem; color: var(--white); }
.result p { margin: 0.3rem 0; font-size: 1rem; }

/* The numbers read like a score pad, same as everywhere else here. */
.final-totals strong { font-family: var(--data); color: var(--white); font-weight: 400; }

/* ---------- the side rail (off the table) ---------- */

.side-rail { display: flex; flex-direction: column; gap: 1rem; }

/* The table's own address, off the baize with everything else that isn't
   play. Set large in mono because it gets read out loud down a phone. */
.table-id {
  background: var(--room-rise);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 1rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  align-items: start;
}

.table-code {
  font-family: var(--data);
  font-size: 2.6rem;
  letter-spacing: 0.22em;
  line-height: 1.1;
  color: var(--white);
  transition: font-size 200ms ease, letter-spacing 200ms ease;
}

/* Once the table is underway the code has done its job. It shrinks to
   ordinary UI text and the block folds into one line, which hands the
   space back to the standings and the feed. It never grows again in a
   session -- a code that changed size while you looked at it would read
   as a glitch. */
.table-id.is-compact {
  flex-direction: row;
  align-items: center;
  gap: 0.6rem;
  padding: 0.6rem 0.9rem;
  flex-wrap: wrap;
}

.table-id.is-compact .table-code {
  font-size: 1.05rem;
  letter-spacing: 0.16em;
}

.table-id.is-compact .pill-button {
  width: auto;
  margin-left: auto;
  font-size: 0.7rem;
  padding: 0.4rem 0.7rem;
}

@media (prefers-reduced-motion: reduce) {
  .table-code { transition: none; }
}

.table-id .pill-button { width: 100%; overflow-wrap: anywhere; }

.stakes {
  background: var(--room-rise);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 1rem 1.1rem;
}

.stakes-pot { display: flex; flex-direction: column; margin-bottom: 0.7rem; }

.stakes-label {
  font-family: var(--data);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

.stakes-value { font-family: var(--data); font-size: 3rem; color: var(--white); line-height: 1.1; }

.stakes-row {
  display: flex;
  justify-content: space-between;
  font-size: 0.9rem;
  color: var(--muted);
  font-family: var(--data);
  padding-top: 0.25rem;
}

/* The host's own controls, collapsed to a single line by default so they
   can afford to sit at the very top of the rail. The stakes are the first
   thing a new table needs, and below the fold is the same as absent. */
.host-controls {
  background: var(--room-rise);
  border: 1px solid var(--rule);
  border-radius: 10px;
  overflow: hidden;
}

/* Big enough to hit without aiming. Everything at this table is sized for
   a family with a wide range of eyesight, and a disclosure triangle is
   exactly the control that usually isn't. */
.host-controls-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: none;
  border: none;
  padding: 0.9rem 1.1rem;
  font-family: var(--body);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  cursor: pointer;
  text-align: left;
}

/* The background has to be reset explicitly, not just left off. The
   global button:hover paints white, and this button's text is white on
   hover too -- which rendered the whole collapsed bar as a blank white
   box. Feedback here is a lift in the surface, not a fill. */
.host-controls-toggle:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--white);
}
.host-controls-toggle:focus-visible { outline: 2px solid var(--white); outline-offset: -2px; }
.host-controls.is-open .host-controls-toggle { color: var(--white); }

/* The signal has to survive the panel being shut. Collapsing it while the
   table is still held on the stakes would otherwise hide the yellow
   button and, with it, the only thing saying whose move it is. */
.host-controls.needs-attention .host-controls-toggle { color: var(--signal); }
.host-controls.needs-attention .host-controls-toggle::after {
  content: '';
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--signal);
  margin-left: auto;
  flex: none;
}
.host-controls.needs-attention.is-open .host-controls-toggle::after { display: none; }

.host-controls-caret {
  width: 0;
  height: 0;
  border-left: 7px solid currentColor;
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform 120ms ease;
  flex: none;
}

.host-controls.is-open .host-controls-caret { transform: rotate(90deg); }

/* Rolls up rather than vanishing. Animating to auto height isn't a thing
   CSS will do, but a grid row from 1fr to 0fr is -- the clip layer hides
   what's being squeezed and the pad layer keeps the padding inside the
   part that shrinks, so it doesn't leave a bar of empty space behind. */
.host-controls-body {
  display: grid;
  grid-template-rows: 1fr;
  transition: grid-template-rows 260ms ease;
}
.host-controls-body.collapsed { grid-template-rows: 0fr; }
.host-controls-clip { overflow: hidden; }
.host-controls-pad { padding: 0 1.1rem 1.1rem; }

/* Anyone who has asked for less movement gets the plain version. */
@media (prefers-reduced-motion: reduce) {
  .host-controls-body { transition: none; }
  .host-controls-caret { transition: none; }
}

.ante-control {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.action-log {
  background: var(--room-rise);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 0.9rem 1.1rem;
  /* Fixed, not a maximum. Growing to 175px as the lines arrived moved
     everything below it down the rail, line by line, during play. */
  height: 175px;
  overflow-y: auto;
  font-size: 1.05rem;
  line-height: 1.7;
  color: var(--muted);
}

.action-log div:last-child { color: var(--card-stock); }

/* A table-wide announcement -- a change of host, a change of stakes.
   Yellow is what signals on this table; red on green is unreadable. */
.action-log .log-notice { color: var(--signal); }
.action-log div:last-child.log-notice { color: var(--signal); }


.ready-control { display: flex; flex-direction: column; gap: 0.7rem; align-items: center; }

/* Already said your piece — the button stops competing for attention
   while the table waits on everyone else. */
.ready-control .is-ready { opacity: 0.62; }

.ready-note {
  color: var(--signal);
  font-size: 1.05rem;
  line-height: 1.5;
  text-align: center;
  margin: 0;
}

.minor-button { font-size: 0.95rem; padding: 0.5rem 0.9rem; }

/* A checkbox in a column of number fields. Large enough to hit on a
   phone, and the whole row is the label so the text is a tap target too. */
.check-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  cursor: pointer;
  font-size: 1.05rem;
}
.check-row input { width: 1.35rem; height: 1.35rem; accent-color: var(--signal); }

/* A busted player's hole card, turned face up. Set slightly apart so it
   reads as "this was the hidden one", not as another up card. */
.revealed-hole { display: inline-block; margin-left: 0.5rem; opacity: 0.9; }
.revealed-hole .card { outline: 2px dashed var(--signal); outline-offset: 2px; border-radius: 6px; }

/* On a laptop the table is tall enough that anything below the cards is
   off-screen — you'd reach your turn and see no way to act. So on wide
   screens the action lives in a column down the left of the baize,
   level with the top of the table and always in view.
   The width is reserved whether or not it's your turn: collapsing it
   would shift the whole table sideways every time the action moved,
   which is worse than the space it costs. Cards are never shrunk to
   make room — they're the one thing that has to stay large. */
.action-column { width: 100%; }

@media (min-width: 1000px) {
  /* The status line and the track run the full width at the top: they are
     the two things you read before deciding anything.
     Then YOUR hand with YOUR buttons beside it, under the rail, because
     that is what you act on -- it used to sit below four other players'
     seat cards and off the bottom of the screen.
     Then everyone else, full width, where a wide row of seats belongs.
     The betting column no longer runs the height of the baize; it is as
     tall as the hand it belongs to and stops. */
  /* No grid. Your hand, the deal button and the other seats stay a plain
     stack, and the controls column is lifted out of that stack into a
     gutter beside it.

     That is the whole trick, and it took three goes to find. While the
     column was a grid cell it SIZED the row it was in, so during betting
     -- when the chips and three buttons appear -- it grew taller than
     your hand and pushed everyone else's cards down the page. Spanning it
     across more rows does not help: a spanning item still grows the rows
     it covers. Out of the flow entirely, its height stops mattering to
     anybody, and the seat cards sit in the same place in every phase.

     The stack keeps its DOM order, so the narrow layout below still reads
     hand, then your buttons, then everyone else, with no reordering. */
  .table-main {
    position: relative;
    padding-left: 15rem;
  }

  .action-column {
    position: absolute;
    left: 0;
    top: 0;
    width: 13.5rem;
    display: flex;
    flex-direction: column;
  }

  .action-column .controls {
    flex-direction: column;
    align-items: stretch;
    gap: 0.7rem;
  }
  /* Actions stack full width, but NOT the chips — a chip is round, and
     stretching one into an oval stops it reading as a chip at all. */
  .action-column .controls button:not(.chip-btn) { width: 100%; }
  .action-column .controls .chip-buttons { flex-direction: row; flex-wrap: wrap; }

  /* The column is narrow, so the running commentary is set at reading
     size rather than the headline size it uses across the full table. */
  .action-column .controls .bet-summary,
  .action-column .controls p { font-size: 1.05rem; line-height: 1.4; margin: 0; }

  /* This used to reserve 8rem so the buttons never moved between phases.
     That was worth its space when the column ran the height of the baize
     and the commentary was the only thing above the buttons. Sitting
     beside the hand it is just a hole -- and worse, it made the column
     the tallest thing in its grid row, which pushed every other player's
     cards down the page. The buttons may shift a line between phases now;
     that costs less than the gap did. */
  /* A modest reserve, so the buttons don't hop a line when the commentary
     goes from "It's your bet." to "It's 5 to you." plus the tap hint.
     This used to be 8rem and left a hole, because everything below it was
     pushed down. Now the bank is pinned to the foot of the column, so any
     slack is absorbed between the two anchors instead of displacing
     anything -- which is what makes a reserve affordable again. */
  .action-column .action-msg {
    min-height: 3.4rem;
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    justify-content: flex-start;
  }
}

/* Stacked layout: no column, so nothing to anchor against. */
@media (max-width: 999px) {
  .action-msg { display: flex; flex-direction: column; gap: 0.35rem; align-items: center; }
}

/* The scoreboard. Numbers in monospace so they line up in a column and
   read like a score pad, which is what this is. */
.standings {
  background: var(--room-rise);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 0.9rem 1.1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.standings-rows { display: flex; flex-direction: column; gap: 0.3rem; }

.standing-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1rem;
  font-size: 1.1rem;
  padding: 0.2rem 0.4rem;
  border-radius: 5px;
}
/* Your own line is the one you look for first, so it doesn't have to be
   hunted for among the others. */
.standing-row.is-me {
  background: rgba(255, 255, 255, 0.09);
  color: var(--card-stock);
}
/* The token comes with the name here too. Three rows all reading "Steve"
   is exactly the case the tokens exist for, and the standings are where
   you look to see who is winning. */
.standing-name {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 1;
  min-width: 0;
}
.standing-name .who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* What this hand is about to do to you, sitting next to the total it is
   about to become. Quieter than the score itself — it's the working, not
   the answer. */
.standing-change {
  font-family: var(--data);
  font-size: 0.9rem;
  padding: 0.05rem 0.4rem;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.09);
}
.change-up { color: var(--card-stock); }
.change-down { color: var(--signal); }

/* One-line notes in the stakes block that read as a statement rather
   than a label and a value. */
.stakes-note { justify-content: flex-end; color: var(--muted); }

.field-hint { margin: 0.35rem 0 0; font-size: 0.85rem; color: var(--muted); }

/* The host's punt controls, on the seat they act on. Quiet by default —
   they're for the rare occasion somebody has walked off, not something
   to be reaching for. Only the host ever sees them. */
.host-tools { display: flex; gap: 0.4rem; margin-top: 0.6rem; }
.host-tool {
  flex: 1;
  font-family: var(--data);
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 0.35rem 0.3rem;
  background: transparent;
  color: var(--muted);
  border: 1px solid var(--rule);
  border-radius: 5px;
  cursor: pointer;
}
.host-tool:hover { color: var(--card-stock); border-color: var(--card-stock); }
/* Removal loses their score and can't be undone, so it doesn't look
   like the reversible one next to it. */
.host-tool.is-remove:hover { color: var(--signal); border-color: var(--signal); }
.standing-score { font-family: var(--data); font-size: 1.25rem; font-weight: 700; }
.score-up { color: var(--card-stock); }
.score-down { color: var(--signal); }
.score-level { color: var(--muted); }

/* A key for the seat colours and dots. Lives off the table with the rest
   of the furniture that isn't play. Small, because it's for the first
   game somebody plays and invisible to them after that. */
.seat-key {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.9rem;
  padding: 0 0.2rem;
  font-size: 0.85rem;
  color: var(--muted);
}
.seat-key-row { display: inline-flex; align-items: center; gap: 0.4rem; }
.key-swatch {
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, 0.35);
  flex: none;
}
.key-red { background: rgba(150, 26, 38, 0.92); }
.key-blue { background: rgba(38, 96, 128, 0.92); }
.seat-key .seat-dot { margin-left: 0; }

/* The legal bits, at the foot of the page where they belong. Quiet, and
   the same on every page so it reads as the site's footer rather than
   part of whatever game is being played above it. */
.legal {
  max-width: 62rem;
  margin: 2.5rem auto 1.5rem;
  padding: 1.2rem 1.5rem 0;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.55;
  text-align: center;
}
.legal p { margin: 0.25rem 0; }
.legal strong { color: var(--card-stock); font-weight: 600; }
.legal-copy { font-family: var(--data); opacity: 0.75; }

/* Over the room, not instead of it: the table you are waiting to get back
   into is what you want to be looking at while you wait. Yellow, because
   that is what signals here. */
.conn-lost {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  padding: 0.7rem 1rem;
  background: var(--signal);
  color: var(--ink);
  font-family: var(--data);
  font-size: 0.85rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.45);
}

.conn-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  background: var(--ink);
  flex: none;
}

/* Its own keyframe: the existing `breathe` animates a border colour, for
   the seat whose turn it is. This is a filled dot and needs opacity. */
@keyframes conn-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}

@media (prefers-reduced-motion: no-preference) {
  .conn-dot { animation: conn-pulse 1.4s ease-in-out infinite; }
}

/* ---------- the rules card ---------- */

/* Over the room, on the room's own surface rather than on baize — it is
   not part of play. Scrolls inside itself so the page behind never moves. */
.rules-card {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(10, 10, 9, 0.78);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 2rem 1.25rem;
  /* The SHEET scrolls, not this. Scrolling the overlay meant its top
     padding sat above the sticky header, so text slid through the gap. */
  overflow: hidden;
}

.rules-sheet {
  background: var(--room-rise);
  border: 1px solid var(--rule);
  border-radius: 14px;
  max-width: 46rem;
  width: 100%;
  max-height: 100%;
  overflow-y: auto;
}

.rules-head {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.2rem 1.5rem;
  border-bottom: 1px solid var(--rule);
  position: sticky;
  top: 0;
  background: var(--room-rise);
  border-radius: 14px 14px 0 0;
}

.rules-head h2 {
  font-family: var(--display);
  font-size: 1.7rem;
  font-weight: 400;
  margin: 0;
  color: var(--white);
  flex: 1;
}

.rules-head .pill-button { flex: none; }

.rules-body { padding: 0.5rem 1.5rem 1.6rem; }

.rules-body section {
  padding: 1.1rem 0;
  border-bottom: 1px solid var(--rule);
}
.rules-body section:last-child { border-bottom: 0; }

.rules-body h3 {
  font-family: var(--data);
  font-size: 0.8rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 0.5rem;
}

/* Large, because this is read across a range of eyesight and often by
   whoever is least sure what is going on. */
.rules-body p {
  margin: 0 0 0.6rem;
  font-size: 1.1rem;
  line-height: 1.6;
  color: var(--card-stock);
}
.rules-body p:last-child { margin-bottom: 0; }
.rules-body strong { color: var(--white); font-weight: 700; }

.rules-aside { color: var(--muted) !important; font-size: 1rem !important; }

/* The two sections people get wrong. Marked with the signal, not with red
   text — red on anything here is a legibility problem, and yellow is what
   this table uses to mean "read this". */
.rules-warn, .rules-key {
  border-left: 4px solid var(--signal);
  padding-left: 1.1rem;
}
.rules-warn h3, .rules-key h3 { color: var(--signal); }

.rules-open { align-self: center; margin-top: 0.5rem; }

@media (max-width: 620px) {
  .rules-card { padding: 0.75rem; }
  .rules-body p { font-size: 1.05rem; }
}

.rail-buttons { display: flex; flex-direction: column; gap: 0.6rem; }

.pill-button {
  background: transparent;
  border: 1px solid var(--rule);
  color: var(--muted);
  font-family: var(--data);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.6rem 0.9rem;
}

.pill-button:hover { background: transparent; color: var(--white); border-color: var(--white); }

/* A button the table is actually being held up on. Yellow is what signals
   on this table -- the same yellow that marks an announcement in the feed
   -- and it drops back to an ordinary button the moment it has been
   pressed, so it reads as "your move" rather than "this is the important
   button". Dark ink on yellow, because the point is to be read. */
.pill-button.is-signal {
  background: var(--signal);
  border-color: var(--signal);
  color: var(--ink);
  font-weight: 700;
}

.pill-button.is-signal:hover {
  background: var(--white);
  border-color: var(--white);
  color: var(--ink);
}

@media (max-width: 620px) {
  body { font-size: 17px; }
  .topbar h1 { font-size: 2.4rem; }
  .table-surface { border-width: 10px; padding: 1rem 0.9rem 1.5rem; }
  .card { width: 108px; height: 152px; }
  .pip-rank { font: 400 56px var(--display); }
  .opponent { min-width: 150px; flex: 1 1 150px; }
}
