/* Guandan client styles (UI_SPEC). One marked section per task — only edit
   your own section (UI_TASKS.md ground rules). Phone breakpoint: 700px.
   The game screen's look (felt table, card faces) is TABLE_LOOK_SPEC; its
   reference design is docs/mockups/table_mockup.html. */

/* === base (U0) === */

:root {
  --bg: #f1ece1;          /* beige page */
  --surface: #ffffff;
  --cream: #fffdf8;       /* header, plates, hand / tribute / result panels */
  --line: #e0d8c6;        /* their light border */
  --btn-border: #cfc8b8;
  --text: #1f1f1f;
  --muted: #6b6b6b;
  --border: #d4d0c6;
  --accent: #2f6fde;
  --accent-text: #ffffff;
  --red: #c62828;         /* ♥ ♦, Big Joker */
  --black: #1f1f1f;       /* ♠ ♣, Small Joker */
  --gold: #d4a017;        /* level-card border, wildcard dot */
  --highlight: #fff4c2;
  --danger: #c62828;
  --ok: #2e7d32;
  --select: #5e9c78;      /* selected hand cards: deeper felt green border */
  --turn: #ffd54f;        /* TURN badge + plate glow */
  --team-a: #7b4bc4;      /* North-South: purple, distinct from the button/"you" blue */
  --team-a-ink: #6a3aab;  /* its text: darker purple, readable */
  --team-b: #d9822b;      /* East-West: orange */
  --team-b-ink: #b0661c;  /* its text: darker orange, readable */
  --felt: #bfe0c8;
  --felt-light: #d6eedb;
  --felt-ink: #24533a;    /* text on the felt */
  --rim: #a8845c;         /* the felt's thin light-walnut border */
  --tap: 40px;            /* minimum tap target (buttons) */
  --radius: 6px;
  --gap: 8px;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial,
    "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  font-size: 16px;
  line-height: 1.35;
}

#app {
  max-width: 1200px;
  margin: 0 auto;
  padding: 8px 12px 10px;
}

button {
  min-height: var(--tap);
  min-width: var(--tap);
  padding: 6px 14px;
  font: inherit;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
}
button:hover:not(:disabled) { border-color: var(--accent); }
button:disabled { opacity: 0.45; cursor: default; }
button.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
}

input[type="text"] {
  min-height: var(--tap);
  padding: 6px 10px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.muted { color: var(--muted); }
.waiting { padding: 24px; text-align: center; color: var(--muted); }

/* === overlays (U0: connection banner, toast, modal, debug toggle) === */

.conn-banner {
  position: sticky;
  top: 0;
  z-index: 30;
  margin: -8px -12px 8px;
  padding: 8px 12px;
  text-align: center;
  background: #fff3cd;
  border-bottom: 1px solid #e0c56e;
  font-weight: 600;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 88px;
  transform: translateX(-50%);
  z-index: 58; /* above the debug panel */
  max-width: calc(100vw - 32px);
  padding: 10px 16px;
  background: var(--danger);
  color: #fff;
  border-radius: var(--radius);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
}

.overlay {
  position: fixed;
  inset: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
  background: rgba(0, 0, 0, 0.45);
}
.overlay-box {
  max-width: 480px;
  width: 100%;
  max-height: calc(100vh - 32px);
  overflow: auto;
  padding: 16px;
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}

.debug-toggle {
  position: fixed;
  top: 8px;
  right: 8px;
  z-index: 60;
  padding: 0;
  font-size: 20px;
  opacity: 0.7;
}
.debug-toggle:hover { opacity: 1; }

/* Language toggle (LANGUAGE_SPEC §2): the "EN | 中文" pill, fixed top-left
   like ≡ on the right, above the banner and overlays. Each half is a
   transparent 40px button (the tap target); the pill is drawn by the
   .lang-seg spans inside, smaller. --lang-inset (in :root below) is the room
   the lobby head and the game ribbon leave for it. */
:root { --lang-inset: 96px; } /* from the viewport's left: 8px + the 85px pill + 3px */
.lang-toggle {
  position: fixed;
  top: 8px;
  left: 8px;
  z-index: 60;
  display: flex;
}
.lang-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}
.lang-seg {
  display: block;
  width: 100%;
  text-align: center;
  padding: 0 9px;
  line-height: 26px;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--btn-border);
  border-radius: 14px 0 0 14px;
}
.lang-btn + .lang-btn .lang-seg { border-left: 0; border-radius: 0 14px 14px 0; }
.lang-btn:hover .lang-seg { color: var(--text); }
.lang-btn.is-current .lang-seg { color: var(--accent-text); background: var(--accent); border-color: var(--accent); }
.lang-btn:focus-visible { outline: none; }
.lang-btn:focus-visible .lang-seg { outline: 2px solid var(--accent); outline-offset: 2px; }

/* === debug (U0) === */

.debug-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 55;
  width: min(480px, 45vw);
  overflow: auto;
  padding: 56px 12px 12px;
  background: #1e1e1e;
  color: #e6e6e6;
  font-size: 12px;
  box-shadow: -4px 0 16px rgba(0, 0, 0, 0.3);
}
.debug-panel button { color: var(--text); min-height: 32px; padding: 2px 10px; }
.debug-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.debug-fixture { margin-bottom: 8px; padding: 6px; background: #333; border-radius: var(--radius); }
.debug-error { color: #ff8a80; margin-top: 4px; }
.debug-meta { color: #aaa; margin-bottom: 4px; }
.debug-panel h4 { margin: 10px 0 4px; }
.debug-panel pre {
  margin: 0;
  padding: 6px;
  background: #111;
  border-radius: 4px;
  white-space: pre-wrap;
  word-break: break-all;
  font-size: 11px;
}
.debug-log { max-height: 40vh; overflow: auto; }

@media (min-width: 700px) {
  body.debug-open #app { margin-right: min(480px, 45vw); }
}

/* === cards (U1; faces per TABLE_LOOK_SPEC §4, §1b) === */

/* A card: white, rounded, rank + suit in the top-left corner, a big pip in
   the middle. Everything is drawn inside the card (level border, selection ring),
   so every card is the same size and nothing overlaps its neighbours. */
.chip {
  position: relative;
  display: inline-block;
  flex: none;
  width: 52px;
  height: 72px;
  min-width: 0;
  min-height: 0;
  margin: 0;
  padding: 0;
  font: inherit;
  font-weight: 700;
  line-height: 1;
  text-align: left;
  color: var(--black);
  background: #fff;
  border: 1px solid #d8d3c8;
  border-radius: 6px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.chip-red { color: var(--red); }
.chip-black { color: var(--black); }
.chip-corner {
  position: absolute;
  top: 3px;
  left: 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
  font-size: 16px;
  line-height: 1;
}
/* The suit under the rank is a touch smaller (16px rank / 14px suit). */
.chip-suit { font-size: 0.875em; }
.chip-pip {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  padding-top: 12px;
  font-size: 28px;
  line-height: 1;
}

/* Jokers: "BJ" / "SJ" in the corner and a star in the middle. */
.chip-joker .chip-corner { top: 4px; font-size: 15px; }
.chip-joker .chip-pip { font-size: 24px; }
.chip-joker-big { color: var(--red); }
.chip-joker-small { color: #222; }

/* Level cards: a gold border drawn inside the card (same width as others). */
.chip-level {
  border-color: var(--gold);
  box-shadow: inset 0 0 0 1px var(--gold), 0 2px 4px rgba(0, 0, 0, 0.25);
}
/* Wildcard (♥ level card): a gold dot in the top-right corner. */
.chip-wild-dot {
  position: absolute;
  top: 3px;
  right: 3px;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 1.5px #fff;
}

button.chip { transition: transform 150ms ease-out, box-shadow 150ms ease-out, border-color 150ms ease-out; }
button.chip:hover:not(:disabled) { border-color: var(--accent); }
/* Selected (TABLE_LOOK_SPEC §1c): the card lifts, with a slightly stronger
   shadow and a thin 2px green border (1px border + 1px inset ring, so the
   corner doesn't shift). No tint, no sideways shift. --lift is smaller in a
   tightened stack (hand section) so the card above keeps its corner. */
.chip.is-selected,
button.chip.is-selected:hover:not(:disabled) {
  border-color: var(--select);
  box-shadow: inset 0 0 0 1px var(--select), 0 5px 9px rgba(0, 0, 0, 0.3);
  transform: translateY(calc(-1 * var(--lift, 6px)));
}
@media (prefers-reduced-motion: reduce) {
  button.chip { transition: none; }
}
.chip.is-disabled { opacity: 0.35; cursor: default; }

/* A row of cards. Table-size (small) rows overlap like a fanned play. */
.card-row { display: flex; flex-wrap: wrap; gap: 4px; }
.card-row-small { flex-wrap: nowrap; gap: 0; }
.card-row-small .chip + .chip { margin-left: -16px; }
.chip.chip-small { width: 50px; height: 70px; }

@media (max-width: 699px) {
  .chip { width: 44px; height: 62px; }
  .chip-corner { font-size: 14px; }
  .chip-pip { font-size: 22px; }
  .chip.chip-small { width: 38px; height: 54px; }
  .chip-small .chip-corner { font-size: 13px; }
  .chip-small .chip-pip { font-size: 19px; padding-top: 9px; }
  .chip-small.chip-joker .chip-corner { font-size: 12px; }
  .chip-small .chip-wild-dot { width: 8px; height: 8px; top: 2px; right: 2px; }
  .card-row-small .chip + .chip { margin-left: -13px; }
}

/* === home (LF2 R2: Home, "no game" / "game has ended", LOBBY_FLOW_SPEC §6.1, §6.2) === */

/* One cream card, like the lobby's: name, Create game, "or", invite code +
   Join game. */
.home { display: flex; justify-content: center; padding-top: 10vh; }
.home-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 400px;
  padding: 20px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.home-title { margin: 0 0 16px; font-size: 28px; }
.home-card > .home-title { display: flex; align-items: center; gap: 10px; }
.home-logo { flex: none; width: 40px; height: 40px; border-radius: 8px; }
.home-label { display: block; margin-bottom: 6px; font-weight: 600; }
.home-card input[type="text"] { width: 100%; min-width: 0; border-color: var(--btn-border); }
.home-card input[aria-invalid="true"] { border-color: var(--danger); }
.home-create {
  min-height: 48px;
  margin-top: 14px;
  border-radius: 10px;
  font-size: 17px;
  font-weight: 700;
}
.home-or {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 18px 0 14px;
  color: var(--muted);
  font-size: 14px;
}
.home-or::before, .home-or::after { content: ""; flex: 1; border-top: 1px solid var(--line); }
.home-join-row { display: flex; gap: var(--gap); }
.home-join-row input[type="text"] {
  flex: 1;
  font-weight: 700;
  letter-spacing: 0.12em;
}
.home-join-row input::placeholder { font-weight: 400; }
.home-join-row button { border-color: var(--btn-border); border-radius: 8px; font-weight: 600; }
.home-error { margin: 8px 0 0; color: var(--danger); font-weight: 600; }
.home-gone .home-title { font-size: 24px; overflow-wrap: anywhere; }
.home-gone p { margin: 0 0 16px; }
.home-gone button { align-self: flex-start; }

@media (max-width: 699px) {
  .home { padding-top: 52px; } /* clears the fixed ≡ toggle */
  .home-card { padding: 16px; }
}

/* === lobby (U2: name form, lobby, kicked) === */

/* Name form (§3.1) */
.name-form-wrap { display: flex; justify-content: center; padding-top: 10vh; }
.name-form {
  width: 100%;
  max-width: 400px;
  padding: 20px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}
.name-form h1 { margin: 0 0 16px; font-size: 28px; }
.name-form label { display: block; margin-bottom: 6px; font-weight: 600; }
.name-form-row { display: flex; gap: var(--gap); }
.name-form-row input { flex: 1; min-width: 0; }
.name-form input[aria-invalid="true"] { border-color: var(--danger); }
.name-error { margin: 10px 0 0; color: var(--danger); font-weight: 600; }

/* Lobby (§3.2, LOBBY_LOOK_SPEC §1): Last deal, two team sections side by
   side (stacked on phones), the Ready card, Settings, New match…. */
.lobby {
  /* Team colors (--team-a / --team-b and their -ink) are in :root. */
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: 820px;
  margin: 0 auto;
  padding-top: 8px;
}
.lobby-title { margin: 0; font-size: 22px; }

/* The title row: "Lobby" on the left, the invite line (LOBBY_FLOW_SPEC
   §6.3: "Invite: 482193 [Copy link]") on the right; stacked on phones. */
.lobby-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 16px;
}
/* LANGUAGE_SPEC §2: clear of the fixed EN | 中文 pill wherever the centred
   820px lobby comes within its 96px of the viewport's left edge (below
   820 + 2 × 96 = 1012px, or with the debug panel squeezing #app). The
   inset counts #app's padding (12px; 8px on phones, below). */
@media (max-width: 1011px) {
  .lobby-head { padding-left: calc(var(--lang-inset) - 12px); }
}
body.debug-open .lobby-head { padding-left: calc(var(--lang-inset) - 12px); }
.lobby-invite { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 10px; }
.invite-label { color: var(--muted); }
.invite-code {
  padding: 3px 10px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 8px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 18px;
  letter-spacing: 0.12em;
  user-select: all;
}
.invite-copy { border-color: var(--btn-border); border-radius: 8px; }
.invite-copy.is-copied { border-color: var(--ok); color: var(--ok); font-weight: 600; }
.invite-manual { flex: 1 1 100%; min-width: 0; font-size: 14px; }

/* Team sections: a colored top edge, "Team North-South" + "your team". */
.lobby-teams { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.lobby-team {
  min-width: 0;
  padding: 12px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-top: 5px solid var(--team-a);
  border-radius: 10px;
}
.lobby-team.team-b { border-top-color: var(--team-b); }
.lobby-team-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  font-size: 16px;
  color: var(--team-a-ink);
}
.lobby-team.team-b .lobby-team-head { color: var(--team-b-ink); }
.lobby-pill {
  padding: 2px 8px;
  border-radius: 99px;
  background: #e8f0fe;
  color: var(--accent);
  font-size: 11px;
  font-weight: 700;
}
.lobby-spots { display: flex; flex-direction: column; gap: 8px; }

/* A spot: compass label, name (+ "you"), connected / offline; a green ✓
   on the right when that player is ready. */
.lobby-spot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 58px;
  padding: 10px 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 8px;
}
.lobby-spot.is-you { border: 2px solid var(--accent); padding: 9px 11px; }
.lobby-spot.is-empty {
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  background: transparent;
  border: 2px dashed var(--btn-border);
  padding: 9px 11px;
}
.lobby-spot.is-empty button { border-color: var(--btn-border); border-radius: 8px; padding: 6px 16px; }
.lobby-spot.is-empty button:hover { color: var(--accent); }
.spot-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.spot-dir {
  color: var(--muted);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.spot-name { display: flex; align-items: center; gap: 6px; font-weight: 700; min-width: 0; }
.spot-name-text { overflow-wrap: anywhere; }
.spot-you {
  padding: 1px 7px;
  border-radius: 99px;
  background: var(--accent);
  color: var(--accent-text);
  font-size: 11px;
  font-weight: 700;
}
.spot-conn { display: flex; align-items: center; gap: 5px; color: var(--muted); font-size: 13px; }
.spot-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: #bbb; }
.spot-conn.is-on .spot-dot { background: var(--ok); }
.spot-tick { color: var(--ok); font-size: 18px; font-weight: 700; }

/* Ready card: the big toggle, who we're waiting for, the locked note. */
.lobby-ready {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 14px 16px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.ready-toggle {
  min-width: 220px;
  min-height: 48px;
  border: 2px solid var(--accent);
  border-radius: 10px;
  font-size: 17px;
  font-weight: 700;
}
.ready-toggle.is-on { background: var(--surface); border-color: var(--ok); color: var(--ok); }
.ready-toggle.is-on:hover:not(:disabled) { border-color: var(--ok); }
.lobby-waiting { margin: 0; color: var(--muted); text-align: center; }
.lobby-waiting strong { color: var(--text); }
.lobby-locked {
  margin: 0;
  padding: 6px 10px;
  border-radius: var(--radius);
  background: var(--highlight);
  font-size: 14px;
  text-align: center;
}

/* A section's header band (Last deal, Settings): a gold-tinted strip across
   the top of the card with a small uppercase title and an optional note on
   the right (docs/mockups/last_deal_mockup.html, option D). */
.sec-band {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 2px 10px;
  padding: 7px 16px;
  background: linear-gradient(#fbf4e3, #f1e5c8);
  border-bottom: 1px solid #e3d3ad;
}
.sec-band h2 { margin: 0; font-size: 13px; font-weight: 800; letter-spacing: 0.1em; text-transform: uppercase; color: #8a6512; }
.sec-band-note { color: var(--muted); font-size: 12px; }

/* Table settings (LOBBY_FLOW_SPEC §6.3): levels + declaring team. */
.lobby-settings {
  overflow: hidden;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.settings-body { padding: 10px 16px 12px; }
.settings-team {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  margin: 6px 0;
  padding-left: 8px;
  border-left: 4px solid transparent;
}
.settings-team.team-a { border-left-color: var(--team-a); }
.settings-team.team-b { border-left-color: var(--team-b); }
.settings-team-name { display: inline-block; min-width: 7.5em; }
.settings-team select {
  min-height: var(--tap);
  min-width: 64px;
  padding: 4px 8px;
  font: inherit;
  border: 1px solid var(--btn-border);
  border-radius: var(--radius);
  background: var(--surface);
}
.settings-tries { color: var(--muted); font-size: 13px; }
/* Lined up with the team rows (their 4px bar + 8px padding). */
.settings-declaring { display: flex; flex-wrap: wrap; align-items: center; gap: 0 14px; margin-top: 6px; padding-left: 12px; }
.settings-choice { display: inline-flex; align-items: center; gap: 6px; min-height: var(--tap); cursor: pointer; }
.settings-choice input { width: 18px; height: 18px; margin: 0; }

/* New match… and its inline confirm. */
.lobby-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}
.lobby-actions button { border-color: var(--btn-border); border-radius: 8px; }
.lobby-actions.is-confirm {
  padding: 8px 12px;
  background: var(--cream);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
}
.lobby-confirm-text { flex: 1 1 200px; font-weight: 600; }
.lobby-actions button.danger { background: var(--danger); border-color: var(--danger); color: #fff; }

@media (max-width: 699px) {
  .lobby-head { flex-direction: column; align-items: flex-start; padding-left: calc(var(--lang-inset) - 8px); }
  .lobby-teams { grid-template-columns: minmax(0, 1fr); }
  .ready-toggle { width: 100%; min-width: 0; }
  .settings-body { padding: 8px 12px 10px; }
  .sec-band { padding: 6px 12px; }
  .settings-declaring { gap: 0 10px; }
  .lobby-actions button { flex: 1; white-space: nowrap; }
  /* The confirm's question gets its own row; its buttons share the next. */
  .lobby-confirm-text { flex-basis: 100%; }
}

/* Kicked overlay content (§3.8); main.js supplies the backdrop + box. */
.kicked h2 { margin: 0 0 8px; }
.kicked p { margin: 0 0 8px; }
.kicked-actions { display: flex; justify-content: flex-end; margin-top: 12px; }


/* === table (U3; felt table per TABLE_LOOK_SPEC §2, §3, §1b) === */

.game { font-size: 15px; }
/* Buttons on the game screen: white with a warm border (mockup). */
.game button { border-color: var(--btn-border); border-radius: 8px; }
.game button.primary { border-color: var(--accent); }
.game button:hover:not(:disabled) { border-color: var(--accent); }

/* Header (GAME_PAGE_V3_SPEC §5, §5b): a full-width ribbon across the top
   with your team on the left, the deal level in a pennant in the middle and
   the other team on the right. A 3-column grid with equal outer columns
   keeps the pennant over the middle of the table. The pennant hangs above
   and below the band: the header's padding keeps it clear of the page top,
   and its bottom padding leaves a clear gap (10px) between the pennant's
   tip and the partner's plate, so it doesn't point at them. Last trick /
   Redeal are off the ribbon, stacked down the right side below it. */
.table-header {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  /* LANGUAGE_SPEC §2: the ribbon starts right of the fixed EN | 中文 pill
     (and ends clear of ≡). The inset is the same on both sides, so the
     pennant stays centred on the table; #app's 12px padding counts. */
  --hdr-inset: calc(var(--lang-inset) - 12px);
  /* Room at the right end for Redeal (inline with the ribbon); the same on
     the left, so the pennant stays centred on the table. */
  --hdr-redeal: 92px;
  padding: 4px calc(var(--hdr-inset) + var(--hdr-redeal)) 22px;
}
.table-header .short { display: none; }
/* The band: each side of the pennant draws its half. */
.rb-side {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-width: 0;
  min-height: 38px;
  padding: 2px 12px 2px 14px;
  background: linear-gradient(#fffaf0, #f2e6c9);
  border: 1px solid #d3bf92;
  border-width: 1px 0;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.08);
}
.rb-theirs { padding: 2px 14px 2px 12px; }
/* Finished ends: the band's outer ends get a border and rounded caps. */
.rb-mine { border-left-width: 1px; border-radius: 8px 0 0 8px; }
.rb-theirs { border-right-width: 1px; border-radius: 0 8px 8px 0; }
.rb-name { min-width: 0; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.team-a .rb-name { color: var(--team-a-ink); }
.team-b .rb-name { color: var(--team-b-ink); }
.rb-you { margin-left: 6px; color: var(--muted); font-size: 13px; font-weight: 600; }
/* （我方）'s full-width bracket brings its own space. */
:lang(zh) .rb-you { margin-left: 0; }
.rb-box-wrap { display: flex; flex-direction: column; align-items: center; flex: none; }
.rb-box {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  min-width: 32px;
  height: 28px;
  padding: 0 6px;
  font-size: 17px;
  font-weight: 800;
  background: #fff;
  border: 2px solid var(--team-a);
  border-radius: 6px;
}
.team-b .rb-box { border-color: var(--team-b); }
/* The declaring team (the deal level is its level): filled, with a star. */
.rb-box.is-declaring { color: #fff; background: var(--team-a); }
.team-b .rb-box.is-declaring { background: var(--team-b); }
.rb-star { font-size: 12px; color: #ffe27a; }
.rb-tries { font-size: 10px; font-weight: 700; line-height: 11px; color: var(--muted); white-space: nowrap; }

/* The pennant: a banner with a pointed bottom, the deal level big in it. */
.pennant {
  position: relative;
  z-index: 1;
  width: 62px;
  height: 54px;
  margin: -4px -2px -12px;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.3));
}
.pennant-edge, .pennant-face { clip-path: polygon(0 0, 100% 0, 100% 74%, 50% 100%, 0 74%); }
.pennant-edge { height: 100%; padding: 2px 2px 3px; background: var(--gold); }
.pennant-face {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-top: 3px;
  color: #fff;
  background: linear-gradient(#2f6a4a, var(--felt-ink));
}
.pennant-label { font-size: 11px; font-weight: 600; line-height: 12px; letter-spacing: 0.04em; color: var(--felt-light); }
.pennant-level { font-size: 26px; font-weight: 800; line-height: 28px; }
/* Redeal: inline with the ribbon, at its right end (in the header's right
   padding, clear of ≡), vertically centred on the band. */
.hdr-actions {
  position: absolute;
  top: 9px;
  right: var(--hdr-inset);
  z-index: 2;
  display: flex;
  width: calc(var(--hdr-redeal) - 8px);
}
.hdr-actions .reset-deal-btn { flex: 1; }
.reset-deal-btn { min-width: 0; min-height: 30px; padding: 2px 10px; font-size: 13px; color: var(--muted); }
.reset-deal-btn[aria-expanded="true"] { background: var(--highlight); border-color: var(--gold); color: var(--text); }
.reset-confirm {
  /* A small box floating beside (left of) the Redeal button, over the page:
     it takes no layout space, so opening it never makes the page scroll. */
  position: absolute;
  top: 46px;
  right: var(--hdr-inset);
  z-index: 5;
  width: 290px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 12px;
  padding: 10px 12px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.18);
}
.reset-confirm .reset-confirm-text { flex-basis: 100%; }
.reset-confirm-text { flex: 1 1 16em; min-width: 0; }
.reset-confirm-buttons { display: flex; gap: 8px; }
.reset-confirm-buttons button { min-height: 34px; padding: 4px 14px; }

/* The table: partner's plate on top, opponents hugging the felt's sides,
   your plate under it. */
.table {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 6px 12px;
  align-items: center;
}

.plate {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  min-width: 0;
  padding: 5px 12px;
  text-align: center;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
.plate.is-turn { box-shadow: 0 0 0 3px var(--turn), 0 0 18px 4px rgba(255, 213, 79, 0.55); }
.plate.is-offline .plate-name { color: var(--muted); }
.plate-name { font-weight: 700; overflow-wrap: anywhere; }
.plate-role { color: var(--muted); font-size: 13px; font-weight: 600; }
.plate-badges { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.badge {
  padding: 2px 7px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.3;
  border-radius: 99px;
  background: #e6e1d6;
  white-space: nowrap;
}
.badge-count { background: #2b2b2b; color: #fff; font-variant-numeric: tabular-nums; }
.badge-place { background: var(--ok); color: #fff; }
.badge-turn { background: var(--turn); color: #3a2b00; }
.badge-off { background: var(--red); color: #fff; }

.plate-top { grid-column: 2; grid-row: 1; justify-self: center; flex-direction: row; gap: 10px; }
.plate-left { grid-column: 1; grid-row: 2; justify-self: end; min-width: 110px; }
.plate-right { grid-column: 3; grid-row: 2; justify-self: start; min-width: 110px; }
.plate-you { grid-column: 2; grid-row: 3; justify-self: center; flex-direction: row; gap: 10px; }

/* The felt: a wide pastel green rectangle with a thin light-walnut border
   (TABLE_LOOK_SPEC §1c). Sized width-first (about half the window,
   480–640px) so it never gets cramped; the height scales with the window so
   the whole screen fits without scrolling. On short screens that makes it
   wider than 16:10 (~2:1), on tall ones close to 16:10. `overflow: hidden`
   keeps piles and slide-ins inside the felt's inner edge. */
.felt {
  grid-column: 2;
  grid-row: 2;
  position: relative;
  width: clamp(480px, 48vw, 640px);
  height: clamp(230px, 38vh, 340px);
  overflow: hidden;
  padding: 8px 10px;
  border: 5px solid var(--rim);
  border-radius: 12px;
  background: radial-gradient(ellipse at 50% 45%, var(--felt-light), var(--felt) 75%);
  box-shadow: inset 0 0 18px rgba(36, 83, 58, 0.18), 0 4px 12px rgba(0, 0, 0, 0.15);
  isolation: isolate;
  display: grid;
  grid-template-areas:
    "top  top    top"
    "left center right"
    "bottom bottom bottom";
  /* The middle column is only as wide as the center text: mid-trick it's
     empty, so the left / right zones get half the felt each. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto minmax(0, 1fr);
  gap: 4px;
}

/* A play zone, just inside the felt's edge on its seat's side: the seat's
   pile for the trick (§1c), passes included as "PASS" cards (§5b.3).
   --dx/--dy: where a new play slides in from; --ox/--oy: which way the
   earlier plays in the pile are offset (toward the seat's edge). */
.zone {
  --cw: 50px;             /* table card width (phone: 38px) */
  --offw: 0px;            /* room kept for the pile's sideways offset */
  display: flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 0;
  container-type: inline-size;
}
/* Top / bottom piles sit toward the felt's middle, so earlier plays
   (offset toward the seat's edge) use the zone's free space, not the rim. */
.zone-top { grid-area: top; align-items: flex-end; --dx: 0px; --dy: -18px; --ox: 0px; --oy: -10px; }
.zone-left { grid-area: left; --dx: -18px; --dy: 0px; --ox: -10px; --oy: 0px; --offw: 20px; }
.zone-right { grid-area: right; --dx: 18px; --dy: 0px; --ox: 10px; --oy: 0px; --offw: 20px; }
.zone-bottom { grid-area: bottom; align-items: flex-start; --dx: 0px; --dy: 18px; --ox: 0px; --oy: 10px; }
/* A long play stays on one row: its cards overlap more to fit the zone. */
.zone .card-row-small .chip + .chip {
  margin-left: clamp(-36px,
    calc((100cqw - var(--offw) - var(--cw)) / max(1, var(--n, 1) - 1) - var(--cw)),
    -14px);
}
.zone-entry {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  max-width: 100%;
}
/* The pile: every entry (play or pass) in the same grid cell, the newest
   last (on top). Earlier ones sit underneath, offset toward the seat's edge
   and faded. */
.pile {
  display: grid;
  justify-items: center;
  align-items: center;
  max-width: 100%;
  min-width: 0;
}
.pile-play {
  grid-area: 1 / 1;
  transform: translate(calc(var(--ox, 0px) * var(--depth, 0)), calc(var(--oy, 0px) * var(--depth, 0)));
}
.pile-play.is-under { opacity: 0.45; }
/* A pass: a table-size card, muted so it reads as "no play": a cream-grey
   face, "PASS" in the middle, a dashed inner border, a softer shadow. */
.chip.chip-pass {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-align: center;
  color: #8b8575;
  background: #ece6d8;
  border-color: #cfc7b4;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18);
}
.chip.chip-pass::before {
  content: "";
  position: absolute;
  inset: 4px;
  border: 1.5px dashed rgba(139, 133, 117, 0.45);
  border-radius: 4px;
}
/* The current best play glows (only plays are ever "best"). */
.pile-play.is-best .card-row { filter: drop-shadow(0 0 6px rgba(230, 170, 0, 0.8)); }
/* The last trick (toggle): dimmed; its one label is in the center. */
.zone.is-dim .pile { opacity: 0.5; }
.zone-tag {
  flex: none;
  padding: 0 6px;
  font-size: 10px;
  font-weight: 700;
  line-height: 14px;
  color: #fff;
  background: var(--felt-ink);
  border-radius: 99px;
  white-space: nowrap;
}

/* Slide-in (§5): a new play or PASS card slides in from its seat's side and
   fades in. Trick end (§1c): the finished trick's piles fade out. */
.zone-in { animation: zone-in 200ms ease-out both; }
@keyframes zone-in {
  from { opacity: 0; transform: translate(var(--dx, 0px), var(--dy, 0px)); }
  to { opacity: 1; transform: none; }
}
.zone-out { animation: zone-out 300ms ease-in both; }
@keyframes zone-out {
  from { opacity: 1; }
  to { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .zone-in, .zone-out { animation: none; }
}

/* Center: text only when it's relevant. It gets the whole middle row unless
   a side zone has something in it. */
.center {
  grid-row: 2;
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
  min-width: 0;
  text-align: center;
  color: var(--felt-ink);
}
.felt.has-side .center { grid-column: 2; max-width: 150px; }
.center-lead { font-size: 18px; font-weight: 700; }
.center-jiefeng {
  padding: 3px 10px;
  font-size: 13px;
  background: rgba(36, 83, 58, 0.12);
  border-radius: 99px;
}

/* Turn order (GAME_PAGE_V3_SPEC §4): faint curved arrows in the felt's
   corners, counterclockwise (you → right → partner → left). table.js draws
   the bottom-right one; the others are the same arrow turned. Behind the
   piles (z-index -1 in the felt's stacking context), no layout space. */
.turn-arrow {
  position: absolute;
  z-index: -1;
  width: 26px;
  height: 26px;
  fill: none;
  stroke: rgba(36, 83, 58, 0.3);
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  pointer-events: none;
}
.turn-arrow-br { right: 4px; bottom: 4px; }
.turn-arrow-tr { right: 4px; top: 4px; transform: rotate(-90deg); }
.turn-arrow-tl { left: 4px; top: 4px; transform: rotate(180deg); }
.turn-arrow-bl { left: 4px; bottom: 4px; transform: rotate(90deg); }

/* The "next" chevron on the plate of whoever acts after the current turn:
   faint, in the plate's corner, out of the layout. */
.plate { position: relative; }
.plate-next {
  position: absolute;
  top: 50%;
  left: 3px;
  transform: translateY(-55%);
  font-size: 18px;
  font-weight: 800;
  line-height: 1;
  color: rgba(36, 83, 58, 0.45);
  pointer-events: none;
}

/* A game notice (take back, returns) at the top of the felt, over the
   zones for a few seconds; clicks go through it. */
.felt-notice {
  position: absolute;
  top: 6px;
  left: 50%;
  z-index: 2;
  transform: translateX(-50%);
  width: max-content;
  max-width: calc(100% - 24px);
  padding: 4px 14px;
  font-size: 14px;
  font-weight: 600;
  text-align: center;
  color: var(--felt-ink);
  background: rgba(255, 253, 248, 0.95);
  border: 1px solid rgba(36, 83, 58, 0.35);
  border-radius: 99px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  pointer-events: none;
  animation: notice-in 200ms ease-out both;
}
@keyframes notice-in {
  from { opacity: 0; transform: translate(-50%, -6px); }
  to { opacity: 1; transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
  .felt-notice { animation: none; }
}
.center-phase { font-size: 16px; font-weight: 700; opacity: 0.85; }

@media (max-width: 699px) {
  .game { font-size: 14px; }
  /* At least as tall as the fixed ≡ button, so it doesn't hang over the
     plates below. */
  /* The ribbon sits between the fixed EN | 中文 pill and ≡ (#app's padding
     is 8px here). Too narrow for the same inset on both sides: the short
     team names wouldn't fit, so the right side only clears ≡ and the
     pennant sits ~22px left of the table's centre. Last trick / Redeal go
     on the row below, on the right. */
  .table-header { --hdr-inset: calc(var(--lang-inset) - 8px); padding: 6px 44px 4px var(--hdr-inset); font-size: 13px; }
  .table-header .long { display: none; }
  .table-header .short { display: inline; }
  .rb-side { gap: 4px; min-height: 36px; padding: 2px 6px; }
  .rb-theirs { padding: 2px 6px; }
  .rb-you { margin-left: 4px; font-size: 12px; }
  .rb-box { min-width: 28px; height: 24px; padding: 0 4px; font-size: 15px; }
  .rb-tries { font-size: 9px; line-height: 10px; }
  .pennant { width: 48px; height: 54px; margin: -6px -2px -14px; }
  .pennant-label { font-size: 9px; line-height: 10px; }
  .pennant-level { font-size: 21px; line-height: 24px; }
  .hdr-actions {
    position: static;
    grid-column: 1 / -1;
    justify-self: end;
    width: auto;
    margin: 16px -44px 0 0;
  }
  .reset-deal-btn { min-height: 32px; padding: 2px 8px; font-size: 14px; }
  .reset-confirm {
    /* Phone: a row under the header, as before (scrolling is fine here). */
    position: static;
    grid-column: 1 / -1;
    width: auto;
    margin: 4px -44px 0 calc(-1 * var(--hdr-inset));
    padding: 6px 0 4px;
    background: none;
    border: none;
    border-top: 1px solid var(--line);
    border-radius: 0;
    box-shadow: none;
  }
  .reset-confirm-buttons { flex: 1; }
  .reset-confirm-buttons button { flex: 1; white-space: nowrap; }
  /* The question gets its own row, so the buttons are never squeezed
     (a short Chinese question would otherwise sit beside them). */
  .reset-confirm-text { flex-basis: 100%; }

  /* Opponents' plates in a row above the felt: left, partner, right (§3). */
  .table { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 6px; }
  .plate { padding: 5px 6px; }
  .plate-name { font-size: 13px; }
  .plate-left, .plate-top, .plate-right {
    grid-row: 1;
    justify-self: stretch;
    min-width: 0;
    flex-direction: column;
    gap: 3px;
  }
  .plate-left { grid-column: 1; }
  .plate-top { grid-column: 2; }
  .plate-right { grid-column: 3; }
  .plate-you { grid-column: 1 / -1; grid-row: 3; }

  /* Zones stacked: partner, left / right side by side, center, you. */
  .felt {
    grid-column: 1 / -1;
    grid-row: 2;
    width: auto;
    height: auto;
    min-height: 150px;
    padding: 10px;
    border-width: 4px;
    grid-template-areas: "top top" "left right" "center center" "bottom bottom";
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: auto;
    gap: 8px;
  }
  .felt .center, .felt.has-side .center { grid-column: 1 / -1; grid-row: 3; max-width: none; }
  .zone { --cw: 38px; }
  .turn-arrow { width: 20px; height: 20px; }
  .turn-arrow-br, .turn-arrow-tr { right: 2px; }
  .turn-arrow-tl, .turn-arrow-bl { left: 2px; }
  .turn-arrow-tr, .turn-arrow-tl { top: 2px; }
  .turn-arrow-br, .turn-arrow-bl { bottom: 2px; }
  .felt-notice { font-size: 13px; padding: 3px 10px; }
  .center-lead { font-size: 16px; }
}

/* === play (U4: hand panel, action bar, reading picker) === */

.play-controls {
  position: relative;
  margin-top: 6px;
  padding: 8px 12px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* Take back / Last trick: top right of the hand box, on the toolbar's line
   (they're about the trick, not the deal). */
.trick-actions {
  position: absolute;
  top: 8px;
  right: 12px;
  display: flex;
  gap: 8px;
}
.trick-actions button { min-width: 0; min-height: 34px; padding: 4px 14px; font-size: 15px; }
/* You're out: no hand and no toolbar, so they'd cover the action bar's
   buttons. Their own right-aligned row above the bar instead. */
.trick-actions.is-own-row { position: static; justify-content: flex-end; }
@media (max-width: 699px) {
  /* Phone: their own right-aligned row above the toolbar (the toolbar's
     three buttons already fill the line). */
  .trick-actions { position: static; justify-content: flex-end; margin-bottom: 6px; }
  .trick-actions button { min-height: 32px; padding: 2px 10px; font-size: 14px; }
}
.last-trick-toggle.is-on { background: var(--highlight); border-color: var(--gold); }

.play-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--gap);
  margin-top: 8px;
}
.play-status { font-weight: 600; }
.play-status.is-turn { color: var(--accent); font-weight: 700; }
.play-picked { font-size: 14px; }
.play-buttons { display: flex; gap: var(--gap); margin-left: auto; }
.play-buttons button { min-width: 72px; }

/* Reading picker content (§3.5); main.js supplies the backdrop + box. */
.reading-picker h2 { margin: 0 0 12px; font-size: 20px; }
.reading-options { display: flex; flex-direction: column; gap: var(--gap); }
.reading-option { width: 100%; text-align: left; font-weight: 600; }
.reading-actions { display: flex; justify-content: flex-end; margin-top: 12px; }

/* Phone: the action bar is fixed to the bottom of the screen so Play/Pass
   are always reachable while scrolling the table; the panel reserves room. */
@media (max-width: 699px) {
  .play-controls { padding: 8px 8px 72px; }
  .play-actions {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 20; /* below overlays (40), debug panel (55), toast (58) */
    margin: 0;
    padding: 8px 10px calc(8px + env(safe-area-inset-bottom, 0px));
    background: var(--cream);
    border-top: 1px solid var(--line);
    box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.2);
    flex-wrap: nowrap;
  }
  .play-status {
    flex: 1;
    min-width: 0;
    font-size: 14px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .play-picked { display: none; }
  .play-buttons button { min-width: 60px; padding: 6px 10px; }
}

/* === tribute (U5) === */

/* Computer: what's going on + your task on the left, your hand on the right
   (so the hand gets the height). Phone: one column. */
.tribute-panel {
  display: grid;
  grid-template-columns: minmax(200px, 250px) minmax(0, 1fr);
  gap: 8px 16px;
  align-items: start;
  margin-top: 6px;
  padding: 8px 12px;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.tribute-title { margin: 0 0 4px; font-size: 16px; }
.tribute-duties { list-style: none; margin: 0 0 6px; padding: 0; font-size: 14px; }
.tribute-duty {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px 10px;
  padding: 3px 0;
  border-bottom: 1px solid var(--line);
}
.tribute-duty:last-child { border-bottom: 0; }
.tribute-pair { font-weight: 600; }
.tribute-name.is-you { color: var(--accent); }
.tribute-paid { display: inline-flex; align-items: center; gap: 6px; }
/* The paid card, small enough for a text line. */
.tribute-paid .chip.chip-small { width: 28px; height: 38px; }
.tribute-paid .chip-small .chip-corner { font-size: 11px; top: 2px; left: 3px; }
.tribute-paid .chip-small .chip-pip { font-size: 14px; padding-top: 12px; }
.tribute-task { margin: 4px 0 0; font-size: 14px; }
.tribute-task.is-yours {
  padding: 6px 10px;
  background: var(--highlight);
  border-radius: var(--radius);
  font-weight: 600;
}
.tribute-actions { display: flex; margin-top: 8px; }
.tribute-hand { min-width: 0; }

@media (max-width: 699px) {
  .tribute-panel { grid-template-columns: minmax(0, 1fr); padding: 8px; }
  .tribute-actions button { flex: 1; }
}

/* === results: the lobby's Last deal card (LOBBY_FLOW_SPEC §6.3, redesigned
   per docs/mockups/last_deal_mockup.html option D): header band, the final
   play on a patch of felt (left), the winners' strip and one score tile per
   team (right). === */

.results-panel {
  overflow: hidden;
  background: var(--cream);
  border: 1px solid var(--line);
  border-radius: 10px;
}
.results-panel.is-match-over { border: 2px solid var(--gold); }
.results-panel.is-match-over .sec-band { background: linear-gradient(#fff3c4, #f6df8a); border-bottom-color: var(--gold); }
.results-body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 12px 16px;
  padding: 12px 16px 14px;
}
.results-body.no-felt { grid-template-columns: minmax(0, 1fr); }

.results-felt {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-width: 150px;
  padding: 8px 16px;
  background: radial-gradient(var(--felt-light), var(--felt));
  border: 3px solid var(--rim);
  border-radius: 12px;
}
.results-who { color: var(--felt-ink); font-size: 13px; font-weight: 600; }
.results-felt .chip.chip-small { width: 38px; height: 54px; }
.results-felt .chip-small .chip-corner { font-size: 13px; top: 2px; left: 4px; }
.results-felt .chip-small .chip-pip { font-size: 19px; padding-top: 12px; }
.results-felt .card-row-small .chip + .chip { margin-left: -12px; }

/* Team colours for the strip and tiles. */
.results-strip.team-a, .results-tile.team-a { --t: var(--team-a); --t-ink: var(--team-a-ink); --t-tint: #f3edfb; }
.results-strip.team-b, .results-tile.team-b { --t: var(--team-b); --t-ink: var(--team-b-ink); --t-tint: #fdf1e4; }

.results-right { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.results-strip { padding: 7px 12px; background: var(--t-tint); border-left: 5px solid var(--t); border-radius: 8px; }
.results-headline { font-size: 18px; line-height: 1.25; }
.results-win { font-weight: 800; color: var(--t-ink); }
.results-finish {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 700;
  color: #fff;
  background: var(--t);
  vertical-align: 2px;
}
.results-places { color: var(--muted); font-size: 13px; }

.results-board { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.results-tile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-width: 0;
  padding: 6px 12px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-left: 4px solid var(--t);
  border-radius: 8px;
}
.results-tile-name { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; font-weight: 700; color: var(--t-ink); }
.results-tile-you { color: var(--muted); font-size: 12px; font-weight: 600; }
.results-score { display: flex; flex-direction: column; align-items: flex-end; line-height: 1.15; }
.results-levels { display: flex; align-items: center; gap: 6px; font-size: 20px; font-weight: 500; white-space: nowrap; }
.results-arrow { color: var(--muted); font-size: 16px; }
.results-attempts { color: var(--muted); font-size: 12px; font-weight: 700; white-space: nowrap; }
.results-attempts.is-bad { color: var(--danger); }

@media (max-width: 699px) {
  .results-body { grid-template-columns: minmax(0, 1fr); padding: 10px 12px 12px; }
  .results-tile { padding: 6px 10px; }
  .results-levels { font-size: 18px; }
}

/* === hand (H2: stacks + groups, HAND_LAYOUT_SPEC §2, §3, §5) === */

/* Toolbar: Group / Ungroup / Clear groups. */
.hand-toolbar { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 8px; }
.hand-toolbar button { min-width: 0; min-height: 34px; padding: 4px 14px; font-size: 15px; }

/* Columns (stacks and groups), strongest on the left, an even 10px apart.
   They wrap onto more rows when they don't fit (phones) instead of
   overflowing. */
.hand-cols {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 10px;
}

/* A stack: cards overlap vertically, each showing a --strip strip with its
   corner; the last (bottom) card is fully visible. --n (set by hand.js) is
   the column's card count: when the column wouldn't fit in --col-room, its
   strip tightens (down to 14px, normally 32px) rather than making the page
   scroll. */
.hand {
  --card-h: 72px;
  --col-room: 190px;
}
/* The room left for the tallest column (a group's padding included) once
   everything else on the screen is laid out: header, plates, the felt (same
   clamp() as .felt), toolbar, action bar. Measured at 1280×720 (+10px
   since the header keeps a gap under the pennant, GAME_PAGE_V3_SPEC §5b.2). */
.play-controls .hand { --col-room: max(120px, calc(100vh - clamp(230px, 38vh, 340px) - 290px)); }
.tribute-panel .hand { --col-room: max(120px, calc(100vh - clamp(230px, 38vh, 340px) - 230px)); }
/* Beside the task column there's less width: a full 15-column hand fits on
   one row at 1280px only with tighter gaps. */
.tribute-panel .hand-cols { column-gap: 5px; }
.hand-stack {
  --strip: clamp(14px, calc((var(--col-room) - var(--card-h)) / max(1, var(--n, 1) - 1)), 32px);
  display: flex;
  flex-direction: column;
}
.hand-stack .chip { height: var(--card-h); }
.hand-stack .chip + .chip { margin-top: calc(var(--strip) - var(--card-h)); }
.hand-stack .chip:not(:last-child) .chip-pip { display: none; }
/* A selected card lifts into the card above it: at most 6px, and never so
   far that less than ~22px of that card's strip stays visible. */
.hand-stack .chip { --lift: clamp(2px, calc(var(--strip) - 22px), 6px); }
/* The corner (rank over suit) must stay inside the strip. It wraps: when
   the strip gets too short for two lines, the suit moves beside the rank. */
.hand-stack .chip-corner {
  flex-wrap: wrap;
  align-content: flex-start;
  column-gap: 1px;
  max-height: calc(var(--strip) + 2px);
  font-size: clamp(11px, calc(var(--strip) - 16px), 16px);
}
.hand-stack .chip-joker .chip-corner { font-size: clamp(11px, calc(var(--strip) - 17px), 15px); }

/* Disabled (tribute): no transparency, since the card underneath would show
   through the overlap; grey it out with an overlay instead. */
.hand-stack .chip.is-disabled { opacity: 1; }
.hand-stack .chip.is-disabled::after {
  content: "";
  position: absolute;
  inset: -1px;
  border-radius: inherit;
  background: rgba(150, 146, 138, 0.62);
}

/* A group: one stack-like column (top card = base) in a tinted outline,
   blue (red for a bomb). What it can be played as is in its hover tooltip
   (title), not on screen. */
.hand-group {
  padding: 5px 8px;
  background: #e3ebfa;
  border: 2px solid #8aa4d6;
  border-radius: 10px;
}
.hand-group.is-bomb { background: #fbe4e4; border-color: var(--red); }
/* Tapping the outline / background selects the whole group (GAME_PAGE_V3_SPEC
   §2), so it looks clickable. */
.hand-group.is-selectable { cursor: pointer; transition: background-color 120ms, border-color 120ms; }
.hand-group.is-selectable:hover { background: #d0def7; border-color: #4f78c6; }
.hand-group.is-bomb.is-selectable:hover { background: #f7d0d0; border-color: #a31f1f; }

@media (max-width: 699px) {
  /* A little scrolling is fine on a phone: stacks keep the full strip. */
  .hand, .play-controls .hand, .tribute-panel .hand { --card-h: 62px; --col-room: 420px; }
  .hand-cols { gap: 10px 8px; }
  .hand-group { padding: 6px; }
}

/* === phone (< 700px) — base/overlay/debug adjustments (U0); screen sections
       put their own phone rules in their own section with the same query === */

@media (max-width: 699px) {
  #app { padding: 8px; }
  /* Its text stays clear of the fixed EN | 中文 pill and ≡ (it wraps). */
  .conn-banner { margin: -8px -8px 8px; padding: 8px var(--lang-inset); }
  .toast { bottom: 96px; }
  .debug-panel {
    top: auto;
    left: 0;
    width: 100%;
    height: 60vh;
    padding-top: 12px;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.3);
  }
}
