/* ─────────────────────────────────────────────────────────────
   Suit colours are NOT defined here. The server sends them from
   config.json and app.js writes them onto :root at load, so the
   palette has exactly one source of truth.
   ───────────────────────────────────────────────────────────── */
:root {
  --porcelain: #FCF7FA;
  --blush: #F3E4EE;
  --blush-deep: #E8CFE0;
  --ink: #2E1F2B;
  --ink-soft: #7A6472;
  --line: #E3D5DE;
  /* One step firmer than --line, for the boxes that sit on the blush room (feed, coach bubble). */
  --line-firm: #D3BCCB;
  --feed-bg: rgba(255, 255, 255, .45);

  --gold: #D8A81C;
  --gold-deep: #8A5D00;
  --pop: #FF2D87;
  /* "Your move": a go colour, not the pop pink, which reads as stop. */
  --go: #17804F; --go-ink: #FFFFFF; --go-glow: 23, 128, 79; --go-lead: #116B40;

  /* Salvage: the round is already lost, play for points. Blue because every other
     accent on this table is spoken for - gold wins, violet is wild, pink is on
     turn, crimson resigns, and all five suits sit in the warm half of the wheel. */
  --salvage: #2F5FA8;

  /* Wild and "can go out" used to share the gold ring and were indistinguishable.
     Gold now means one thing only: this discard wins the round. Wild is violet. */
  --wild-ring: #B08AD9;
  --wild-ink: #5E2596;

  /* How far apart the cards in a fan sit. app.js narrows it as the hand grows
     so a 13-card round still fits the table. */
  --fan-step: 70px;

  --card-w: 104px;
  --card-h: 146px;
  --card-r: 14px;
  --card-face: #FFFFFF;
  /* White faces on a pale blush room need a drawn edge; a shadow alone dissolves into it.
     Inset, so it never changes a card's box and sits over the wild gradients. */
  --card-edge: inset 0 0 0 1px rgba(120, 50, 95, .26);
  --card-shadow: var(--card-edge), 0 2px 4px rgba(120, 50, 95, .10), 0 8px 20px rgba(120, 50, 95, .10);

  --display: "Fredoka", "Trebuchet MS", system-ui, sans-serif;
  --body: "Outfit", system-ui, -apple-system, "Segoe UI", sans-serif;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

/* Any rule that sets `display` outranks the browser's own [hidden] handling, so
   an element with the hidden attribute stays on screen. This is how the banner
   first shipped as an empty yellow bar. Settle it once, for everything. */
[hidden] { display: none !important; }

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--porcelain);
  color: var(--ink);
  font-family: var(--body);
  font-size: 16px;
  line-height: 1.55;
  overscroll-behavior: none;
}

h1 { font-family: var(--display); font-weight: 600; font-size: 2.4rem; letter-spacing: -.02em; margin: 0 0 6px; }
h2 { font-family: var(--display); font-weight: 500; font-size: 1.2rem; margin: 0 0 10px; }
p { margin: 0 0 14px; }
.muted { color: var(--ink-soft); }
.small { font-size: .88rem; }

.screen { display: none; }
.screen.on { display: block; }

/* ── home ─────────────────────────────────────────────────── */
.home {
  position: relative;
  max-width: 520px;
  margin: 0 auto;
  padding: clamp(14px, 3dvh, 28px) 22px max(12px, env(safe-area-inset-bottom));
}
.home-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 0 6px; }
.home-title { display: flex; align-items: center; gap: .28em; margin: 0; }
.home-title img { width: .8em; height: 1em; object-fit: contain; flex: 0 0 auto; }
.sound-toggle { flex: 0 0 auto; min-width: 44px; padding: 5px 8px; font-size: 1.25rem; line-height: 1.2; }
.home-actions { display: flex; gap: 8px; flex: 0 0 auto; }
.menu-toggle, .feed-btn { position: relative; display: inline-grid; place-items: center; }
.menu-toggle svg, .feed-btn svg { width: 1.2em; height: 1.2em; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; }


/* A small taunt tucked under the button that starts a Sharp game. It uses the
   page's body face and palette instead of introducing a third visual voice. */
.brag {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: calc(100% - 28px); min-height: 2.5em; margin: -6px 14px 0; padding: 7px 12px;
  background: var(--blush); border: 1px solid var(--line); box-shadow: 0 2px 5px #2E1F2B0D; border-radius: 0 0 12px 12px;
  font-family: var(--body); font-weight: 500; font-size: .78rem; line-height: 1.25;
  color: var(--ink-soft); text-align: center; cursor: pointer;
}
.brag[data-loading] { visibility: hidden; }
.brag:hover:not(:disabled), .brag:focus-visible { color: var(--ink); border-color: var(--wild-ring); background: var(--blush-deep); transform: none; }
.brag-prompt { flex: 0 0 auto; color: var(--wild-ink); font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.brag-line {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  overflow-wrap: anywhere; font-variant-numeric: tabular-nums; transition: opacity .2s ease;
}
.brag-line.out { opacity: 0; }
body.dark .brag { background: #241933; border-color: var(--line); box-shadow: 0 2px 6px #0004; color: var(--ink-soft); }
body.dark .brag:hover, body.dark .brag:focus-visible { background: #2E1F3C; border-color: #AA77CD; color: var(--ink); }
body.dark .brag-prompt { color: #C9A6F2; }
.brag-note { margin: 0 0 12px; }
.brag-note b { font-variant-numeric: tabular-nums; }
.field { margin: 0 0 18px; }
.name-hint { font-size: .78rem; }
.name-picker { display: flex; align-items: center; gap: 8px; }
.name-picker input { min-width: 0; }
.name-picker button { flex: 0 0 auto; }
.field .remember-name { display: flex; margin-top: 12px; align-items: center; }
.field label { display: block; font-size: .85rem; color: var(--ink-soft); margin-bottom: 5px; }
input[type="text"] {
  width: 100%;
  font-family: var(--body); font-size: 1rem;
  padding: 12px 15px;
  border: 1.5px solid var(--line); border-radius: 12px;
  background: #fff; color: var(--ink);
}
input[type="text"]:focus-visible { outline: 3px solid var(--pop); outline-offset: 1px; border-color: transparent; }
#joinCode { min-width: 0; flex: 1 1 35%; text-transform: uppercase; letter-spacing: .18em; font-family: var(--display); font-weight: 500; }
#btnJoin { flex: 0 0 auto; padding-inline: 18px; white-space: nowrap; }

.stack { display: flex; flex-direction: column; gap: 10px; }
.home-footer { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 18px; }
.home-footer:not(:has(button:not([hidden]))) { display: none; }
.home-footer button.tiny { padding-inline: 6px; white-space: nowrap; }
.home-pair { margin-top: 14px; }
.home-pair button { padding-inline: 12px; white-space: nowrap; }
.joiner { margin-top: 12px; }

/* The challenge card: a pack, your total, your place. Same quiet voice as the brag. */
.challenge-card {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  width: 100%; margin-top: 16px; padding: 13px 18px; text-align: left;
  background: var(--blush); color: var(--ink); border: 1px solid var(--line-firm); border-radius: 16px;
  box-shadow: 0 2px 6px #2E1F2B0D; font-family: var(--body); font-weight: 400;
}
.challenge-card:hover:not(:disabled), .challenge-card:focus-visible { background: var(--blush-deep); border-color: var(--wild-ring); transform: none; }
.challenge-card { position: relative; }
/* A sticker slapped on the corner: a starburst in the "stop and look" pink, canted. */
.new-splat {
  position: absolute; top: -16px; left: -12px; width: 58px; height: 58px;
  display: grid; place-items: center; transform: rotate(-14deg);
  pointer-events: none; filter: drop-shadow(0 2px 3px #2E1F2B40);
  animation: splat-in .5s cubic-bezier(.3, 1.6, .5, 1) both .3s;
}
.new-splat svg { position: absolute; inset: 0; width: 100%; height: 100%; fill: var(--pop); }
.new-splat span {
  position: relative; color: #fff; font-family: var(--display); font-weight: 700;
  font-size: .78rem; letter-spacing: .02em; text-transform: uppercase;
}
@keyframes splat-in { from { transform: rotate(-40deg) scale(.2); opacity: 0; } to { transform: rotate(-14deg) scale(1); opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .new-splat { animation: none; } }
/* The splat overhangs the card, so keep the text clear of it. */
.challenge-card .challenge-text { padding-left: 38px; }
.challenge-text { display: grid; gap: 1px; min-width: 0; }
.challenge-kicker { color: var(--wild-ink); font-size: .68rem; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; }
.challenge-name { font-family: var(--display); font-weight: 600; font-size: 1.12rem; line-height: 1.2; overflow-wrap: anywhere; }
.challenge-sub { color: var(--ink-soft); font-size: .8rem; line-height: 1.3; }
.challenge-score { display: grid; justify-items: end; flex: 0 0 auto; }
.challenge-score b { font-family: var(--display); font-size: 1.7rem; font-weight: 600; line-height: 1; font-variant-numeric: tabular-nums; }
.challenge-score small { color: var(--ink-soft); font-size: .72rem; white-space: nowrap; }
/* body.dark button sets a dark ink on every button, which outranks .challenge-card. */
body.dark .challenge-card { background: #241933; box-shadow: 0 2px 6px #0004; color: var(--ink); }
body.dark .challenge-card:hover, body.dark .challenge-card:focus-visible { background: #2E1F3C; border-color: #AA77CD; }
body.dark .challenge-kicker { color: #C9A6F2; }

/* The book and board inside the challenge sheet. */
.sheet.stats > #challengeTitle { flex: none; }
.challenge-packs, .challenge-view { display: flex; flex-wrap: wrap; gap: 6px; }
.challenge-packs { margin-bottom: 12px; }
.challenge-total { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin: 4px 0 12px; }
.challenge-total b { font-family: var(--display); font-size: 1.35rem; font-variant-numeric: tabular-nums; }
.challenge-book { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.challenge-book th, .challenge-book td { padding: 8px 6px; border-top: 1px solid var(--line); text-align: center; vertical-align: middle; }
.challenge-book thead th { border-top: 0; font-size: .78rem; font-weight: 500; color: var(--ink-soft); }
.challenge-book tbody th { text-align: left; font-weight: 500; white-space: nowrap; }
.challenge-cell b { display: block; font-family: var(--display); font-size: 1.1rem; line-height: 1.1; }
.challenge-cell small { display: block; color: var(--ink-soft); font-size: .7rem; margin-bottom: 4px; }
.challenge-cell button.tiny { padding: 4px 11px; font-size: .76rem; }
.challenge-pair { font-family: var(--display); font-weight: 600; }
.challenge-pair.unplayed { color: var(--ink-soft); font-weight: 400; }
.challenge-rules { margin: 12px 0 16px; line-height: 1.4; }
.challenge-retired { color: var(--wild-ink); }
.challenge-board { list-style: none; margin: 0; padding: 0; font-variant-numeric: tabular-nums; }
.challenge-board li { display: grid; grid-template-columns: 2.2em minmax(0, 1fr) auto 3.5em; align-items: baseline; gap: 8px; padding: 7px 4px; border-top: 1px solid var(--line); }
.challenge-board li.you { background: var(--blush); border-radius: 8px; }
.challenge-board .rank { color: var(--ink-soft); }
.challenge-board .who { overflow-wrap: anywhere; }
.challenge-board .pairs { color: var(--ink-soft); font-size: .78rem; }
.challenge-board b { text-align: right; font-family: var(--display); }
body.dark .challenge-board li.you { background: #241933; }
@media (max-width: 350px) {
  .home-footer { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
#btnBot { display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
#btnBot .bot-mark { flex: 0 0 18px; }
.split { display: flex; gap: 10px; }
.split > * { flex: 1; }

.rooms { list-style: none; padding: 0; margin: 10px 0 0; }
.rooms li { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-top: 1px solid var(--line); }
.rooms code { font-family: var(--display); font-weight: 600; font-size: 1.1rem; letter-spacing: .12em; }

/* ── buttons ──────────────────────────────────────────────── */
button {
  font-family: var(--display); font-weight: 600; font-size: 1rem;
  border: 0; border-radius: 999px; padding: 13px 26px;
  background: var(--ink); color: #fff; cursor: pointer;
  box-shadow: 0 4px 14px rgba(46, 31, 43, .26);
  transition: transform .12s ease;
}
button:hover:not(:disabled) { transform: translateY(-1px); }
button:active:not(:disabled) { transform: translateY(1px); }
button:focus-visible { outline: 3px solid var(--pop); outline-offset: 3px; }
button:disabled { background: #E3D5DE; color: #A38FA0; box-shadow: none; cursor: not-allowed; }
button.go:not(:disabled) { background: var(--gold); color: #3B2A00; box-shadow: 0 4px 16px rgba(216, 168, 28, .45); }
button.salvage:not(:disabled) { background: var(--salvage); color: #fff; box-shadow: 0 4px 16px rgba(47, 95, 168, .42); }
button.ghost { background: #fff; color: var(--ink); box-shadow: 0 1px 3px rgba(120, 50, 95, .14); }
button.tiny { font-size: .82rem; padding: 7px 15px; }

/* The home actions need a clear edge against the pale page. */
#home button.ghost {
  background: #EFE2F6;
  border: 1px solid #A575C4;
  color: #4B2466;
  box-shadow: 0 3px 0 #D7B9E8, 0 5px 10px #6A2BA81A;
}
#home button.ghost:hover:not(:disabled) { box-shadow: 0 3px 0 #A575C4, 0 8px 17px #6A2BA84D; }
#home #btnBot {
  background: #3D2452;
  color: #fff;
  box-shadow: 0 5px 14px #6A2BA865;
  animation: homeBotHalo 5.5s ease-in-out infinite;
}
#home #btnBot .bot-mark { color: inherit; }
#home #btnBot:hover:not(:disabled) { animation: none; box-shadow: 0 6px 21px #B51E6580; }
#home #btnBuild { animation: homeBuildNudge 12s ease-in-out infinite; }
#home #btnBuild:is(:hover, :focus-visible, [aria-expanded="true"]) { animation: none; }
@keyframes homeBotHalo {
  0%, 83%, 100% { box-shadow: 0 5px 14px #6A2BA865; }
  88%, 94% { box-shadow: 0 5px 21px #D548BB99, 0 0 0 3px #D548BB44; }
}
@keyframes homeBuildNudge {
  0%, 84%, 88%, 92%, 100% { translate: 0; }
  86% { translate: -1px; }
  90% { translate: 1px; }
}

/* ── game shell ───────────────────────────────────────────── */
.game { max-width: 900px; margin: 0 auto; padding: 16px 18px 28px; }



.chip {
  font-family: var(--display); font-weight: 500; font-size: .9rem;
  padding: 7px 15px; border-radius: 999px;
  background: #fff; box-shadow: 0 1px 3px rgba(120, 50, 95, .12);
  white-space: nowrap;
}
/* Whoever is on turn: the whole bubble, not a tint - name, card count and the
   thinking dots all sit inside it, and at a four-handed table it is the fastest
   way to find the live seat. */
/* Whose turn: a quiet outline for everyone else, the go colour for you. */
.chip.turn { background: var(--blush-deep); color: var(--ink); box-shadow: inset 0 0 0 2px var(--ink-soft); }
.chip.turn.me { background: var(--go); color: var(--go-ink); box-shadow: 0 3px 12px rgba(var(--go-glow), .35); }
.opp .chip.turn { transform: translateY(-1px); }
.chip.score { background: var(--gold); color: #3B2A00; }
.chip.wild { background: var(--ink); color: #fff; }
.chip.away { opacity: .55; }
.chip code { font-family: var(--display); letter-spacing: .14em; }

.table {
  background: linear-gradient(170deg, var(--blush) 0%, var(--blush-deep) 100%);
  border-radius: 24px;
  padding: 22px 20px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, .7);
}

.zone { margin-bottom: 20px; }
.zone:last-child { margin-bottom: 0; }
.zone h3 {
  font-family: var(--body); font-weight: 500; font-size: .82rem;
  color: var(--ink-soft); margin: 0 0 9px;
}

.piles { display: flex; gap: 30px; align-items: flex-end; justify-content: center; grid-column: 1; grid-row: 2; }
.table-center { display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(260px, 1fr); gap: 22px; align-items: center; }
.round-info { grid-column: 2; grid-row: 2 / span 2; align-self: stretch; border: 1px solid var(--line); background: var(--porcelain); border-radius: 18px; padding: 18px; min-width: 0; }
.round-info h2 { font-size: 1.1rem; margin-bottom: 14px; }
.wild-feature { position: relative; text-align: center; padding: 12px 10px; border-radius: 12px; background: var(--blush); margin-bottom: 14px; }
.wild-display { display: grid; grid-template-columns: 64px minmax(0, 1fr); align-items: center; column-gap: 8px; }
.wild-display > span { grid-column: 2; }
.wild-display strong { grid-column: 1; grid-row: 1 / 5; }
.wild-feature.went-out .wild-display { visibility: hidden; }
/* The round number moved in here when the round heading went away. */
.wild-feature .round-label { font-family: var(--display); font-weight: 600; font-size: .8rem; color: var(--ink); letter-spacing: .01em; }
.wild-display strong { font-family: var(--display); font-size: 3.8rem; line-height: 1.1; color: var(--wild-ink); }
#chipWild { font-family: var(--display); font-weight: 600; }
.leaderboard { list-style: none; padding: 0; margin: 0; }
.leaderboard li { display: flex; align-items: center; gap: 7px; padding: 7px 0; border-top: 1px solid var(--line); font-size: .8rem; }
.leaderboard .rank { width: 16px; flex: 0 0 auto; font-family: var(--display); }
.leaderboard .player-label { flex: 1; }
.leaderboard .in-lead .rank { color: var(--gold-deep); font-weight: 700; }
.standing-points { white-space: nowrap; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
#banner.final-notice { position: absolute; inset: 8px; display: grid; place-items: center; color: var(--ink); font-weight: 600; line-height: 1.35; margin: 0; }
.round-info.round-alert { background: #FFF0BD; border-color: #D8A81C; box-shadow: 0 0 0 1px #D8A81C, 0 0 0 4px #D8A81C33, 0 6px 24px #D8A81C33; --ink: #3B2A00; --ink-soft: #6B5015; --line: #D8A81C66; }
.round-alert #banner.final-notice { padding: 8px; border-radius: 10px; background: #FFDC70; color: #3B2A00; }
.alert-title { display: block; font-family: var(--display); font-size: 1.12rem; line-height: 1.3; margin-bottom: 8px; }
.alert-instruction { display: block; font-size: .85rem; }
body.dark .round-info.round-alert { background: #382A10; --ink: #FFF0BD; --ink-soft: #ECD59A; --line: #D8A81C77; }

.hand-tools { display: inline-flex; align-items: center; gap: 10px; }
.advice-zone { grid-column: 1; grid-row: 3; align-self: end; min-width: 0; margin-bottom: 0; }
.advice-panel { padding: 14px; margin-bottom: 0; background: var(--porcelain); border: 1px solid var(--line); border-radius: 12px; }
.advice-panel pre { max-height: 320px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font-size: .75rem; margin: 12px 0 0; }
.advice-panel #adviceCandidates { white-space: pre; overflow-wrap: normal; }
#btnAdviceDetails { margin-left: 10px; vertical-align: baseline; }
/* Card references in the advice line wear their suit colour, and the fleur gets
   its SVG rather than the outline glyph no font will fill. */
.advice-panel .suited { color: var(--suit); font-weight: 600; }
.advice-metric.helpful .advice-metric-arrow { color: #16833f; }
.advice-metric.costly .advice-metric-arrow { color: #b52d2d; }
.advice-metric-arrow { font-weight: 800; }
body.dark .advice-metric.helpful .advice-metric-arrow { color: #69d78e; }
body.dark .advice-metric.costly .advice-metric-arrow { color: #ff8585; }
#btnCopyAdvice { margin-top: 12px; }
.splash { position: absolute; inset: 0; z-index: 100; display: grid; place-items: center; pointer-events: none; }
.splash[hidden] { display: none; }
.splash img { grid-area: 1 / 1; width: 130px; height: 130px; filter: drop-shadow(0 9px 3px #35253735); animation: fleur-fall 1.35s both; }
.splash::before { content: ''; grid-area: 1 / 1; width: 100px; height: 15px; margin-top: 145px; border-radius: 50%; background: #35253740; filter: blur(5px); animation: fleur-shadow 1.35s both; }
.home.entering > :not(.splash) { animation: home-reveal 1.35s both; }
@keyframes fleur-fall {
  0% { transform: translateY(var(--fleur-start, -360px)) scale(.95); opacity: 0; animation-timing-function: cubic-bezier(.65, 0, 1, .45); }
  8% { opacity: 1; }
  40% { transform: translateY(0) scale(1); opacity: 1; }
  44% { transform: translateY(11px) scale(1.22, .78); animation-timing-function: ease-out; }
  53% { transform: translateY(-10px) scale(.96, 1.04); }
  61%, 72% { transform: translateY(0) scale(1); opacity: 1; animation-timing-function: cubic-bezier(.4, 0, .2, 1); }
  100% { transform: translate(var(--fleur-x, 0px), var(--fleur-y, -240px)) scale(var(--fleur-scale, .2)); opacity: 0; }
}
@keyframes fleur-shadow {
  0%, 8% { transform: scale(.25); opacity: 0; }
  40%, 44% { transform: scale(1.15); opacity: 1; }
  53% { transform: scale(.85); opacity: .7; }
  61%, 72% { transform: scale(1); opacity: .8; }
  100% { transform: scale(.5); opacity: 0; }
}
@keyframes home-reveal {
  0%, 40% { opacity: .3; }
  44% { opacity: .5; translate: 0 2px; }
  53% { translate: 0 -1px; }
  61%, 100% { opacity: 1; translate: 0; }
}
#optionsBody p.small { margin: 4px 0 16px; }
.public-melds .mini .card .pip { font-size: .7rem; }
.public-melds .mini .card .pip i { font-size: .65rem; }
.public-melds .mini .card .pip.tl { top: 4px; left: 4px; }
.public-melds .mini .card .pip.br { bottom: 4px; right: 4px; }
.public-melds .mini .card .glyph { font-size: 1.4rem; }
.public-melds .mini .card.joker .pip { font-size: .45rem; }
.public-melds .mini .card .wild-label { display: none; }
.player-label { display: inline-block; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.seat-order-label { margin: 0 0 7px; font-size: .72rem; }
.seat-order .chip { display: inline-flex; align-items: center; gap: 7px; min-width: 0; max-width: 100%; }
.seat-order .player-label { max-width: 17ch; }
.seat-number { opacity: .75; font-size: .75em; }
.bot-mark { display: inline-flex; width: 18px; height: 18px; opacity: 1; }
.bot-glyph { width: 100%; height: 100%; overflow: visible; }
.bot-rank-casual { color: #708994; }
.bot-rank-standard { color: #A66C05; }
.bot-rank-sharp { color: #B51E65; }
.seat-order .chip.turn .bot-mark { color: inherit; }
body.dark .bot-rank-casual { color: #9CB7C1; }
body.dark .bot-rank-standard { color: #F0C45C; }
body.dark .bot-rank-sharp { color: #FF7DB6; }
.bot-level { flex: 0 0 auto; font-size: .65rem; opacity: .85; }
.bot-difficulties { margin: 0 0 18px; padding: 14px; border: 1px solid var(--line); border-radius: 12px; }
.bot-difficulties select { width: 100%; font: inherit; color: var(--ink); background: var(--porcelain); border: 1px solid var(--line); border-radius: 10px; padding: 10px; }

@media (max-width: 680px) {
  .table-center { grid-template-columns: minmax(0, 1fr); gap: 18px; }
  .piles, .round-info, .advice-zone { grid-column: 1; grid-row: auto; }
  .round-info { order: -1; align-self: auto; padding: 14px; }
  .round-info-body { display: grid; grid-template-columns: minmax(70px, .7fr) minmax(0, 1.6fr); gap: 12px; align-items: center; }
  .wild-feature { margin-bottom: 0; padding: 8px 4px; }
  .wild-display { display: flex; flex-direction: column; }
  .wild-display strong { font-size: 3rem; }
  /* The normal wild card is deliberately narrow here. The went-out sentence
     borrows the right-hand space without changing the grid row's height. */
  .wild-feature.went-out { z-index: 2; }
  .wild-feature.went-out #banner { right: auto; width: calc(328.571% - 4px); box-sizing: border-box; z-index: 2; }
  .wild-feature .small { font-size: .68rem; }
  .seat-order { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .seat-order .chip { padding: 7px 10px; font-size: .8rem; }
  .leaderboard li { font-size: .75rem; gap: 4px; }
}
.pile { text-align: center; }
.pile .label { font-size: .78rem; color: var(--ink-soft); margin-top: 8px; }
.pile .slot {
  width: var(--card-w); height: var(--card-h);
  border-radius: var(--card-r);
  border: 2px dashed rgba(122, 100, 114, .35);
  display: grid; place-items: center;
  color: var(--ink-soft); font-size: .8rem; text-align: center; padding: 8px;
}

/* ── card ─────────────────────────────────────────────────── */
.card {
  position: relative;
  isolation: isolate;
  width: var(--card-w); height: var(--card-h);
  background: var(--card-face);
  border-radius: var(--card-r);
  box-shadow: var(--card-shadow);
  color: var(--suit, var(--ink));
  flex: 0 0 auto;
  user-select: none; -webkit-user-select: none;
  touch-action: none;
}
.card .pip {
  position: absolute;
  z-index: 2;
  font-family: var(--display); font-weight: 600;
  font-size: 1.3rem; line-height: .95;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
}
.card .pip i { font-style: normal; font-size: 1.05rem; line-height: 1; }
.card .pip.tl { top: 8px; left: 9px; }
.card .pip.br { bottom: 8px; right: 9px; transform: rotate(180deg); }
.card .glyph { position: absolute; z-index: 2; inset: 0; display: grid; place-items: center; font-size: 3.4rem; line-height: 1; opacity: .92; }

.card.joker { color: var(--gold-deep); }
.card.joker .pip { font-size: .86rem; letter-spacing: .02em; }
.card.joker .glyph { font-size: 2.2rem; }

/* Wilds carry their finish everywhere they appear. Round-rank wilds get a
   pearl wash; Jokers get visible foil ribbons. The brief light pass stays
   inside the card face, so a discard is no louder than the same card in hand. */
.card.wild:not(.joker) {
  background:
    radial-gradient(circle at 20% 16%, rgba(245, 117, 205, .42), transparent 44%),
    radial-gradient(circle at 83% 72%, rgba(70, 198, 239, .40), transparent 49%),
    linear-gradient(145deg, rgba(252, 211, 70, .28), rgba(163, 105, 226, .17) 48%, transparent 72%),
    var(--card-face);
}
.card.joker.wild {
  background:
    repeating-linear-gradient(118deg,
      rgba(238, 75, 176, .34) 0 9px,
      rgba(61, 188, 235, .32) 9px 18px,
      rgba(247, 193, 44, .29) 18px 27px,
      rgba(133, 82, 225, .32) 27px 36px),
    var(--card-face);
}
.card.wild::after {
  content: "";
  position: absolute; z-index: 1; inset: 0;
  border-radius: inherit; pointer-events: none;
  background: linear-gradient(115deg, transparent 30%, rgba(255, 255, 255, .72) 48%, transparent 66%);
  background-size: 240% 100%;
  animation: wildShine 7s ease-in-out infinite;
}
.card .wild-label {
  position: absolute; z-index: 3; left: 50%; bottom: 9px; transform: translateX(-50%);
  font-family: var(--body); font-size: .62rem; font-weight: 600;
  letter-spacing: .08em; color: var(--wild-ink);
  text-transform: lowercase;
}
@keyframes wildShine {
  0%, 72% { background-position: 110% 0; }
  90%, 100% { background-position: -110% 0; }
}

.card.back {
  background:
    repeating-linear-gradient(58deg, rgba(106, 43, 168, .13) 0 6px, transparent 6px 14px),
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .85) 0 2px, transparent 2px) 0 0/16px 16px,
    linear-gradient(160deg, #F6E6F1, #E4CBE2);
  border: 3px solid var(--card-face);
  box-shadow: 0 0 0 1px var(--gold), var(--card-shadow);
}
/* Sized as a percentage of the card, not in em, so the deck back, the opponent's
   42px minis and the round sheet all carry the same mark at the same weight. */
.card.back .mark { position: absolute; inset: 0; display: grid; place-items: center; color: var(--gold-deep); opacity: .92; }
.card.back .mark .fdl { width: 46%; height: auto; filter: drop-shadow(0 1px 0 rgba(255, 255, 255, .65)); }

/* Lobby: the stock is the room code on a wild card, the discard a faded way out. */
.card.code-card { display: grid; place-items: center; color: var(--wild-ink); }
.card.code-card .code-text { position: relative; z-index: 2; font-family: var(--display); font-weight: 700; font-size: calc(var(--card-w) * .27 * var(--code-fit, 1)); letter-spacing: .06em; line-height: 1; }
.card.code-card .pip.tl { font-size: .6rem; letter-spacing: .14em; }
.card.leave-card {
  display: grid; place-content: center; justify-items: center; gap: 4px;
  background: transparent; box-shadow: none; border: 2px dashed rgba(122, 100, 114, .45);
  color: var(--ink-soft); opacity: .5; transition: opacity .2s, color .2s, border-color .2s;
}
.card.leave-card:hover, .card.leave-card:focus-visible, .card.leave-card.armed { opacity: 1; }
.card.leave-card.armed { color: var(--pop); border-color: var(--pop); }
.leave-card .leave-x { font-size: 1.5rem; line-height: 1; }
.leave-card .leave-text { font-family: var(--display); font-weight: 600; font-size: 1rem; }
.leave-card .leave-sub { font-size: .66rem; text-align: center; padding: 0 8px; }

.card.tappable { cursor: pointer; }
.card.tappable:hover { transform: translateY(-4px); }
.card:focus-visible { outline: 3px solid var(--pop); outline-offset: 3px; }

/* The card that would let you go out. Gold, glowing, and raised: a ring alone
   is not enough, because in a fanned hand every ring but the last card's is
   painted over by the neighbour overlapping it. */
.card.canOut::before {
  content: "";
  position: absolute; inset: -5px;
  border-radius: calc(var(--card-r) + 5px);
  border: 3px solid var(--gold);
  box-shadow: 0 0 18px rgba(216, 168, 28, .6), inset 0 0 12px rgba(216, 168, 28, .25);
  pointer-events: none;
  animation: winkle 2.2s ease-in-out infinite;
}
@keyframes winkle {
  0%, 100% { opacity: 1; }
  50%      { opacity: .58; }
}

/* ── hand ─────────────────────────────────────────────────── */
.hand { display: flex; padding: 26px 8px 6px; min-height: calc(var(--card-h) + 38px); }
.hand .card {
  margin-left: calc(var(--fan-step) - var(--card-w));
  transition: transform .14s ease, margin .14s ease;
  cursor: grab;
}
.hand .card:first-child { margin-left: 0; }
/* Raised above plain neighbours so the gold ring is not sliced off by the
   card overlapping it. Ordered before :hover and .selected so both still win. */
.hand .card.canOut { transform: translateY(-9px); z-index: 1; }
.hand .card:hover { transform: translateY(-12px); z-index: 2; }
.hand .card.selected { transform: translateY(-22px); z-index: 3; }
/* Set down by the click that deselected it, and the pointer has not left yet.
   Without this the card holds the hover lift and looks half-selected. */
.hand .card.nolift:hover { transform: none; z-index: 1; }
/* A go-out card rests lifted, so "sit down" has to mean its own resting height
   rather than flat, or deselecting it makes it visibly sink. */
.hand .card.canOut.nolift:hover { transform: translateY(-9px); z-index: 1; }
.hand .card.dragging { cursor: grabbing; z-index: 20; transition: none; transform: translateY(-18px) rotate(2deg) scale(1.03); }

.handbar { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 12px; }

/* The turn line tells you whose move it is and what the move is. It used to be
   the same muted 0.82rem as every other zone heading, which is how a final turn
   could arrive without anyone noticing. */
.zone h3.turnline {
  font-family: var(--display); font-weight: 500; font-size: 1.12rem;
  color: var(--ink); margin: 0 0 11px;
}
.zone h3.turnline .quiet { color: var(--ink-soft); font-weight: 400; }

.banner {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--display); font-weight: 500; font-size: 1rem;
  padding: 11px 17px; margin-bottom: 14px;
  border-radius: 16px;
  background: linear-gradient(100deg, #FFF4D2, #FFE9AE);
  color: var(--gold-deep);
  box-shadow: 0 2px 10px rgba(216, 168, 28, .28);
}
.banner .mark { font-size: 1.15rem; }


.public-melds .mini { display: flex; }
.public-melds .mini .card {
  --card-w: 42px; --card-h: 59px; --card-r: 7px;
  margin-left: calc(var(--mini-step, 16px) - var(--card-w));
  box-shadow: var(--card-edge), 0 1px 3px rgba(120, 50, 95, .18);
}
.public-melds .mini .card:first-child { margin-left: 0; }
.public-melds .mini .card .mark { font-size: .85rem; }

/* A card in the advice headline, sized to sit in a line of text. */
.advice-card { display: inline-flex; vertical-align: -4px; margin: 0 3px; }
.advice-card .card {
  --card-w: 26px; --card-h: 36px; --card-r: 5px;
  box-shadow: none; cursor: default;
}
.advice-card .card .pip { font-size: .44rem; top: 2px; left: 3px; }
.advice-card .card .pip i { font-size: .42rem; }
.advice-card .card .pip.br { display: none; }
.advice-card .card .glyph { font-size: .95rem; }
.advice-card .card.joker .pip { font-size: .3rem; }
.advice-card .card .wild-label { display: none; }

/* ── overlay ──────────────────────────────────────────────── */
.overlay {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(46, 31, 43, .45);
  display: grid; place-items: center; padding: 20px;
  overflow-y: auto;
}
.overlay[hidden] { display: none; }
/* An overlay opened from inside another one. Only #achOverlay so far. */
.overlay.stacked { z-index: 60; }
.sheet {
  background: var(--porcelain); border-radius: 24px;
  padding: 26px 24px; max-width: 620px; width: 100%;
  box-shadow: 0 24px 60px rgba(46, 31, 43, .3);
  max-height: 90dvh; overflow-y: auto;
}

/* Results and the local record use the same frame as About: the heading and
   actions stay put while only the potentially long middle scrolls. */
.sheet.results, .sheet.stats { display: flex; flex-direction: column; overflow: hidden; }
.sheet.results > .result-sheet-header,
.sheet.results > .sheet-actions,
.sheet.stats > #statsTitle,
.sheet.stats > #bragTitle,
.sheet.stats > .sheet-actions { flex: none; }
.sheet.results > #sheetBody,
.sheet.stats > #statsBody,
.sheet.stats > #bragBody,
.sheet.stats > #challengeBody {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 14px 6px 18px 0; margin-right: -6px;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent; scrollbar-gutter: stable;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12px, #000 calc(100% - 18px), transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 12px, #000 calc(100% - 18px), transparent);
}
.sheet.results > .sheet-actions,
.sheet.stats > .sheet-actions {
  margin-top: 0; padding-top: 16px; border-top: 1px solid var(--line);
}
.result-sheet-header > .muted { margin-bottom: 0; }

.result { border-top: 1px solid var(--line); padding: 14px 0; }
.result .who { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.result .who strong { font-family: var(--display); font-weight: 600; font-size: 1.05rem; }
.result .points { margin-left: auto; font-family: var(--display); font-weight: 600; font-size: 1.15rem; }
.result .points.zero { color: var(--gold-deep); }
.result .who { flex-wrap: wrap; }
.result .who strong { overflow-wrap: anywhere; min-width: 0; }
.auto-next { display: inline-flex; align-items: center; gap: 8px; margin-left: auto; font-size: .85rem; cursor: pointer; }
.auto-next input { width: 18px; height: 18px; margin: 0; accent-color: var(--pop); }
.auto-next[hidden] { display: none; }
@media (max-width: 420px) {
  .auto-next { flex-basis: 100%; justify-content: flex-end; font-size: .8rem; }
}
.result-hand { padding: 10px 0 16px; }
.result-hand p { margin: 0 0 10px; }
.result-hand-cards { display: flex; flex-wrap: wrap; gap: 8px; }
.result-hand-cards .card { --card-w: 60px; --card-h: 85px; --card-r: 8px; }
.result-hand-cards .card .glyph { font-size: 1.8rem; }
.result-hand-cards .card .pip { font-size: .8rem; }
.result-hand-cards .card .wild-label { font-size: .6rem; }

.meldrow { display: flex; flex-wrap: wrap; gap: 16px; margin-bottom: 8px; }
.meld { display: flex; }
.meld .card, .leftover .card {
  --card-w: 46px; --card-h: 65px; --card-r: 8px;
  margin-left: -16px;
}
.meld .card:first-child, .leftover .card:first-child { margin-left: 0; }
.meld .card .glyph, .leftover .card .glyph { font-size: 1.35rem; }
.meld .card .pip, .leftover .card .pip { font-size: .68rem; top: 3px; left: 4px; }
.meld .card .pip i, .leftover .card .pip i { font-size: .58rem; }
.meld .card .pip.br, .leftover .card .pip.br { display: none; }
.meld .card .wild-label, .leftover .card .wild-label { display: none; }
.meld .card.wild, .leftover .card.wild {
  box-shadow: 0 1px 3px rgba(120, 50, 95, .18);
}
.leftover { display: flex; align-items: center; gap: 10px; }
.leftover .tag { font-size: .78rem; color: var(--ink-soft); flex: 0 0 auto; white-space: nowrap; }
/* The tag is the first child, so the first CARD was not :first-child and kept its
   -16px fan overlap - which is what was eating the "r" of "left over". */
.leftover .tag + .card { margin-left: 0; }

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  background: var(--ink); color: #fff;
  font-size: .9rem; padding: 11px 20px; border-radius: 999px;
  box-shadow: 0 8px 24px rgba(46, 31, 43, .35);
  z-index: 80; max-width: 88vw;
}
.toast[hidden] { display: none; }

/* ── the one orchestrated moment ──────────────────────────── */
@keyframes deal {
  from { opacity: 0; transform: translateY(-18px) rotate(-4deg) scale(.95); }
  to { opacity: 1; transform: none; }
}
.hand .card.fresh { animation: deal .4s cubic-bezier(.2, .8, .3, 1) both; }

@media (prefers-reduced-motion: reduce) {
  * { animation: none !important; transition: none !important; }
}

@media (max-width: 620px) {
  :root { --card-w: 72px; --card-h: 101px; --card-r: 10px; }
  .card .glyph { font-size: 2.3rem; }
  .card .pip { font-size: 1rem; }
  .card .pip i { font-size: .8rem; }
  .card.joker .glyph { font-size: 1.5rem; }
  .card.joker .pip { font-size: .6rem; }
  /* No margin-left override here: the fan step is computed from the real
     container width in app.js, and a hard-coded overlap silently defeated it. */
  .piles { gap: 18px; }
  h1 { font-size: 1.9rem; }
  .home-title { font-size: clamp(1.3rem, 6.4vw, 1.9rem); white-space: nowrap; }
  .game { padding: 12px 12px 24px; }
  .table { padding: 16px 12px; border-radius: 18px; }
}

/* ── rules panel ──────────────────────────────────────────── */
.about-tabs { display: flex; gap: 8px; margin: 16px 0 10px; }
/* The About sheet keeps its title, tabs and button still and scrolls only the
   panel, which fades out at both edges instead of being sliced by the corners. */
.sheet.rules { display: flex; flex-direction: column; overflow: hidden; }
.sheet.rules > * { flex: none; }
.sheet.rules > [role="tabpanel"] {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 14px 6px 22px 0; margin-right: -6px;
  scrollbar-width: thin; scrollbar-color: var(--line) transparent; scrollbar-gutter: stable;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 14px, #000 calc(100% - 22px), transparent);
          mask-image: linear-gradient(to bottom, transparent, #000 14px, #000 calc(100% - 22px), transparent);
}
.sheet.rules > [role="tabpanel"] > :first-child, .sheet.rules > [role="tabpanel"] > .release:first-child h2 { margin-top: 0; }
.sheet.rules > [role="tabpanel"] > h2:first-child { border-top: 0; padding-top: 0; }
@media (prefers-reduced-motion: no-preference) { .sheet.rules > [role="tabpanel"] { scroll-behavior: smooth; } }
.install-help { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 14px; color: var(--ink-soft); }
.install-help summary { cursor: pointer; color: var(--ink); margin-bottom: 10px; }
.install-help button { margin-bottom: 12px; }
.about-tabs button.ghost[aria-selected="true"] { background: var(--ink); color: var(--porcelain); }

/* ── the menu ──────────────────────────────────────────────
   One sheet, four tabs. Fixed height so switching tabs does not make the
   sheet jump; only the panel scrolls. Icons are inline SVG in currentColor
   because the emoji equivalents render as colour emoji on some phones. */
.sheet.menu { height: min(90dvh, 780px); }
.sheet.menu h1 { margin-bottom: 6px; }
.menu-tabs { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin: 14px 0 6px; }
.menu-tabs button.tiny {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 9px 4px 8px; border-radius: 14px; font-size: .74rem; line-height: 1.15; white-space: normal;
}
.menu-tabs svg, .record-teaser svg {
  width: 20px; height: 20px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round;
}
.menu-tabs #tabOptions svg circle { fill: currentColor; }
/* Something unread. Pink is the "look here" colour already (your turn). */
.tab-dot, .menu-dot {
  position: absolute; top: 5px; right: 6px; width: 9px; height: 9px; border-radius: 50%;
  background: var(--pop); box-shadow: 0 0 0 2px var(--porcelain);
}
.menu-dot { top: 2px; right: 2px; }
.vh { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.menu .menu-head {
  font-family: var(--display); font-weight: 600; font-size: 1.05rem;
  margin: 20px 0 10px; padding-top: 14px; border-top: 1px solid var(--line);
}
#recordBody > #btnStatsReset { margin-bottom: 6px; }
/* The record lost its button on the start screen, so it gets a standing
   invitation at the top of Options instead. */
.record-teaser {
  display: flex; align-items: center; gap: 12px; width: 100%; margin: 0;
  padding: 12px 14px; text-align: left; font: inherit; font-weight: 400;
  color: var(--ink); background: var(--blush); border: 1px solid var(--line);
  border-radius: 14px; box-shadow: none;
}
.record-teaser svg { width: 26px; height: 26px; color: var(--gold-deep); }
.record-teaser b { display: block; font-family: var(--display); font-weight: 600; }
.record-teaser .small { display: block; }
.record-teaser .chev { margin-left: auto; font-size: 1.4rem; color: var(--ink-soft); }
.record-teaser:hover:not(:disabled), .record-teaser:focus-visible { border-color: var(--wild-ring); background: var(--blush-deep); }
.record-teaser.fresh { border-color: var(--pop); }
body.dark .record-teaser { background: #241933; color: var(--ink); }
body.dark .record-teaser:hover, body.dark .record-teaser:focus-visible { background: #2E1F3C; border-color: #AA77CD; }
body.dark .record-teaser svg { color: #F0CF6A; }
body.dark .record-teaser.fresh { border-color: var(--pop); }
body.dark .tab-dot, body.dark .menu-dot { box-shadow: 0 0 0 2px #2E1F3C; }
.release + .release { border-top: 1px solid var(--line); margin-top: 24px; padding-top: 20px; }
.sheet.rules .release h2 { border: 0; margin: 4px 0 12px; padding: 0; font-size: 1.25rem; }
.sheet.rules .release li { margin-bottom: 12px; }
.sheet.rules .release-meta { margin: 0; }
.sheet.rules h1 { font-size: 1.9rem; margin-bottom: 18px; }
.sheet.rules :is(#rulesBody, #changesBody) h2 {
  font-family: var(--display); font-weight: 600; font-size: 1.05rem;
  margin: 24px 0 8px; padding-top: 16px; border-top: 1px solid var(--line);
}
.sheet.rules :is(#rulesBody, #changesBody) p { margin: 0 0 10px; }
.sheet.rules :is(#rulesBody, #changesBody) :is(ol, ul) { margin: 0 0 12px; padding-left: 22px; }
.sheet.rules :is(#rulesBody, #changesBody) li { margin-bottom: 3px; }
.sheet.rules strong { font-weight: 600; }
.sheet.rules .example {
  background: #fff; border-radius: 12px; padding: 10px 14px;
  box-shadow: 0 1px 3px rgba(120, 50, 95, .1);
  font-size: .92rem; color: var(--ink-soft);
}
.sheet.rules .example b {
  font-family: var(--display); font-weight: 600; font-size: 1.15rem;
  color: var(--ink); margin-left: 7px;
}
.sheet.rules .example i { font-style: normal; }

/* ═════════════════════════════════════════════════════════════
   Added 2026-09-06: the fleur shape, turn indicators, the round
   sheet's deal-out, and a dark table.
   ═════════════════════════════════════════════════════════════ */

/* ── the fleur ────────────────────────────────────────────── */
/* U+269C is the only suit in the set the browser will not draw reliably: no
   text-presentation guarantee, so it lands as a hairline outline or as a colour
   emoji that ignores `color` outright. app.js swaps in our own path instead. */
.fdl { display: block; fill: currentColor; }
.card .glyph .fdl { width: .8em; height: .8em; }
.card .pip i .fdl { width: .95em; height: .95em; }

/* ── drag to reorder ──────────────────────────────────────── */
/* Neighbours must not be mid-transition while the drop target is measured from
   their bounding boxes, or the insertion point oscillates under the cursor. */
.hand.reordering .card { transition: none; }

/* ── whose turn it is ─────────────────────────────────────── */
.chip.thinking .dots { display: inline-flex; align-items: center; gap: 3px; margin-left: 7px; }
.chip.thinking .dots i {
  width: 4px; height: 4px; border-radius: 50%;
  background: currentColor; animation: blip 1.25s ease-in-out infinite;
}
.chip.thinking .dots i:nth-child(2) { animation-delay: .18s; }
.chip.thinking .dots i:nth-child(3) { animation-delay: .36s; }
@keyframes blip {
  0%, 70%, 100% { opacity: .25; transform: translateY(0); }
  35%           { opacity: 1;   transform: translateY(-3px); }
}

/* Blinks until the player touches something - see the capture-phase ack in
   app.js. An indicator that never stops is just a distraction. */
.zone h3.turnline.yourturn { animation: breathe 2.4s ease-in-out infinite; }
.zone h3.turnline.yourturn::before {
  content: ""; display: inline-block;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--pop); margin-right: 9px; vertical-align: 1px;
  animation: ping 1.6s ease-out infinite;
}
/* A slow glow, not a blink: it is an invitation, not an alarm. */
@keyframes breathe { 0%, 100% { box-shadow: 0 3px 12px rgba(var(--go-glow), .3); } 50% { box-shadow: 0 3px 12px rgba(var(--go-glow), .3), 0 0 0 5px rgba(var(--go-glow), .2); } }
@keyframes ping {
  0%        { box-shadow: 0 0 0 0 color-mix(in srgb, var(--go-ink) 60%, transparent); }
  70%, 100% { box-shadow: 0 0 0 11px color-mix(in srgb, var(--go-ink) 0%, transparent); }
}

.banner { animation: shakein .55s ease both; }
@keyframes shakein {
  0%   { opacity: 0; transform: translateY(-8px); }
  40%  { opacity: 1; transform: translateX(-6px); }
  55%  { transform: translateX(6px); }
  70%  { transform: translateX(-4px); }
  85%  { transform: translateX(3px); }
  100% { transform: none; }
}

/* ── who is winning ───────────────────────────────────────── */
/* Lowest total takes it, which nobody guesses unprompted. */
.chip .lead { color: var(--gold); margin-right: 5px; }
.result .who .lead { color: var(--gold); font-size: 1.1rem; margin-right: -4px; }

/* ── the round sheet deals itself out ─────────────────────── */
/* Melds first, one at a time, then a beat, then the deadwood lands with a shake
   and the number pops. Every step is keyed off a --d delay written by app.js, so
   the reduced-motion rule above switches the whole performance off in one line. */
@keyframes layDown {
  from { opacity: 0; transform: translateY(-14px) rotate(-3deg); }
  to   { opacity: 1; transform: none; }
}
.sheet .meld { animation: layDown .32s cubic-bezier(.2, .8, .3, 1) backwards; animation-delay: var(--d, 0s); }

@keyframes dropShake {
  0%   { opacity: 0; transform: translateY(-16px); }
  50%  { opacity: 1; transform: translateY(0); }
  62%  { transform: translateX(-6px); }
  74%  { transform: translateX(6px); }
  86%  { transform: translateX(-3px); }
  100% { transform: none; }
}
.sheet .leftover { animation: dropShake .62s ease backwards; animation-delay: var(--d, 0s); }

@keyframes pointsPop {
  0%   { opacity: 0; transform: scale(.6); }
  62%  { opacity: 1; transform: scale(1.24); }
  100% { opacity: 1; transform: none; }
}
.sheet .points { animation: pointsPop .42s ease backwards; animation-delay: var(--d, 0s); }

.sheet.narrow { max-width: 430px; }

/* ── resign ───────────────────────────────────────────────── */
button.danger:not(:disabled) { background: #B3245C; color: #fff; box-shadow: 0 4px 14px rgba(179, 36, 92, .34); }
button.ghost.danger:not(:disabled) { background: #fff; color: #B3245C; box-shadow: 0 1px 3px rgba(120, 50, 95, .14); }

/* ── dark table ───────────────────────────────────────────── */
/* Cards keep their white faces - they are cards. Only the room goes dark. */
body.dark {
  --porcelain: #17101E;
  --blush:     #2C1D3A;
  --blush-deep:#1B1128;
  --ink:       #F2E7F1;
  --ink-soft:  #AD98B3;
  --line:      #3B2B49;
  --line-firm: #4A3759;
  --feed-bg: transparent;
  --card-edge: 0 0 0 0 transparent;
  --go: #5CD497; --go-ink: #0B2416; --go-glow: 92, 212, 151; --go-lead: #7FE3B0;
  --card-shadow: 0 2px 4px rgba(0, 0, 0, .45), 0 10px 26px rgba(0, 0, 0, .45);
}
body.dark button { color: #1A1226; }
body.dark button.ghost { background: #2E1F3C; color: var(--ink); box-shadow: 0 1px 3px rgba(0, 0, 0, .5); }
body.dark button.ghost.danger:not(:disabled) { background: #2E1F3C; color: #FF74A8; }
body.dark button:disabled { background: #31233D; color: #7C6889; }
body.dark #home button.ghost {
  background: #39264B;
  border-color: #AA77CD;
  color: #F2DDFF;
  box-shadow: 0 3px 0 #24152F, 0 5px 12px #0008;
}
body.dark #home button.ghost:hover:not(:disabled) { box-shadow: 0 3px 0 #AA77CD, 0 8px 17px #A575C44D; }
body.dark #home #btnBot {
  background: #B78BE0;
  color: #1A1022;
  animation-name: homeBotHaloDark;
}
body.dark #home #btnBot:hover:not(:disabled) { box-shadow: 0 6px 21px #B78BE099; }
@keyframes homeBotHaloDark {
  0%, 83%, 100% { box-shadow: 0 5px 17px #B78BE066; }
  88%, 94% { box-shadow: 0 5px 22px #D47EE6AA, 0 0 0 3px #D47EE655; }
}
@media (prefers-reduced-motion: reduce) {
  #home #btnBot, #home #btnBuild, body.dark #home #btnBot { animation: none; }
}
body.dark input[type="text"] { background: #241933; color: var(--ink); border-color: var(--line); }
/* A broad dark override outranks every variant modifier: `body.dark .chip` is
   0-2-1 and `.chip.turn` only 0-2-0, so the base quietly repainted the turn chip
   and the score chip over their own colours and left the text stranded on the
   wrong background. Any variant listed below must restate BOTH background and
   colour, not just the one that looked wrong. */
body.dark .chip { background: #2E1F3C; color: var(--ink); box-shadow: 0 1px 3px rgba(0, 0, 0, .45); }
body.dark .chip.wild { background: var(--ink); color: #1A1226; }
body.dark .chip.turn { background: var(--blush-deep); color: var(--ink); box-shadow: inset 0 0 0 2px var(--ink-soft); }
body.dark .chip.turn.me { background: var(--go); color: var(--go-ink); box-shadow: 0 3px 14px rgba(var(--go-glow), .35); }
body.dark .chip.score { background: var(--gold); color: #3B2A00; }
body.dark .chip.empty { background: transparent; box-shadow: none; }
body.dark .table { box-shadow: inset 0 1px 0 rgba(255, 255, 255, .07); }
body.dark .wild-feature strong { color: #D7B0FF; }
/* Spades are near-black and clubs are deep teal; both vanish on the dark panel.
   Cards keep white faces so only running text needs lifting. */
body.dark .advice-panel .suited { color: color-mix(in srgb, var(--suit) 55%, #fff); }
body.dark .leaderboard .in-lead .rank { color: var(--gold); }
body.dark .pile .slot { border-color: rgba(220, 200, 230, .28); }
body.dark .overlay { background: rgba(8, 4, 12, .66); }
body.dark .sheet.rules .example { background: #241933; box-shadow: 0 1px 3px rgba(0, 0, 0, .45); }
body.dark .toast { color: #1A1226; }
/* ── build a game ─────────────────────────────────────────── */
.builder {
  margin-top: 14px; padding: 18px 18px 20px;
  background: #fff; border-radius: 18px;
  box-shadow: 0 1px 3px rgba(120, 50, 95, .14), 0 8px 24px rgba(120, 50, 95, .08);
}
.builder .field:last-of-type { margin-bottom: 10px; }
.builder button#btnDeal { width: 100%; margin-top: 4px; }

.seatpick { display: flex; gap: 8px; }
.seatpick .seat {
  flex: 1 1 0; padding: 10px 0; font-size: 1.05rem;
  border: 1.5px solid var(--line); box-shadow: none;
}
.seatpick .seat.on { background: var(--ink); color: #fff; border-color: var(--ink); }
.seatpick .seat:disabled { opacity: .38; border-color: var(--line); }

.check { display: flex; align-items: center; gap: 10px; font-size: .9rem; margin-bottom: 10px; cursor: pointer; }
.check input { width: 18px; height: 18px; accent-color: var(--pop); flex: 0 0 auto; }

.tablesum { font-size: .85rem; color: var(--ink-soft); margin: 0 0 14px; min-height: 2.4em; }

/* An unclaimed chair, in the lobby. Dashed so it reads as absence, not a player. */
.chip.empty {
  background: transparent; color: var(--ink-soft);
  box-shadow: none; border: 1.5px dashed var(--line);
}

/* Four players wrap onto a second line rather than squeezing. */
#scoreboard { row-gap: 8px; }
.opp { row-gap: 12px; }

body.dark .builder { background: #241933; box-shadow: 0 1px 3px rgba(0, 0, 0, .5); }
body.dark .seatpick .seat { background: #2E1F3C; border-color: var(--line); }
body.dark .seatpick .seat.on { background: var(--ink); color: #1A1226; border-color: var(--ink); }
/* Fill mode is `backwards`, not `both`, everywhere a delay is involved.
   `both` also holds the FROM state whenever an animation is defined but never
   runs - a background tab, a throttled page - which left the standings sitting
   at opacity 0 with nothing to show. `backwards` covers the delay and then hands
   back to the element's own styles, so "not animating" degrades to "visible"
   rather than "invisible". */

/* ── the last screen ──────────────────────────────────────── */
/* Eleven rounds deserve better than a line of small text. */
.sheet.finale { text-align: center; animation: sheetIn .5s cubic-bezier(.2, .9, .3, 1) backwards; }
@keyframes sheetIn {
  from { opacity: 0; transform: translateY(16px) scale(.96); }
  to   { opacity: 1; transform: none; }
}
.sheet.finale h1 { font-size: 2.5rem; line-height: 1.1; margin-bottom: 6px; }
.sheet.finale .result-sheet-header > .muted { font-size: 1rem; }

.sheet.finale .trophy {
  display: block; width: 68px; margin: 2px auto 14px;
  color: var(--gold);
  animation: trophyIn .75s cubic-bezier(.2, .9, .3, 1) backwards;
}
.sheet.finale .trophy .fdl {
  width: 100%; height: auto;
  filter: drop-shadow(0 8px 16px rgba(216, 168, 28, .45));
}
@keyframes trophyIn {
  from { opacity: 0; transform: translateY(22px) scale(.6) rotate(-10deg); }
  60%  { transform: translateY(-4px) scale(1.08) rotate(2deg); }
  to   { opacity: 1; transform: none; }
}

.standings { margin: 22px 0 4px; text-align: left; }
.stand {
  display: flex; align-items: baseline; gap: 12px;
  padding: 12px 16px; margin-bottom: 8px;
  background: #fff; border-radius: 14px;
  box-shadow: 0 1px 3px rgba(120, 50, 95, .12);
  animation: standIn .42s cubic-bezier(.2, .8, .3, 1) backwards;
  animation-delay: var(--d, 0s);
}
.stand .pos { font-family: var(--display); font-weight: 600; color: var(--ink-soft); width: 1.3em; }
.stand strong { font-family: var(--display); font-weight: 600; font-size: 1.06rem; }
.stand .pts { margin-left: auto; font-family: var(--display); font-weight: 600; font-size: 1.2rem; }
.stand.win {
  background: linear-gradient(100deg, #FFF4D2, #FFE9AE);
  color: var(--gold-deep);
  box-shadow: 0 4px 18px rgba(216, 168, 28, .34);
}
.stand.win .pos, .stand.win .pts { color: var(--gold-deep); }
@keyframes standIn {
  from { opacity: 0; transform: translateX(-16px); }
  to   { opacity: 1; transform: none; }
}

.sheet.finale .lastround {
  font-family: var(--body); font-weight: 500; font-size: .82rem;
  color: var(--ink-soft); text-align: left;
  margin: 22px 0 0; padding-top: 16px; border-top: 1px solid var(--line);
}
.sheet.finale .result { text-align: left; }

/* Suits falling past the sheet. Inside the overlay so they sit above its wash
   and below the sheet itself, and inert so nothing is ever unclickable. */
.confetti { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.confetti .bit {
  position: absolute; top: 0; left: var(--x);
  font-size: var(--sz); line-height: 1;
  animation: fall var(--t) linear var(--d) forwards;
  will-change: transform, opacity;
}
.confetti .bit .fdl { width: 1em; height: 1em; }
@keyframes fall {
  0%   { transform: translate3d(0, -12vh, 0) rotate(0deg); opacity: 0; }
  8%   { opacity: .95; }
  90%  { opacity: .95; }
  100% { transform: translate3d(0, 106vh, 0) rotate(var(--r)); opacity: 0; }
}

/* Same trap as the chips: `body.dark .stand` was repainting the winner's gold row
   dark purple while `body.dark .stand.win` set only the text, which is how the
   winning name ended up dark brown on dark purple. The gold band stays gold in
   dark mode - it is the one thing on the screen that should shout. */
body.dark .stand { background: #2E1F3C; box-shadow: 0 1px 3px rgba(0, 0, 0, .45); }
body.dark .stand.win {
  background: linear-gradient(100deg, #FFE9AE, #EFCE6B);
  color: #3B2A00;
  box-shadow: 0 4px 20px rgba(216, 168, 28, .4);
}
body.dark .stand.win .pos, body.dark .stand.win .pts { color: #3B2A00; }
/* Gold-deep is chosen to sit on white cards, so as text on the dark sheet it all
   but vanishes. Lift it where it is ink rather than a mark. */
body.dark .result .points.zero { color: #F0CF6A; }
body.dark .sheet.finale .trophy { color: #F0CF6A; }


.public-melds .mini { display: inline-flex; margin: 5px 10px 7px 0; }
.public-melds p { margin: 12px 0 4px; }
#handHeading { font-size: .85rem; line-height: 1.45; margin: 8px 0 14px; }
/* A green wash on the room, not a slab of green: the call to act is the green
   lead-in, the edge and the breathing glow. The edge is an outline so neither
   the box nor the breathe animation (which owns box-shadow) is disturbed. */
#handHeading.turn-prompt {
  background: rgba(var(--go-glow), .09); color: var(--ink);
  outline: 1px solid rgba(var(--go-glow), .5); outline-offset: -1px;
  box-shadow: 0 3px 12px rgba(var(--go-glow), .18);
}
#handHeading.turn-prompt strong { color: var(--go-lead); }
#handHeading.turn-prompt .quiet { color: var(--ink-soft); }
#handHeading.turn-prompt.yourturn::before { background: var(--go); }
body.dark #handHeading.turn-prompt { background: rgba(var(--go-glow), .11); outline-color: rgba(var(--go-glow), .55); }
body.dark #handHeading.turn-prompt .quiet { color: #D9CCDA; }
/* Nothing here may change the prompt's box: the hand sits under this panel and jumped ~20px on a go-out. */
.round-alert #handHeading:not(.turn-prompt) { box-shadow: inset 0 1px 0 var(--line); }
.seat-order-block { grid-column: 1 / -1; align-self: start; }
.seat-order { display: grid; grid-template-columns: repeat(var(--seat-count, 4), minmax(0, 1fr)); gap: 10px; }
.seat-order .chip { display: grid; grid-template-columns: 16px minmax(0, 1fr); grid-template-rows: 22px 18px; gap: 2px 6px; height: 64px; padding: 10px 12px; border-radius: 14px; box-sizing: border-box; }
.seat-order .seat-number { grid-row: 1 / 3; align-self: center; }
.seat-order .player-label { max-width: none; width: 100%; font-size: .85rem; }
.seat-order .bot-level { grid-column: 2; align-self: start; }
.seat-order .chip.turn.me { animation: seat-breathe 2.4s ease-in-out infinite; }
@keyframes seat-breathe { 0%, 100% { box-shadow: 0 0 0 0 rgba(var(--go-glow), .12); } 50% { box-shadow: 0 0 0 4px rgba(var(--go-glow), .28), 0 0 16px rgba(var(--go-glow), .22); } }
#banner.final-notice { font-size: 1rem; }
/* Same padding whether or not it is your turn, and room for the longest prompt
   ("Your last turn. Draw, then discard. Whatever will not meld..."), so neither a
   new turn nor a go-out moves the table. app.js (reservePromptHeight) replaces
   this three-line guess with the measured height of the longest prompt. */
#handHeading { padding: 12px; border-radius: 10px; min-height: calc(3 * 1.45em + 24px); box-sizing: border-box; }
@media (max-width: 680px) {
  .seat-order-block { order: -2; }
  .seat-order { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (prefers-reduced-motion: reduce) { .seat-order .chip.turn, .zone h3.turnline.yourturn, .zone h3.turnline.yourturn::before { animation: none; } }

/* ── the local trophy case ───────────────────────────────────
   Glyphs are text, not emoji, precisely so `color` works on them:
   an unearned cell is the same character at low contrast, which is
   cheaper and steadier than filtering a colour image to grey.
   ───────────────────────────────────────────────────────────── */
.stat-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px;
  margin: 4px 0 14px;
}
.stat-tile {
  background: var(--blush); border-radius: 14px; padding: 10px 6px; text-align: center;
}
.stat-tile b {
  display: block; font-family: var(--display); font-weight: 600; font-size: 1.5rem;
  line-height: 1.1; font-variant-numeric: tabular-nums;
}
.stat-tile span { font-size: .72rem; color: var(--ink-soft); letter-spacing: .02em; }
.stat-foot { margin: 0 0 16px; }

.ach-head {
  font-family: var(--display); font-size: 1.15rem; font-weight: 600;
  margin: 0 0 10px; padding-top: 14px; border-top: 1px solid var(--line);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px;
}
.ach-head span { font-weight: 400; }

.ach-grid {
  /* 84px broke "Featherweight" across a hyphenless line break. 96 holds the
     longest current name on two lines and still fits four across a phone. */
  display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px;
  margin-bottom: 16px;
}
/* A button since the requirement moved behind a press, so the global pill
   styling has to be unwound: everything here that is not layout is undoing
   `button {}`. The 1px hover lift it also brings is welcome and left alone. */
.ach-cell {
  border: 1px solid var(--line); border-radius: 14px; padding: 10px 4px 8px;
  text-align: center; background: transparent; cursor: pointer;
  display: block; width: 100%;
  font: inherit; color: inherit; box-shadow: none;
}
.ach-cell:hover { border-color: var(--gold); }
.ach-glyph {
  display: block; font-size: 1.7rem; line-height: 1.2;
  color: var(--line); transition: color .2s, transform .2s;
}
.ach-name {
  display: block; font-size: .64rem; line-height: 1.25; color: var(--ink-soft);
  overflow-wrap: anywhere;
}
.ach-cell.sealed .ach-glyph { color: var(--blush-deep); }
.ach-cell.got {
  border-color: var(--gold); background: var(--blush);
}
.ach-cell.got .ach-glyph { color: var(--gold-deep); }
.ach-cell.got .ach-name { color: var(--ink); font-weight: 600; }

/* The rank after the name on the panel title. */
#statsTitle .rank { margin-left: .3em; color: var(--gold-deep); }

@media (max-width: 480px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); }
}

/* One trophy, opened from the case. Gold-deep is picked to sit on white, so on
   the dark sheet it needs the same lift the finale trophy gets. */
.sheet.ach-detail { text-align: center; }
.ach-big { font-size: 3.6rem; line-height: 1; color: var(--line); }
.ach-big.got { color: var(--gold-deep); }
.ach-big-name {
  font-family: var(--display); font-size: 1.6rem; font-weight: 600;
  margin: 12px 0 8px;
}
.ach-big-how { margin: 0; }
.ach-big-when { margin: 12px 0 0; font-size: .82rem; color: var(--ink-soft); }
.ach-big-when.got { color: var(--gold-deep); font-weight: 600; }
.ach-bar {
  height: 8px; border-radius: 999px; background: var(--blush);
  overflow: hidden; margin: 16px 0 6px;
}
.ach-bar i { display: block; height: 100%; background: var(--gold); border-radius: 999px; }
.ach-bar-label { margin: 0; }

body.dark .stat-tile { background: #2E1F3C; }
body.dark .ach-cell.got { background: #2E1F3C; }
body.dark .ach-big { color: #4A3557; }
body.dark .ach-big.got, body.dark .ach-big-when.got { color: #F0CF6A; }
body.dark .ach-bar { background: #2E1F3C; }

/* ── play feed, seats popover, turn bubble ───────────────────
   Popovers sit at body level so the home page can open Options too. */
/* The feed line and its two buttons share one row: nothing new stacks on the table.
   Menu over Details, each half the feed's height, so the feed keeps its whole width. */
.feed-row { display: flex; align-items: stretch; gap: 8px; margin-bottom: 10px; }
.feed-buttons { flex: 0 0 auto; display: flex; flex-direction: column; gap: 6px; }
.feed-buttons > button { flex: 1 1 0; min-width: 44px; min-height: 0; padding: 0 10px; font-size: 1.1rem; border-radius: 10px; }
/* Fixed at three lines whatever the message says, card chips included, so the feed
   can never change the round panel's height. Longer lines clamp; Details has the rest. */
.feed-area { display: flex; align-items: center; flex: 1 1 auto; min-width: 0; width: 100%; box-sizing: border-box; height: calc(4.2em + 18px); min-height: 0; margin-bottom: 0; padding: 8px 10px; overflow: hidden; text-align: left; font-family: inherit; font-weight: 400; font-size: .88rem; line-height: 1.4; color: var(--ink-soft); background: var(--feed-bg); border: 1px dashed var(--line-firm); border-radius: 10px; box-shadow: none; }
.feed-area:hover:not(:disabled) { transform: none; border-color: var(--pop); }
#feedLatest { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden; overflow-wrap: anywhere; }
/* Your own stock draw: private, so say so quietly. */
.feed-private { font-size: .78em; opacity: .75; font-style: italic; }
/* Card mentions get a tiny white card face so dark suits survive the dark room.
   Keep the face fixed and no taller than the line: it must not make feed or coach
   copy jump when the mentioned suit changes. */
.feed-area .suited, .feed-list .suited, .advice-panel .suited {
  display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box; width: 1.12em; height: 1.12em; padding: 0;
  vertical-align: -.14em; border-radius: 999px; background: #fff;
  color: var(--suit, #1A1226); font-weight: 700; line-height: 1;
  box-shadow: 0 0 0 1px #0002;
}
.feed-area .suited .fdl, .feed-list .suited .fdl, .advice-panel .suited .fdl {
  display: block; width: .68em; height: .85em;
}
/* A named card (rank + suit, or Joker) is a tiny card face, not a coin: same
   height as the glyph-only coin, width follows the label. */
.feed-area .suited.card-tag, .feed-list .suited.card-tag {
  width: auto; min-width: 1.12em; padding: 0 .22em; gap: .05em;
  border-radius: .26em; white-space: nowrap;
}
#gameDetails { margin: auto; width: min(400px, calc(100vw - 32px)); height: min(80dvh, 600px); padding: 22px; border: 1px solid var(--line); border-radius: 18px; color: var(--ink); background: var(--porcelain); box-shadow: 0 12px 40px #0005; }
/* Fixed height and flex so switching tabs does not resize the sheet; only the panel scrolls. */
#gameDetails:popover-open { display: flex; flex-direction: column; }
#gameDetails > * { flex: none; }
#gameDetails > h2 { margin: 0; }
#gameDetails > .details-panel {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding: 8px 4px 12px 0; scrollbar-width: thin; scrollbar-color: var(--line) transparent;
}
#gameDetails > .details-panel[hidden] { display: none; }
#gameDetails .details-foot { margin: 10px 0 0; }
#gameDetails .seat-order { grid-template-columns: repeat(2, minmax(0, 1fr)); }
#gameDetails::backdrop { background: #0004; }
.game-code { margin: 0 0 14px; }
.game-code code { font-size: 2rem; font-weight: 600; letter-spacing: .12em; }
.game-actions { margin: 0 0 16px; padding: 12px 14px; border: 1px solid var(--line); border-radius: 14px; }
.game-actions p.small { margin: 6px 0 0 !important; }
.feed-list { margin: 12px 0; padding: 0; list-style: none; font-size: .88rem; }
.feed-list li { padding: 6px 0; border-top: 1px solid var(--line); }
.feed-list li:first-child { border-top: 0; }
#seatsList { grid-template-columns: minmax(0, 1fr); margin: 12px 0; }

.leaderboard li.turn .player-label { flex: 0 1 auto; margin-right: auto; padding: 2px 11px; border-radius: 999px; background: var(--blush-deep); color: var(--ink); font-weight: 600; box-shadow: inset 0 0 0 1.5px var(--ink-soft); }
.leaderboard li.turn.me .player-label { background: var(--go); color: var(--go-ink); box-shadow: none; animation: seat-breathe 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .leaderboard li.turn .player-label { animation: none; } }
/* body.dark button sets a dark ink on every button and outranks .feed-area, which left the feed text black on the dark room. */
body.dark .feed-area { color: var(--ink-soft); }

/* ── coach ────────────────────────────────────────────────── */
/* Stockfleur in the advice panel, talking in a bubble. The glow is the whole
   expression: green at rest, a severity colour for a few seconds after a note.
   No gold (gold is "this discard wins the round") and no violet (wild). */
.coach-row { display: flex; align-items: center; gap: 14px; }
.coach-row:not([hidden]) + #adviceAction:not([hidden]) { display: block; margin-top: 12px; }
.coach-bot {
  --glow: #3FBF7F;
  position: relative; flex: none; width: 46px; height: 46px;
  display: grid; place-items: center; color: var(--ink); padding: 0;
  border: 0; background: none; box-shadow: none; cursor: pointer;
}
.coach-bot:hover { box-shadow: none; transform: none; }
.coach-bot:focus-visible { outline: 2px solid var(--pop); outline-offset: 5px; border-radius: 50%; }
.coach-bot::before {
  content: ""; position: absolute; inset: -8px; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--glow) 60%, transparent) 0%, color-mix(in srgb, var(--glow) 22%, transparent) 45%, transparent 72%);
  transition: background .5s ease;
  animation: coach-breathe 3.4s ease-in-out infinite;
}
.coach-bot .bot-glyph { position: relative; width: 32px; height: 32px; }
.coach-bot[data-mood="good"]  { --glow: #2FD67A; }
.coach-bot[data-mood="close"] { --glow: #2FB3A8; }
.coach-bot[data-mood="weak"]  { --glow: #E8853A; }
.coach-bot[data-mood="miss"]  { --glow: #E2563A; }
.coach-bot[data-mood="big"]   { --glow: #D92F52; }
.coach-bot[data-mood="streak"] { --glow: #2FD67A; }
.coach-bot[data-mood="streak"]::before { inset: -13px; }
.coach-bot[data-mood="streak"] .bot-glyph { animation: coach-hop .4s ease-out 3; }.coach-bot[data-mood="big"]::before { inset: -13px; animation-duration: 1.1s; }
.coach-bot[data-mood="good"] .bot-glyph  { animation: coach-hop .45s ease-out; }
.coach-bot[data-mood="close"] .bot-glyph { animation: coach-tilt .6s ease-in-out; }
.coach-bot[data-mood="weak"] .bot-glyph,
.coach-bot[data-mood="miss"] .bot-glyph  { animation: coach-wiggle .5s ease-in-out 2; }
.coach-bot[data-mood="big"] .bot-glyph   { animation: coach-wiggle .32s ease-in-out 4; }
@keyframes coach-breathe { 0%, 100% { transform: scale(.92); opacity: .85; } 50% { transform: scale(1.06); opacity: 1; } }
@keyframes coach-hop { 0%, 100% { transform: translateY(0); } 40% { transform: translateY(-5px); } }
@keyframes coach-tilt { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(-9deg); } }
@keyframes coach-wiggle { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-10deg); } 75% { transform: rotate(10deg); } }

.coach-bubble {
  position: relative; flex: 1; min-width: 0; min-height: calc(2.9em + 18px); margin: 0; padding: 9px 13px;
  background: #fff; border: 1px solid var(--line-firm); border-radius: 12px;
  box-shadow: 0 1px 3px rgba(120, 50, 95, .10);
  line-height: 1.45; overflow-wrap: anywhere;
}
.coach-bubble::before {
  content: ""; position: absolute; left: -6px; top: 50%; width: 10px; height: 10px;
  background: inherit; border-left: 1px solid var(--line-firm); border-bottom: 1px solid var(--line-firm);
  transform: translateY(-50%) rotate(45deg);
}
.coach-bubble[data-tone="close"] { border-color: #2FB3A8; }
.coach-bubble[data-tone="weak"]  { border-color: #E8853A; }
.coach-bubble[data-tone="miss"]  { border-color: #E2563A; }
.coach-bubble[data-tone="big"]   { border-color: #D92F52; }
.coach-bubble[data-tone="close"]::before { border-color: #2FB3A8; }
.coach-bubble[data-tone="weak"]::before  { border-color: #E8853A; }
.coach-bubble[data-tone="miss"]::before  { border-color: #E2563A; }
.coach-bubble[data-tone="big"]::before   { border-color: #D92F52; }
body.dark .coach-bubble { background: #241933; box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  .coach-bot::before, .coach-bot .bot-glyph { animation: none; }
}

/* ── the coach's one-time introduction ──────────────────────
   A full-screen catcher dims the room through a giant box-shadow on a hole
   sized to the advice panel, so the panel needs no z-index fight with its
   ancestors. The catcher takes every click; the panel only shows through. */
.coach-intro { position: fixed; inset: 0; z-index: 70; cursor: pointer; outline: none; }
.coach-intro-hole {
  position: fixed; border-radius: 16px; pointer-events: none;
  box-shadow: 0 0 0 200vmax rgba(8, 12, 18, .62);
  transition: left .2s ease, top .2s ease, width .2s ease, height .2s ease;
  animation: coach-intro-in .35s ease-out backwards;
}
@keyframes coach-intro-in { from { box-shadow: 0 0 0 200vmax rgba(8, 12, 18, 0); } }
.coach-next { margin-left: 8px; white-space: nowrap; font-size: .8em; opacity: .6; font-variant-numeric: tabular-nums; }
