:root {
    /* ── V2 Brand Palette ── */
    --bg:       #0B0706;
    --surface:  #160C0E;
    --surface2: #1F1014;
    --border:   #2E1A1F;
    --gold:     #C9A227;
    --gold-hi:  #EDD06A;
    --red:      #B51E1E;
    --green:    #4caf7d;
    --felt-deep:#071E17;
    --felt-mid: #0C4030;
    --text:     #F0E5CC;
    --muted:    #8a7a6a;
    /* ── Card sizes (unchanged) ── */
    --card-w:  clamp(54px, 9vw, 80px);
    --card-h:  clamp(76px, 12.5vw, 112px);
    --board-w: clamp(68px, 11vw, 96px);
    --board-h: clamp(96px, 15vw, 134px);
  }
  * { box-sizing: border-box; margin: 0; padding: 0; -webkit-tap-highlight-color: transparent; }
  body { background: var(--bg); color: var(--text); font-family: 'DM Sans', sans-serif; min-height: 100vh; overflow: hidden; user-select: none; overscroll-behavior: none; }
  .screen { position: fixed; inset: 0; display: flex; align-items: center; justify-content: center; flex-direction: column; }
  .screen.hidden { display: none; }
  .hidden { display: none !important; }

  /* ── LOBBY ── */
  /* Override the default .screen flex-centered fixed layout so the lobby
     can scroll when content overflows the viewport (e.g. when the
     Customize game settings collapsible is open on mobile). Center stays
     intact via margin:auto on .lobby-inner — short content still looks
     centered, long content scrolls naturally. */
  #lobby {
    background:
      radial-gradient(ellipse at 35% 15%, rgba(107,23,40,0.55) 0%, transparent 55%),
      radial-gradient(ellipse at 75% 80%, rgba(61,10,21,0.4) 0%, transparent 50%),
      var(--bg);
    align-items: stretch;
    justify-content: flex-start;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* WKWebView (Capacitor shell): without an explicit pan-y the webview can
       swallow vertical drags on a fixed-position scroller — taps worked but
       scrolling silently didn't. contain stops the rubber-band chaining to
       the (overflow:hidden) body. No effect on desktop web. */
    touch-action: pan-y;
    overscroll-behavior: contain;
  }
  /* flex-shrink: 0 is load-bearing: #lobby is a column flex scroller, and
     WebKit compresses a shrinkable child to the container height instead of
     letting it overflow — scrollHeight never exceeded clientHeight, so the
     lobby could not scroll AT ALL in the iOS shell (Chromium's min-height
     heuristic masked this on web). */
  .lobby-inner { text-align: center; padding: 24px; max-width: 420px; width: 100%; margin: auto; flex-shrink: 0; }
  /* Native app only (body.native-shell set by auth.js): the corner chips sit
     at safe-area+14 with 44px height; start the lobby content fully below
     them so the RUNG title can't collide with the Leaderboard/Player pills
     on phone-width screens. Web keeps the original 24px — its chips pin to
     the window corners, far from the centered title. */
  body.native-shell .lobby-inner {
    padding-top: calc(68px + var(--sa-t, env(safe-area-inset-top, 0px)));
    /* Clearance for the fixed WhatsApp footer strip: without it the last
       lobby card sits UNDER the strip while the container technically fits
       the viewport — nothing overflowed, so nothing could scroll. */
    padding-bottom: calc(96px + var(--sa-b, env(safe-area-inset-bottom, 0px)));
  }
  .game-title { font-family: 'Cinzel', serif; font-size: clamp(2.8rem,9vw,4rem); font-weight: 700; color: var(--gold); letter-spacing: 0.18em; line-height: 1; text-shadow: 0 2px 24px rgba(201,162,39,0.25); }
  .game-subtitle { color: var(--muted); font-size: 0.8rem; letter-spacing: 0.22em; text-transform: uppercase; margin-top: 7px; }
  .suit-row { display: flex; gap: 10px; justify-content: center; margin: 14px 0 22px; font-size: 1.6rem; }
  .suit-row span { opacity: 0.5; animation: floatPulse 3s ease-in-out infinite; }
  .suit-row span:nth-child(2){animation-delay:.5s}.suit-row span:nth-child(3){animation-delay:1s}.suit-row span:nth-child(4){animation-delay:1.5s}
  @keyframes floatPulse { 0%,100%{opacity:0.3;transform:translateY(0)} 50%{opacity:0.9;transform:translateY(-4px)} }
.form-group { margin-bottom: 14px; text-align: left; }
  .form-group label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); display: block; margin-bottom: 6px; }
  .form-group input, .form-group select { width: 100%; background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; color: var(--text); font-size: 0.95rem; outline: none; transition: border-color 0.2s; -webkit-appearance: none; appearance: none; }
  .form-group select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%238a7a6a' stroke-width='1.5' fill='none'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; padding-right: 36px; }
  .form-group input:focus, .form-group select:focus { border-color: var(--gold); }
  .form-group select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%237a7e8e' stroke-width='1.5' fill='none'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 14px center; padding-right: 36px; }
  .settings-row { display: flex; gap: 10px; }
  .settings-row .form-group { flex: 1; }
  .btn { width: 100%; padding: 14px; border-radius: 8px; border: none; cursor: pointer; font-size: 1rem; font-weight: 600; letter-spacing: 0.04em; transition: all 0.2s; }
  .btn-primary { background: linear-gradient(135deg, var(--gold) 0%, #8B6A10 100%); color: #0B0706; border: none; font-weight: 700; }
  .btn-primary:hover { transform: translateY(-1px); box-shadow: 0 8px 24px rgba(201,162,39,0.3); filter: brightness(1.08); }
  .btn-ghost { background: transparent; color: rgba(240,229,204,0.65); border: 1px solid rgba(201,162,39,0.28); margin-top: 10px; }
  .btn-ghost:hover { border-color: rgba(201,162,39,0.55); color: var(--text); }

  /* ── DEALING SCREEN ── */
  #dealing-screen { background: radial-gradient(ellipse at center, #1a2e1a 0%, #0a150a 100%); z-index: 20; }
  .dealing-inner { text-align: center; padding: 24px; max-width: 500px; width: 100%; }
  .dealing-title { font-family: 'Cinzel', serif; font-size: clamp(1.8rem, 5vw, 2.4rem); font-weight: 700; color: var(--gold); margin-bottom: 6px; letter-spacing: 0.04em; text-shadow: 0 2px 20px rgba(201,162,39,0.3); }
  .dealing-sub { color: var(--muted); font-size: 0.82rem; margin-bottom: 28px; line-height: 1.5; }
  .flip-cards-row { display: flex; gap: 0; justify-content: center; margin-bottom: 24px; align-items: flex-start; }
  .team-flip-group { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 0 22px; }
  .team-flip-header { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); font-weight: 700; background: rgba(255,255,255,0.06); padding: 3px 12px; border-radius: 20px; }
  .team-flip-cards { display: flex; gap: 14px; }
  .team-flip-divider { width: 1px; background: rgba(255,255,255,0.1); align-self: stretch; margin: 4px 0; }
  .flip-wrap { display: flex; flex-direction: column; align-items: center; gap: 8px; }
  /* nowrap: a wrapped label makes that column taller and shoves ITS card out
     of line with the other three (the crown emoji did exactly this on 375w —
     field screenshot, 2026-09-19). */
  .flip-wrap .p-label { font-size: 0.72rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; transition: color 0.4s; white-space: nowrap; }
  .flip-wrap.winner .p-label { color: var(--gold); font-weight: 600; }
  /* Green-screen family redesign (2026-08-28) — picker-matching chrome. */
  .dealing-eyebrow { font-size: 0.68rem; letter-spacing: 0.22em; color: var(--gold); margin-bottom: 4px; min-height: 1em; }
  .dealing-eyebrow:empty { display: none; }
  .flip-wrap.winner .card { border-color: var(--gold-hi); box-shadow: 0 0 18px rgba(201,162,39,0.45); transform: scale(1.08); }
  /* The winner's crown PERCHES ON THE CARD's top edge, not in the label: in
     the label it widened the column, wrapped onto its own line, and pushed
     the winner's card below the other three. A positioned badge shifts no
     layout at any width, and mkCard still owns the face itself. */
  .flip-wrap.winner .card::after { content: '👑'; position: absolute; top: -13px; left: 50%;
    transform: translateX(-50%); font-size: 1.05rem; line-height: 1;
    text-shadow: 0 2px 8px rgba(0,0,0,0.65); }
  /* Choosing-rung wait: the flip stays as the story, dimmed but readable. */
  .flip-cards-row.flip-dimmed .flip-wrap:not(.winner) { opacity: 0.55; }
  .flip-cards-row.flip-dimmed .flip-wrap.winner .card { transform: scale(1.0); }
  /* Tie / caller reason gets the gold-box treatment the redeal banner wears. */
  .dealing-msg.deal-reason { border: 1px solid rgba(201,162,39,0.4); background: rgba(201,162,39,0.08); border-radius: 10px; padding: 8px 14px; display: inline-block; }
  /* Suit ring: something alive to rest the eye on while the caller thinks. */
  .choosing-ring { width: 54px; height: 54px; border-radius: 50%; border: 2px solid var(--gold); margin: 14px auto 10px; position: relative; }
  .choosing-ring span { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 1.3rem; color: var(--gold-hi); opacity: 0; animation: ringSuit 3.2s linear infinite; }
  .choosing-ring span:nth-child(2) { animation-delay: 0.8s; color: var(--red); }
  .choosing-ring span:nth-child(3) { animation-delay: 1.6s; }
  .choosing-ring span:nth-child(4) { animation-delay: 2.4s; color: var(--red); }
  @keyframes ringSuit { 0%, 22% { opacity: 1; } 28%, 100% { opacity: 0; } }
  /* Game Starting: the four seats as team pills — signs of life. */
  .deal-seat-pills { display: flex; justify-content: center; align-items: center; gap: 7px; flex-wrap: wrap; margin: 4px 0 14px; }
  .deal-seat-pills .dsp { border: 1px solid rgba(255,255,255,0.18); border-radius: 20px; padding: 5px 14px; font-size: 0.78rem; color: var(--text); }
  .deal-seat-pills .dsp-vs { font-size: 0.68rem; color: var(--muted); }
  .deal-pulse-mark { font-size: 2.6rem; margin-bottom: 10px; animation: dealPulse 1.4s ease-in-out infinite; display: inline-block; }
  @keyframes dealPulse { 0%, 100% { transform: scale(1); opacity: 0.75; } 50% { transform: scale(1.12); opacity: 1; } }
  #deal-countdown { margin-top: 10px; }
  /* trump display block */
  .trump-block { text-align: center; padding-bottom: 12px; margin-bottom: 10px; border-bottom: 1px solid rgba(255,255,255,0.07); }
  .trump-label-sm { font-size: 0.58rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.16em; margin-bottom: 6px; }
  /* Rung symbol — bigger than v1 (70→88px desktop) and gently pulsing so
     the eye finds it instantly. Suit name visible underneath. */
  .trump-sym-big { position: relative; font-size: 4rem; line-height: 1; display: flex; align-items: center; justify-content: center; width: 88px; height: 88px; margin: 0 auto; border-radius: 16px; border: 1px solid rgba(255,255,255,0.08); transition: color 0.3s, background 0.3s, border-color 0.3s; }
  /* Hidden Rung, caller's own view: the suit stays visible (they picked it) but
     a lock badge marks that it's still secret from the table and the pot is
     locked. Without it the caller can't tell a hidden round from a normal one. */
  .trump-sym-big.is-secret::after {
    content: '🔒'; position: absolute; right: -7px; bottom: -7px;
    font-size: 0.82rem; line-height: 1; padding: 4px;
    background: #0B0706; border: 1px solid rgba(201,162,39,0.45);
    border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.5);
  }
  .trump-sym-big.is-red   { background: rgba(181,30,30,0.18); border-color: rgba(181,30,30,0.35); animation: trumpPulseRed   3.2s ease-in-out infinite; }
  .trump-sym-big.is-black { background: rgba(201,162,39,0.10); border-color: rgba(201,162,39,0.28); animation: trumpPulseBlack 3.2s ease-in-out infinite; }
  @keyframes trumpPulseRed   { 0%,100% { box-shadow: 0 0 22px rgba(181,30,30,0.22);  } 50% { box-shadow: 0 0 36px rgba(181,30,30,0.45);  } }
  @keyframes trumpPulseBlack { 0%,100% { box-shadow: 0 0 22px rgba(201,162,39,0.14); } 50% { box-shadow: 0 0 36px rgba(201,162,39,0.36); } }
  /* Trump suit name — visible by default now (was hidden in v1). Also
     forces visibility even with .hidden class so legacy code paths keep it. */
  .trump-name        { display: block; font-family: 'Cinzel', serif; font-size: 0.78rem; color: var(--text); letter-spacing: 0.05em; margin-top: 6px; }
  .trump-name.hidden { display: block; }
  .trump-name.is-secret { color: var(--gold); }
  .dealing-msg { color: var(--text); font-size: 0.95rem; margin-bottom: 20px; min-height: 24px; line-height: 1.5; }
  .dealing-msg .hl { color: var(--gold); font-weight: 600; }
  .pees-banner { background: rgba(201,168,76,0.1); border: 1px solid rgba(201,168,76,0.3); border-radius: 10px; padding: 10px 16px; font-size: 0.82rem; color: var(--gold); margin-bottom: 20px; }
  .verify-banner { background: rgba(224,85,85,0.12); border: 1px solid rgba(224,85,85,0.55); border-radius: 10px; padding: 12px 16px; font-size: 0.85rem; color: var(--red); margin: 0 0 16px; cursor: pointer; line-height: 1.5; }
  .verify-banner b { color: #fff; }
  .seat-remove { background: rgba(224,85,85,0.15); border: 1px solid rgba(224,85,85,0.5); color: var(--red); border-radius: 50%; width: 22px; height: 22px; line-height: 1; font-size: 0.7rem; cursor: pointer; font-family: inherit; padding: 0; flex-shrink: 0; }
  .seat-remove:active { transform: scale(0.92); }
  .pees-banner.hidden { display: none; }

  /* ── GAME TABLE ── */
  /* Track the DYNAMIC viewport height so the bottom-anchored hand / turn-timer
     sit ABOVE the iOS Safari bottom toolbar (and any in-app browser chrome)
     instead of behind it. #game is position:fixed; inset:0 (from .screen);
     adding an explicit height over-constrains the box vertically, so `bottom`
     is dropped and top:0 + height wins. The 100vh line is a graceful fallback
     for browsers without dvh — they keep today's behaviour, no regression. */
  #game { background: radial-gradient(ellipse at center, rgba(12,64,48,0.4) 0%, var(--felt-deep) 100%); height: 100vh; height: 100dvh; }
  .felt { position: absolute; inset: 0; background: radial-gradient(ellipse at center, var(--felt-mid) 0%, var(--felt-deep) 100%); }
  .felt::before { content: ''; position: absolute; inset: 20px; border: 1px solid rgba(255,255,255,0.035); border-radius: 50%; pointer-events: none; }

  /* A player's remaining cards are one face-down card with a count on it. It
     used to float on its own in the middle of their side, reading as a card in
     play. It now tucks behind the name pill so it reads as "this seat, this
     many left" — one object, not two. Scaled down for the same reason: it is a
     counter, not a card you can do anything with. */
  .player-zone { position: absolute; display: flex; align-items: center; justify-content: center; gap: 3px;
                 transform-origin: center; z-index: 4; }
  .player-zone .card, .player-zone .fd { transform: scale(0.62); }

  /* Opponent card counts are not rendered. In Rung all four players always hold
     the same number of cards — thirteen each, one played per trick — so a
     per-seat count can never differ from 13 minus the tricks played, which the
     trick counter already says. It was the same number written four times, and
     read as an unexplained digit under each name.

     The elements stay in the DOM: render.js writes to them, and a game where
     hands CAN differ would want them back. */
  .player-zone { display: none; }
  /* 128px, tracking .pos-top's move to 90px. The name pill is ~31px tall, so
     the old 95px put the face-down fan directly on top of the name — it read as
     a nameless seat rather than as an overlap. */
  .zone-top   { top: calc(182px + var(--sa-t, env(safe-area-inset-top, 0px))); left: 50%; transform: translateX(-50%); flex-direction: column; align-items: center; }
  /* Centred on the same axis as the vertical name pill (top: 50%), not offset
     28px below it. The offset was inherited from the horizontal pills, where the
     fan sat under the name; with the name now turned 90° alongside, the two read
     as one seat only if they share a centre line. */
  /* Side fans sit ABOVE the played card, not beside it. With the played cards
     moved out toward their owners there is no longer horizontal room for both
     on a 375px screen — the fan and the card were landing on each other. Order
     from the edge inward is now: name pill, then the fan above, then that
     player's played card level with the pill. */
  /* Width is pinned to the strip outside the played card. The name pill and
     that player's played card share a vertical band, so the marker can only sit
     behind the pill if it stays narrower than the gap to the card — otherwise
     tucking it under the name puts it back under the card. */
  /* Both chips flush with the bottom end of their pill, at the same offset, so
     the two sides mirror each other. An earlier edit missed this rule and left
     the left seat on a stale value — the two sides sat at different heights and
     looked broken rather than deliberate. */
  .zone-left  { left: 7px; width: 21px; top: calc(50% + 26px); flex-direction: column; align-items: center; }
  .zone-right { right: 7px; width: 21px; top: calc(50% + 26px); flex-direction: column; align-items: center; }
  /* At 320px the play area hits its 260px floor and the trick reaches out far
     enough to touch the side markers. The markers give way, not the cards —
     they are a count, and the trick is the game. */

  /* count badge on the single face-down bot card */
  .bot-card-count { position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); font-size: 0.68rem; font-weight: 700; color: rgba(255,255,255,0.5); line-height: 1; pointer-events: none; }

  /* player name label above each bot's card pile */

  .pinfo { position: absolute; z-index: 6; background: rgba(7,5,5,0.82); backdrop-filter: blur(8px); border: 1px solid var(--border); border-radius: 10px; padding: 7px 14px; font-size: 0.82rem; line-height: 1; white-space: nowrap; display: flex; align-items: center; gap: 7px; transition: border-color 0.3s; }
  /* A pill is the same size whether or not its player is the caller, is Senior,
     or is on turn. Those badges used to grow the box — Omar's pill was visibly
     taller than Sara's for holding a crown and a star — which reads as the
     layout shifting rather than as information. Fixed box, and the turn is
     shown by light instead. */
  .pos-top, .pos-bottom { min-width: 104px; justify-content: center; }
  .pos-left, .pos-right { min-height: 96px; justify-content: center; }
  /* Turn = an illuminated stroke. Thicker, brighter, and breathing, so it is
     legible at a glance from across the table without the dot. */
  .pinfo.active { border-color: var(--green); box-shadow: 0 0 0 1px var(--green) inset;
                  animation: turnGlow 1.6s ease-in-out infinite; }
  /* Partner is no longer marked. They sit opposite you — the seat says it, and
     a second colour on the table only competed with the one that matters.
     Green now means exactly one thing: it is that player's turn. */
  .pinfo.partner { }
  /* The dot is gone. It spent a permanent 13px of a pill to say something the
     border can say for free, and on the rotated side pills it cost width that
     is scarcer than anything else on the table. */
  .pinfo .dot { display: none; }


  .pinfo.offline .dot { background: #ff6b6b !important; animation: staleBlink 1.5s ease-in-out infinite !important; }
  .pinfo.offline { border-color: rgba(255,107,107,0.45) !important; opacity: 0.72; }
  .pinfo.offline .pname::after { content: ' 📵'; font-size: 0.75em; }
  @keyframes staleBlink { 0%,100%{opacity:1} 50%{opacity:0.25} }
  .pinfo .pname { font-weight: 600; }
  .pinfo .psep  { color: var(--muted); }
  .pinfo .psars { color: var(--muted); }
  /* NAME ONLY on the table. The sars count is answered by the score corner, and
     carrying it here was what made the rotated side pills 35px wide instead of
     the ~18px the layout was drawn for — the count cost more felt than it was
     worth. The elements stay in the DOM because render.js writes to them. */
  .pinfo .psars, .pinfo .psep { display: none; }
  .pinfo .pcrown { display: none; font-size: 1rem; line-height: 1; margin-right: 1px; }
  .pinfo.is-caller .pcrown { display: inline; }
  /* Badges sit UPRIGHT inside the rotated side pills. writing-mode is
     inherited, so the star and the caller's suit were being turned on their
     side along with the name — the name is meant to read vertically, a glyph
     is not. writing-mode alone is NOT enough on the left pill: it is flipped
     with transform: rotate(180deg), and no writing-mode on a child undoes a
     parent's TRANSFORM — the suit rendered upside-down. Counter-rotate it. */
  .pos-left .psenior, .pos-right .psenior,
  .pos-left .pcrown,  .pos-right .pcrown {
    writing-mode: horizontal-tb; text-orientation: mixed;
  }
  .pos-left .psenior, .pos-left .pcrown { transform: rotate(180deg); }
  .pos-left .psenior, .pos-right .psenior { padding: 2px 4px; line-height: 1; }
  .pinfo .psenior { display: none; font-size: 0.68rem; background: rgba(201,162,39,0.12); color: var(--gold); border: 1px solid rgba(201,162,39,0.35); border-radius: 6px; padding: 1px 6px; letter-spacing: 0.04em; }
  .pinfo.is-senior .psenior { display: inline; animation: seniorPulse 1.4s ease-in-out infinite; }
  @keyframes seniorPulse { 0%,100%{opacity:1} 50%{opacity:0.55} }
  @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.3} }
  /* Glow rather than pulse-scale: a pill that changes size nudges the cards
     beside it, and on the side seats it would push into the felt every second. */
  @keyframes turnGlow {
    0%,100% { box-shadow: 0 0 0 1px var(--green) inset, 0 0 0 0 rgba(45,122,89,0.0), 0 2px 10px rgba(0,0,0,0.5); }
    50%     { box-shadow: 0 0 0 1px var(--green) inset, 0 0 16px 3px rgba(74,190,135,0.5), 0 2px 10px rgba(0,0,0,0.5); }
  }
  @media (prefers-reduced-motion: reduce) {
    .pinfo.active { animation: none; box-shadow: 0 0 0 1px var(--green) inset, 0 0 12px 2px rgba(74,190,135,0.45); }
  }

  /* 90px, not 56px: the corner tiles now occupy the top edge. HUD v2 hit
     exactly this and moved it for the same reason. */
  /* 104px. The score tile grew when its padding did, and at 90px the pill's top
     edge met the tile's bottom exactly — the two corner tiles and the top seat
     are all competing for the same 120px, so moving one moves the others. */
  /* 148px. At 104 the pill sat 9px under the score tile with a large void
     below it — everything crowded the top edge and then nothing until the
     trick. This spends that space on the gap that was too tight. */
  .pos-top    { top: calc(148px + var(--sa-t, env(safe-area-inset-top, 0px))); left: 50%; transform: translateX(-50%); }
  /* Short screens: 148px is a quarter of the way down a 568px phone, which puts
     the top seat inside the trick. The gap to the tiles gives way rather than
     the trick — the tiles are read between plays, the trick during them. */
  /* Short-screen overrides live at the END of this file, under ONE breakpoint.
     There used to be a second block here at max-height: 640px while the real
     one was 700px, so between those two values the seat offset silently fell
     back to the full-height value — and the two blocks could not be reconciled
     by reading either of them alone. */
  /* Turned 90° so both sides read outward from the felt. Drops each pill
     from ~62px of width to ~18px, which is where the extra table space
     comes from — more than the bigger Rung corner costs. */
  .pos-left   { left: calc(6px + var(--sa-l, env(safe-area-inset-left, 0px))); top: 50%; transform: translateY(-50%) rotate(180deg); writing-mode: vertical-rl; padding: 9px 4px; }
  .pos-right  { right: calc(6px + var(--sa-r, env(safe-area-inset-right, 0px))); top: 50%; transform: translateY(-50%); writing-mode: vertical-rl; padding: 9px 4px; }
  .pos-bottom { bottom: calc(clamp(120px, 20vw, 158px) + var(--sa-b, env(safe-area-inset-bottom, 0px))); left: 50%; transform: translateX(-50%); }

  /* play area */
  /* The four played cards meet in the middle, as they do in the shipped game.
     An earlier version of this spread them out to sit beside their owners on
     the theory that whose-card-is-whose would read better. Seen next to the
     live layout in hud-lab.html it does not: the tight diamond reads as ONE
     trick — a thing being contested — while the spread reads as four unrelated
     cards, and the centre of the table stops meaning anything. Same numbers as
     play, deliberately, so this is a revert and not a near-miss. */
  .play-area { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); width: clamp(260px, 48vw, 380px); height: clamp(220px, 38vw, 320px); }
  .pslot { position: absolute; width: var(--board-w); height: var(--board-h); }
  .pslot.top    { top: 0; left: 50%; transform: translateX(-50%); }
  .pslot.bottom { bottom: 0; left: 50%; transform: translateX(-50%); }
  .pslot.left   { left: 0; top: 50%; transform: translateY(-50%); }
  .pslot.right  { right: 0; top: 50%; transform: translateY(-50%); }
  /* board cards are larger than hand cards */
  .pslot .card { width: var(--board-w); height: var(--board-h); }
  .pslot .card .csb { font-size: clamp(1.7rem, 3.8vw, 2.4rem); }
  .pslot .card .cc  { font-size: clamp(0.8rem, 1.75vw, 1.05rem); }

  /* cards */
  .card { width: var(--card-w); height: var(--card-h); border-radius: 6px; border: 1px solid rgba(201,162,39,0.2); display: flex; flex-direction: column; align-items: center; justify-content: center; font-weight: 600; position: relative; font-size: 1rem; line-height: 1; box-shadow: 0 2px 10px rgba(0,0,0,0.6); flex-shrink: 0; transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease; cursor: default; -webkit-touch-callout: none; touch-action: manipulation; }
  .card.fup { background: #F0E5CC; }
  /* Card back: phulkari — single centred motif */
  .card.fdown {
    background-color: #2A0810;
    background-image:
      linear-gradient(145deg, #3A0A14 0%, #190509 100%);
    border-color: rgba(201,162,39,0.1);
    box-shadow: 0 2px 10px rgba(0,0,0,0.6);
    overflow: hidden;
  }
  .card.fdown::before {
    content: '';
    position: absolute;
    inset: 5px;
    border-radius: 3px;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 60'%3E%3Cpath d='M30 10 L50 30 L30 50 L10 30 Z' fill='none' stroke='%23C9A227' stroke-width='0.8' opacity='0.25'/%3E%3Cline x1='30' y1='14' x2='30' y2='46' stroke='%23C9A227' stroke-width='0.6' opacity='0.18'/%3E%3Cline x1='14' y1='30' x2='46' y2='30' stroke='%23C9A227' stroke-width='0.6' opacity='0.18'/%3E%3Cline x1='16' y1='16' x2='44' y2='44' stroke='%23C9A227' stroke-width='0.45' opacity='0.1'/%3E%3Cline x1='44' y1='16' x2='16' y2='44' stroke='%23C9A227' stroke-width='0.45' opacity='0.1'/%3E%3Ccircle cx='30' cy='30' r='2' fill='%23C9A227' opacity='0.3'/%3E%3C/svg%3E");
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    pointer-events: none;
  }
  .card.fdown::after { content: ''; position: absolute; inset: 5px; border: 1px solid rgba(201,162,39,0.1); border-radius: 4px; pointer-events: none; }
  .card.red { color: var(--red); }
  .card.black { color: #2a1a18; }
  .card.playable { cursor: pointer; touch-action: manipulation; }
  .card.playable:hover { transform: translateY(-18px) scale(1.04); box-shadow: 0 18px 38px rgba(0,0,0,0.75); border-color: rgba(201,162,39,0.65); z-index: 5; }
  @media (hover: none) { .card.playable:hover { transform: none; box-shadow: 0 2px 8px rgba(0,0,0,0.5); border-color: rgba(201,162,39,0.2); } }
  .card.sel { transform: translateY(-22px) scale(1.05) !important; box-shadow: 0 22px 44px rgba(201,162,39,0.55) !important; border-color: var(--gold) !important; z-index: 20 !important; }
  .card.landed { animation: land 0.28s ease-out; }
  @keyframes land { from{transform:scale(0.65);opacity:0} to{transform:scale(1);opacity:1} }
  .cc { position: absolute; font-size: clamp(0.72rem, 1.55vw, 0.95rem); font-weight: 700; line-height: 1.1; }
  .cc.tl { top: 4px; left: 6px; }
  /* Minimal face at EVERY size (Abu, 2026-09-21 — was compact-only, but a
     wide-viewport phone fell outside the 480px gate and got the busy face):
     one index top-left, one pip anchored to the corner the hidden second
     index freed. The rotated .br index is a physical-table convention — no
     screen is ever read upside-down. cardHTML still emits .br so nothing
     that parses card DOM changes; it just never paints. */
  .cc.br { display: none; }
  .csb { font-size: clamp(1.5rem, 3.2vw, 2rem); position: absolute; right: 7px; bottom: 6px; line-height: 1; }

  /* human hand — cards sit flush at bottom; confirm button floats above without displacing cards */
  .hand-wrap { position: absolute; bottom: 0; left: var(--sa-l, env(safe-area-inset-left, 0px)); right: var(--sa-r, env(safe-area-inset-right, 0px)); height: clamp(115px, 19vw, 150px); display: flex; align-items: flex-end; justify-content: center; padding-bottom: calc(4px + var(--rules-band) + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
  .hand-cards { display: flex; align-items: flex-end; justify-content: center; height: clamp(105px, 17vw, 140px); }
  /* z-index above .card.sel (20); margin tracks the hand's home-indicator
     lift so the bubble stays clear of the cards on notched phones. */
  #play-confirm-btn { position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%); margin-bottom: calc(10px + var(--sa-b, env(safe-area-inset-bottom, 0px))); z-index: 30; white-space: nowrap; background: var(--gold); color: #1a1a0e; border: none; border-radius: 10px; padding: 12px 44px; font-size: 1rem; font-weight: 700; letter-spacing: 0.04em; cursor: pointer; box-shadow: 0 4px 18px rgba(201,168,76,0.45); transition: transform 0.12s ease, box-shadow 0.12s ease; touch-action: manipulation; }
  #play-confirm-btn:active { transform: translateX(-50%) scale(0.95); box-shadow: 0 2px 8px rgba(201,168,76,0.3); }
  /* Count-aware overlap at EVERY size (was compact-only; Abu 2026-09-21):
     renderAll publishes the live card count as --hand-n on #h0, and the
     pitch follows it — a full hand overlaps like a held fan, a 4-card
     endgame hand relaxes to real 6px gaps. The 640px budget keeps a full
     hand reading as a HAND on desktop instead of a metre-wide spread. */
  .hand-cards .card { margin-left: clamp(-28px, calc(min(100vw - 32px, 640px) / var(--hand-n, 13) - var(--card-w)), 6px); transition: transform 0.15s ease-out, box-shadow 0.15s ease, border-color 0.15s ease; will-change: transform; }
  .hand-cards .card.playable:hover, .hand-cards .card.sel { transition: transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1), box-shadow 0.15s ease, border-color 0.15s ease; }
  .hand-cards .card:first-child { margin-left: 0; }
  /* no margin change on sel — layout stays stable so re-tapping a different card lands accurately */

  /* score panel */
  /* Corner score panel — slightly wider/more breathing room than v1 to fit
     the bigger trump symbol and score numbers below. */
  /* The tall left-edge panel is gone — see the corner HUD below. Anything still
     emitting one is hidden rather than left half-styled. */
  .score-panel { display: none; }

  /* ══ CORNER HUD ══════════════════════════════════════════════════════════
     Four tiles in the four corners. Nothing spans an edge, which is what keeps
     the middle of the table clear and is why this survives landscape, where
     the old panel was taller than the entire viewport. */
  .cnr { position: absolute; z-index: 8; background: rgba(10,6,5,0.92); backdrop-filter: blur(8px);
         border: 1px solid rgba(255,255,255,0.13); border-radius: 10px; }

  /* Rung · top-left. Deliberately the biggest thing on the table. */
  /* A generous square, the way the suit picker draws the chosen rung: the glyph
     sits INSIDE a box with real room around it rather than being wrapped tightly
     by one. It governs every card played, so it can afford the space. */
  .cnr-rung { top: calc(9px + var(--sa-t, env(safe-area-inset-top, 0px))); left: calc(9px + var(--sa-l, env(safe-area-inset-left, 0px)));
              min-width: 86px; padding: 8px 11px 11px; border-radius: 16px;
              display: flex; flex-direction: column; align-items: center; gap: 6px;
              border-color: rgba(201,162,39,0.4);
              /* height-matched with the scoreboard — see --hud-tile-h */
              min-height: var(--hud-tile-h); justify-content: center; box-sizing: border-box; }
  /* 3rem, not 1.9rem. The suit picker shows it at 2rem inside a generously
     padded button and the old panel at 4rem in an 88px tile, so 1.9rem in a
     tight corner read as a downgrade from both. It governs every card you
     play — it should be the biggest thing on the table. */
  /* The suit sits in a tinted rounded square, as it does in the shipped game —
     .trump-sym-big is an 88px tile with a suit-coloured wash and border. Scaled
     to the corner rather than reinvented, and no glow: the box carries it. */
  .cnr-sym { font-family: 'Cinzel', serif; font-size: 3.2rem; line-height: 1; font-weight: 700;
             width: 62px; height: 62px; display: flex; align-items: center; justify-content: center;
             border-radius: 14px; border: 1px solid rgba(255,255,255,0.08);
             color: var(--gold-hi, #EDD06A); position: relative;
             transition: color 0.3s, background 0.3s, border-color 0.3s; }
  .cnr-sym.is-red   { background: rgba(181,30,30,0.18);  border-color: rgba(181,30,30,0.35); }
  .cnr-sym.is-black { background: rgba(201,162,39,0.10); border-color: rgba(201,162,39,0.28); }
  .cnr-sym.is-red { color: #E24B4B; }
  .cnr-lb  { font-size: 0.46rem; letter-spacing: 0.2em; color: #8a7a6a; font-weight: 700; text-transform: uppercase; }
  /* Hidden Rung: the caller sees their own suit, so without a marker their
     corner looks identical to a normal round. Ported from .trump-sym-big — the
     badge was bound to the old element and would silently have stopped showing. */
  .cnr-rung { position: absolute; }
  .cnr-sym.is-secret::after {
    content: '🔒'; position: absolute; right: -6px; top: -6px;
    font-size: 0.62rem; line-height: 1; padding: 3px;
    background: #0B0706; border: 1px solid rgba(201,162,39,0.45);
    border-radius: 50%; box-shadow: 0 2px 8px rgba(0,0,0,0.5);
  }
  /* The suit name only renders while the Rung is secret — that is the one
     moment the extra word earns its space. Normal rounds are glyph + label. */
  .cnr-rung .trump-name { font-family: 'Cinzel', serif; font-size: 0.46rem; color: #F0E5CC;
                          letter-spacing: 0.04em; margin-top: 1px; text-align: center; }

  /* Tricks + series · top-right. Same corner because they are the same
     question — "how am I doing" — at two time scales. */
  /* One shared height for the two top tiles — the strip only reads as a
     designed pair when the Rung tile and the scoreboard match (Abu,
     2026-08-25). The scoreboard's natural stack sets the number; the Rung
     tile centres up to it. */
  :root { --hud-tile-h: 112px; }
  .cnr-score { top: calc(9px + var(--sa-t, env(safe-area-inset-top, 0px))); right: calc(9px + var(--sa-r, env(safe-area-inset-right, 0px))); padding: 8px 9px; cursor: pointer;
               display: flex; flex-direction: column; align-items: stretch; gap: 4px; min-width: 148px;
               min-height: var(--hud-tile-h); justify-content: center; box-sizing: border-box; }
  .cnr-score .match-mini { margin: 4px 0 0; padding: 4px 6px 0; }

  /* ── Banner scorecard (2026-08-25) ──────────────────────────────────────
     The a2d2bd8 score rows, compacted to match the Rung tile's height
     (~100px): team colour answers "who's ahead" faster than any font size.
     render.js keeps its old contract — sv1/sv2 text, winning on st1/st2. */
  .sc-banner { display: flex; justify-content: space-between; align-items: center;
               gap: 12px; border-radius: 7px; padding: 3px 11px; }
  .sc-b-us   { background: rgba(201,162,39,0.08); border: 1px solid rgba(201,162,39,0.22); }
  .sc-b-them { background: rgba(107,23,40,0.16);  border: 1px solid rgba(107,23,40,0.35); }
  .sc-b-team { font-family: 'Cinzel', serif; font-size: 0.78rem; letter-spacing: 0.08em;
               color: var(--gold, #C9A227); }
  .sc-b-them .sc-b-team { color: #E08080; }
  .sc-b-n    { font-family: 'Cinzel', serif; font-size: 1.4rem; font-weight: 700;
               line-height: 1; color: var(--gold-hi, #EDD06A); }
  .sc-b-them .sc-b-n { color: #E08080; }
  .sc-banner.winning.sc-b-us   .sc-b-n { text-shadow: 0 0 10px rgba(237,208,106,0.5); }
  .sc-banner.winning.sc-b-them .sc-b-n { text-shadow: 0 0 10px rgba(224,128,128,0.45); }
  .cnr-score .match-mini { justify-content: center; }
  /* Phones: tighter pair, so the rules row below the tiles still clears the
     top seat's name pill. AFTER the base rules — same specificity, cascade
     order is the override mechanism here. */
  @media (max-width: 699.98px) {
    :root { --hud-tile-h: 100px; }
    /* Slimmer than the desktop numbers ON PURPOSE: the scoreboard's width is
       what pushes the rules column off the screen's centreline (the Rung
       tile is narrower). At ~112px wide the pills can sit at symmetric
       insets and line up dead-centre with the top seat's name pill (Abu,
       2026-08-26). */
    .sc-b-n { font-size: 1.15rem; }
    .sc-b-team { font-size: 0.62rem; }
    .sc-banner { padding: 1px 10px; }
    .cnr-score { min-width: 112px; padding: 5px 7px; gap: 3px; }
    .cnr-score .match-mini { margin: 2px 0 0; padding: 3px 4px 0; font-size: 0.6rem; }
    .cnr-score .match-mini .mm-label { font-size: 0.5rem; }
    .cnr-score .match-mini .mm-target { font-size: 0.56rem; }
  }
  .cnr-k     { font-size: 0.38rem; letter-spacing: 0.18em; color: #8a7a6a; text-transform: uppercase; font-weight: 700; }
  /* ── Round score: four treatments, switchable from hud-lab.html ───────────
     RETIRED from the live table 2026-08-25 — the banner scorecard above owns
     the shipped markup (st1/st2 no longer carry .cnr-rd/.cnr-us). These rules
     stay for hud-lab.html, whose fixtures still use the old structure.
     Us-Them is the one number that changes every trick. The question is only
     how loudly to draw it next to a label that never changes and a series line
     that changes once a round. Set .sc-rows / .sc-plain / .sc-split / .sc-pill
     on #game; rows is the default. All four read the same elements, so none of
     them can show a different number from the others. */
  .cnr-rd    { font-size: 0.92rem; font-weight: 800; line-height: 1;
               display: inline-flex; align-items: baseline; gap: 4px; }

  /* ROWS — one line per team, label left, number right. Closest to the layout
     that shipped, where the two teams were separate blocks. */
  .sc-rows .cnr-rd { display: grid; grid-template-columns: auto auto; gap: 1px 8px;
                     align-items: baseline; justify-items: end; font-size: 1.02rem; }
  .sc-rows .cnr-rd .cnr-side:first-of-type { grid-column: 1; grid-row: 1; }
  .sc-rows .cnr-us   { grid-column: 2; grid-row: 1; }
  .sc-rows .cnr-dash { display: none; }
  .sc-rows .cnr-rd .cnr-side:last-of-type  { grid-column: 1; grid-row: 2; }
  .sc-rows .cnr-them { grid-column: 2; grid-row: 2; }

  /* PLAIN — one line, no box. Quietest. */
  .sc-plain .cnr-rd { font-size: 0.98rem; }

  /* SPLIT — two cells with a hairline between, like a scoreboard. */
  .sc-split .cnr-rd { gap: 0; align-items: stretch; font-size: 0.98rem;
                      border: 1px solid rgba(255,255,255,0.12); border-radius: 8px; overflow: hidden; }
  .sc-split .cnr-rd > * { display: flex; align-items: center; padding: 3px 7px; }
  .sc-split .cnr-rd .cnr-side { background: rgba(255,255,255,0.04); font-size: 0.44rem; }
  .sc-split .cnr-us   { border-right: 1px solid rgba(255,255,255,0.12); }
  .sc-split .cnr-dash { display: none; }

  /* PILL — bordered and tinted. */
  .sc-pill .cnr-rd { padding: 5px 11px 6px; border-radius: 999px;
                     background: linear-gradient(180deg, rgba(201,162,39,0.14), rgba(201,162,39,0.06));
                     border: 1px solid rgba(201,162,39,0.32);
                     box-shadow: inset 0 1px 0 rgba(255,255,255,0.05), 0 2px 8px rgba(0,0,0,0.4); }
  /* Named sides, and legible ones. At 0.4rem in the muted label colour they
     were technically present and practically invisible — the tile still read as
     "7 – 4" and you still had to remember which side was yours. Bigger, brighter,
     and the two numbers no longer share a colour: yours is gold, theirs is
     plain, so the tile can be read without reading the words at all. */
  .cnr-side  { font-size: 0.5rem; letter-spacing: 0.1em; text-transform: uppercase;
               color: rgba(240,229,204,0.72); font-weight: 800; }
  .cnr-us    { color: var(--gold-hi, #EDD06A); }
  .cnr-them  { color: rgba(255,255,255,0.86); }
  .cnr-dash  { color: rgba(255,255,255,0.3); margin: 0 1px; }
  .cnr-us.winning  { text-shadow: 0 0 10px rgba(237,208,106,0.5); }
  .cnr-them.winning { text-shadow: 0 0 10px rgba(255,255,255,0.35); }
  /* Bottom-left by default, built like the score corner — small caps label over
     a bright value — so the two tiles read as the same kind of object. */
  .cnr-trick { bottom: calc(clamp(120px, 20vw, 158px) + var(--sa-b, env(safe-area-inset-bottom, 0px))); left: calc(9px + var(--sa-l, env(safe-area-inset-left, 0px)));
               padding: 6px 11px 7px; white-space: nowrap; text-align: center; }
  .cnr-tv { font-size: 0.72rem; font-weight: 800; color: #F0E5CC; line-height: 1.15; }
  .cnr-tv .sep  { color: rgba(255,255,255,0.3); margin: 0 1px; }
  .cnr-tv .tot  { color: #9d8f7c; font-weight: 700; }

  /* The other placement — tucked under the score corner — kept switchable from
     hud-lab.html. Same element in both, so the trick number is never written
     twice and the two options cannot disagree. */
  #game:not(.tk-bottomleft) .cnr-trick {
    top: calc(97px + var(--sa-t, env(safe-area-inset-top, 0px))); right: 9px; bottom: auto; left: auto;
    padding: 4px 10px; font-size: 0.46rem;
  }
  .cnr-sr    { font-size: 0.46rem; color: #9d8f7c; white-space: nowrap; }
  .cnr-sr b  { color: var(--gold-hi, #EDD06A); font-weight: 800; }
  /* Before the Double Sar threshold there is nothing to show. One muted dash and
     the trick it opens on, rather than "— – —". */
  .scores-hidden .cnr-them, .scores-hidden .cnr-dash { display: none; }
  .scores-hidden .cnr-rd { color: #6b6055; font-size: 0.6rem; }
  .scores-hidden .cnr-rd::after { content: ' from trick ' attr(data-from); font-size: 0.42rem; letter-spacing: .04em; }

  /* Overflow · bottom-right */
  /* 44×44 minimum. It measured 35×22 — the smallest target on the table, and
     the only way to reach Quit, Rules, Log, Sound and Surrender now that those
     four buttons are behind it. The glyph stays small; the box does not. */
  .cnr-dots { bottom: calc(clamp(120px, 20vw, 158px) + var(--sa-b, env(safe-area-inset-bottom, 0px))); right: calc(9px + var(--sa-r, env(safe-area-inset-right, 0px)));
              min-width: 44px; min-height: 44px; padding: 0; display: grid; place-items: center;
              font-size: 0.72rem; color: #8a7a6a; font-weight: 800; letter-spacing: 1px;
              font-family: inherit; cursor: pointer; line-height: 1; }
  .cnr-dots:hover { color: var(--gold); border-color: rgba(201,162,39,0.4); }

  /* Above .voice-btn and .react-btn, which are position:fixed at z-index 30 —
     at 20 the menu opened underneath them. */
  .hud-menu-backdrop { position: fixed; inset: 0; z-index: 59; }
  .hud-menu { position: absolute; z-index: 60; right: calc(9px + var(--sa-r, env(safe-area-inset-right, 0px)));
              bottom: calc(clamp(150px, 20vw + 30px, 188px) + var(--sa-b, env(safe-area-inset-bottom, 0px)));
              background: rgba(10,6,5,0.97); backdrop-filter: blur(10px);
              border: 1px solid rgba(255,255,255,0.13); border-radius: 12px; padding: 5px;
              display: flex; flex-direction: column; gap: 1px; min-width: 156px;
              box-shadow: 0 10px 34px rgba(0,0,0,0.6); }
  .hud-mi { background: none; border: none; color: var(--text); font-family: inherit; font-size: 0.78rem;
            text-align: left; padding: 9px 12px; border-radius: 8px; cursor: pointer; }
  .hud-mi:hover  { background: rgba(255,255,255,0.06); }
  .hud-mi.danger { color: var(--red); }
  .hud-mi.is-off { color: var(--muted); }
  .hud-brief { font-size: 0.56rem; color: #8a7a6a; line-height: 1.4; padding: 7px 12px 8px;
               border-top: 1px solid rgba(255,255,255,0.07); margin-top: 3px; cursor: pointer; }
  .tricks-row { display: flex; align-items: center; justify-content: space-between; margin-top: 2px; }
  .tricks-label { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.12em; color: var(--muted); }
  .tricks-val { font-family: 'Cinzel', serif; font-size: 0.82rem; font-weight: 600; color: var(--text); }
  .score-divider { border: none; border-top: 1px solid rgba(255,255,255,0.06); margin: 8px 0 8px; }
  .score-row { display: flex; justify-content: space-between; align-items: center; border-radius: 6px; padding: 8px 12px; margin-bottom: 5px; gap: 10px; }
  .score-row:last-of-type { margin-bottom: 0; }
  .score-us   { background: rgba(201,162,39,0.06);  border: 1px solid rgba(201,162,39,0.16); }
  .score-them { background: rgba(107,23,40,0.12);   border: 1px solid rgba(107,23,40,0.28); }
  .score-team { font-family: 'Cinzel', serif; font-size: 0.72rem; color: var(--gold); letter-spacing: 0.1em; flex: 1; }
  .score-team.them { color: #E08080; }
  .score-n { font-family: 'Cinzel', serif; font-size: 1.9rem; font-weight: 700; line-height: 1; color: var(--gold); min-width: 2ch; text-align: right; }
  .score-them .score-n { color: #E08080; }
  .score-label { display: none; }
  /* keep .steam for any legacy uses */
  .steam { display: flex; justify-content: space-between; gap: 20px; margin-bottom: 4px; }
  .steam .sname { color: var(--muted); font-size: 0.72rem; }
  .steam .sval  { font-weight: 700; font-family: 'Cinzel', serif; font-size: 0.85rem; }

  /* ── Active rules + turn timer: one band at the bottom edge ──────────────
     The rules used to live two taps into the ⋯ menu, where players stopped
     finding them and started asking mid-game which ruleset they were on. This
     puts them on the table, directly above the timer so the bar reads as the
     line's underline rather than as a second thing competing for the edge.

     Pills, not one run of text: the line is 35 characters on default rules but
     99 when every rule is non-default, and chips wrap onto a second row rather
     than truncating a rule in half.

     --rules-band is the height the hand has to give back. It is a variable
     because that reservation is repeated per orientation, and the two drifting
     apart is how the cards end up sitting on top of the pills. */
  :root { --rules-band: 20px; }
  .rules-strip { position: absolute; left: 0; right: 0; bottom: 5px; display: flex;
                 flex-wrap: wrap; justify-content: center; align-items: flex-end;
                 gap: 4px; padding: 0 10px; z-index: 5; cursor: pointer; }
  /* The chips themselves are .rule-pill, the same class the waiting-room card
     uses — a player meets these exact chips before the deal and then sees them
     again on the table, red where the room changed a rule. Only the size is
     the strip's business; everything else is deliberately inherited. */
  .rules-strip .rule-pill { font-size: 0.68rem; padding: 3px 9px; }
  /* The pills live at the TOP everywhere (player ask, 2026-08-25 — the
     bottom edge is crowded by the hand). Two placements, one idea:
     wide screens have a gap BETWEEN the Rung tile and the scoreboard;
     phones don't (the two tiles own the strip), so there the row sits
     full-width just UNDER the tile pair — clear of the top seat's pill,
     verified live at 375px. */
  @media (min-width: 700px) {
    .rules-strip { bottom: auto; top: calc(14px + var(--sa-t, env(safe-area-inset-top, 0px)));
                   left: 130px; right: 200px; align-items: flex-start; }
    .rules-strip .rule-pill { font-size: 0.62rem; }
  }
  @media (max-width: 699.98px) {
    /* Phones: the pills FILL the box between the Rung tile and the
       scoreboard (Abu, 2026-08-26) — a centred column, one rule per line,
       the same height as the tile pair so the whole top strip reads as one
       band. _fitRulesStrip trims by HEIGHT here (column mode), not by row. */
    .rules-strip { bottom: auto; top: calc(9px + var(--sa-t, env(safe-area-inset-top, 0px)));
                   left: 126px; right: 126px; height: var(--hud-tile-h);
                   flex-direction: column; flex-wrap: nowrap;
                   justify-content: center; align-items: center;
                   gap: 3px; padding: 0 2px; overflow: hidden; }
    .rules-strip .rule-pill { font-size: 0.5rem; padding: 1px 6px; max-width: 100%;
                              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  }
  @media (max-width: 349.98px) {
    /* Tiny phones (320-class): the between-tiles box is ~76px wide there —
       six rules become ellipsis soup and the column spills behind the
       scoreboard (tested live at 320x568, 2026-08-26). Fall back to the
       full-width row just under the tile pair; the row-trimmer handles
       overflow honestly. 350+ keeps the box (~106px wide there). */
    .rules-strip { top: calc(15px + var(--hud-tile-h) + var(--sa-t, env(safe-area-inset-top, 0px)));
                   left: 0; right: 0; height: auto;
                   flex-direction: row; flex-wrap: wrap;
                   justify-content: center; align-items: flex-start;
                   overflow: visible; padding: 0 8px; }
    .rules-strip .rule-pill { font-size: 0.54rem; padding: 2px 7px; max-width: none; }
  }
  /* The trimmed-rules counter: gold, because it is the one chip that is a door
     rather than a fact. */
  .rules-strip .rule-pill.more { color: var(--gold-hi); border-color: rgba(201,162,39,0.45);
                                 background: rgba(201,162,39,0.11); }

  /* ── The row that sits just above the hand rises with it ─────────────────
     The strip gave the hand --rules-band of its height back, which lifts the
     CARDS. Five things are parked immediately above them — the trick tile, your
     name pill, the ⋯ button, and the two FABs — and each carries its own copy
     of the hand-height expression rather than sharing one. Leaving them put is
     what pushed the card tops under your name pill.

     One margin-bottom instead of editing six `bottom:` values across four media
     queries: on an absolutely (or fixed) positioned element it shifts the box
     up by exactly that much, whatever `bottom` each rule happens to set. So the
     row tracks the band everywhere, including breakpoints written later. */
  .pos-bottom, .cnr-trick, .cnr-dots,
  .voice-btn,  .react-btn { margin-bottom: var(--rules-band); }
  /* Native shell floats the timer inside the safe zone, so the strip clears it there. */
  body.native-shell .rules-strip { bottom: calc(13px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }

  /* turn timer bar */
  .timer-bar-wrap { position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background: rgba(255,255,255,0.06); z-index: 5; pointer-events: none; }
  /* Native app: curved screen corners clip a flush-bottom full-width bar —
     float it inside the safe zone with rounded ends instead. Web unchanged. */
  body.native-shell .timer-bar-wrap { left: 14px; right: 14px; bottom: calc(6px + var(--sa-b, env(safe-area-inset-bottom, 0px))); height: 4px; border-radius: 2px; overflow: hidden; }
  .timer-bar { height: 100%; width: 100%; background: var(--gold); transform-origin: left; }
  .timer-bar.urgent { background: var(--red); }

  /* quit button */
  .quit-btn { display: none; }
  .quit-btn:hover { border-color: rgba(224,85,85,0.6); color: var(--red); }

  /* top bar */

  /* trump picker modal */
  .overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.87); display: flex; align-items: center; justify-content: center; z-index: 100; backdrop-filter: blur(4px); }
  .overlay.hidden { display: none; }
  .modal { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 28px; text-align: center; max-width: 340px; width: 92%; animation: mIn 0.28s ease-out; }
  @keyframes mIn { from{transform:scale(0.88);opacity:0} to{transform:scale(1);opacity:1} }
  .modal h2 { font-family: 'Cinzel', serif; font-size: 1.55rem; font-weight: 700; color: var(--gold); margin-bottom: 6px; letter-spacing: 0.06em; }
  .modal p  { color: var(--muted); font-size: 0.82rem; margin-bottom: 18px; line-height: 1.55; }
  .modal-hand { display: flex; gap: 6px; justify-content: center; margin-bottom: 18px; flex-wrap: wrap; }
  /* Rung picker takeover (2026-08-28): the pick owns the screen. The overlay
     backdrop goes opaque felt instead of a dimmed sheet, and the old boxed
     .modal chrome is replaced by a free-standing column. */
  /* .overlay.trump-takeover, not bare .trump-takeover: the base .overlay rule
     later in this file wins the equal-specificity race and leaves the felt
     translucent — the game chrome bled through the "full-screen" pick. */
  .overlay.trump-takeover { background: radial-gradient(ellipse at 50% 30%, var(--felt-mid) 0%, var(--felt-deep) 70%); z-index: 340; }
  .trump-inner { width: min(94vw, 420px); text-align: center; }
  .trump-inner .modal-hand { flex-wrap: nowrap; }
  .trump-inner .modal-hand .card { width: min(var(--card-w), 17vw); }
  .trump-round { font-size: 0.68rem; letter-spacing: 0.22em; color: var(--gold); margin-bottom: 4px; }
  .trump-title { font-family: 'Cinzel', serif; font-size: 1.5rem; color: var(--gold-hi); margin: 0 0 4px; }
  .trump-inner #trump-desc { font-size: 0.8rem; color: var(--muted); margin: 0 0 10px; }
  .trump-inner .modal-hand { justify-content: center; margin-bottom: 14px; }
  .redeal-banner { border: 1px solid rgba(201,162,39,0.5); background: rgba(201,162,39,0.1); border-radius: 10px; padding: 8px 10px; margin: 0 0 14px; }
  .redeal-banner p { font-size: 0.78rem; color: var(--gold); margin: 0 0 8px; }
  .redeal-btn { background: rgba(201,162,39,0.25); border: 1px solid var(--gold); color: var(--gold-hi); border-radius: 8px; padding: 8px 20px; font-size: 0.85rem; font-family: inherit; font-weight: 600; cursor: pointer; }
  .redeal-btn:active { transform: scale(0.97); }
  .scount { color: var(--gold); margin-left: 5px; font-size: 0.72rem; }
  .redeal-proof { display: flex; justify-content: center; gap: 6px; margin: 14px 0 10px; }
  .redeal-note { font-size: 0.8rem; color: var(--muted); }

  .suit-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; max-width: 280px; margin: 0 auto; }
  .sbtn { background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 16px 10px; cursor: pointer; font-family: inherit; color: var(--text); transition: all 0.18s; display: flex; flex-direction: column; align-items: center; gap: 3px; }
  .sbtn:hover { border-color: var(--gold); transform: translateY(-2px); background: rgba(201,162,39,0.06); }
  .sbtn .si { font-size: 2rem; }
  .sbtn .sn { font-size: 0.78rem; color: var(--muted); }
  .sbtn.red .si { color: var(--red); }

  /* toast */
  /* ── Toast banners ──
     Anchored to the TOP, over the game chrome — never the trick, never the
     hand, and clear of the Play Card button that appears at ~160px. Stacked,
     so a second message no longer destroys the first. */
  /* At the top edge a toast sits directly on both corner tiles — the Rung and
     the score, the two things you most need mid-trick. The corner HUD emptied
     the upper-middle of the felt, so in-game they drop into that band instead:
     below the top player's fan, above the played cards. Every other screen
     keeps the top edge, where nothing is behind them. */
  body:has(#game:not(.hidden)) .toast-stack { top: 27%; }
  .toast-stack { position: fixed; top: calc(6px + var(--sa-t, env(safe-area-inset-top, 0px))); left: calc(8px + var(--sa-l, env(safe-area-inset-left, 0px))); right: calc(8px + var(--sa-r, env(safe-area-inset-right, 0px))); z-index: 300; display: flex; flex-direction: column; align-items: center; gap: 6px; pointer-events: none; }
  .toast { display: flex; align-items: flex-start; gap: 8px; width: auto; max-width: min(86%, 320px); padding: 9px 11px; background: rgba(12,8,7,0.97); border: 1px solid var(--border); border-radius: 11px; box-shadow: 0 6px 22px rgba(0,0,0,0.55); font-size: 0.82rem; font-weight: 600; color: var(--text); text-align: left; cursor: pointer; pointer-events: auto; opacity: 0; transform: translateY(-10px) scale(0.97); transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.2,0.9,0.3,1.2); }
  .toast.show { opacity: 1; transform: translateY(0) scale(1); }
  .toast-ico { flex: 0 0 auto; width: 17px; height: 17px; border-radius: 50%; display: grid; place-items: center; font-size: 0.64rem; font-weight: 800; margin-top: 1px; background: rgba(255,255,255,0.1); color: var(--muted); }
  /* pre-line: a toast message may carry newlines (the watcher list puts each
     name on its own row). Every other toast is single-line and unaffected. */
  .toast-msg { flex: 1; min-width: 0; line-height: 1.35; white-space: pre-line; }
  .toast-x { flex: 0 0 auto; background: none; border: none; color: var(--muted); font-size: 0.8rem; cursor: pointer; padding: 0 2px; line-height: 1; margin-top: 2px; font-family: inherit; }
  .toast.gold  { border-color: rgba(201,162,39,0.5); }
  .toast.gold  .toast-ico { background: rgba(201,162,39,0.18); color: var(--gold-hi); }
  .toast.green { border-color: rgba(76,175,125,0.5); }
  .toast.green .toast-ico { background: rgba(76,175,125,0.18); color: #7fd8a8; }
  .toast.red   { border-color: rgba(181,30,30,0.62); background: rgba(26,8,8,0.97); }
  .toast.red   .toast-ico { background: rgba(181,30,30,0.28); color: #ff9d9d; }
  @media (prefers-reduced-motion: reduce) {
    .toast { transition: opacity 0.01ms; transform: none; }
    .toast.show { transform: none; }
  }

  /* ── Rung announcement ──
     The only message that takes the centre of the table. Allowed here and
     nowhere else because trump is called before any card is played, so the
     middle is empty at exactly this moment. */
  .rung-hero { position: fixed; top: 46%; left: 50%; transform: translate(-50%,-50%) scale(0.86); z-index: 310; display: flex; flex-direction: column; align-items: center; gap: 2px; background: rgba(10,6,5,0.95); border: 1px solid rgba(201,162,39,0.55); border-radius: 18px; padding: 20px 38px; box-shadow: 0 12px 44px rgba(0,0,0,0.7), 0 0 46px rgba(201,162,39,0.14); pointer-events: none; opacity: 0; transition: opacity 0.3s ease, transform 0.3s cubic-bezier(0.2,0.9,0.3,1.3); }
  .rung-hero.show { opacity: 1; transform: translate(-50%,-50%) scale(1); }
  .rung-hero-label { font-size: 0.58rem; letter-spacing: 0.24em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
  .rung-hero-suit { font-family: 'Cinzel', serif; font-size: 3.6rem; line-height: 1.06; font-weight: 700; color: var(--text); }
  .rung-hero-suit.red { color: #E24B4B; }
  .rung-hero-who { font-size: 0.68rem; color: rgba(240,229,204,0.75); }
  @media (prefers-reduced-motion: reduce) {
    .rung-hero { transition: opacity 0.01ms; transform: translate(-50%,-50%); }
    .rung-hero.show { transform: translate(-50%,-50%); }
  }

  /* end screen */
  /* justify-content: flex-start, NOT the .screen default center: a centred
     flex column taller than the viewport overflows BOTH ways, and the top
     half is unreachable by scrolling — on an iPhone SE the COURT! headline
     started 47px above the screen (measured live, 2026-09-19). Anchored to
     the top, tall content scrolls honestly; .end-inner's margin:auto still
     centres it whenever it is shorter than the screen. */
  #end-screen { background: radial-gradient(ellipse at center, #220A10 0%, var(--bg) 70%); z-index: 50; overflow-y: auto; -webkit-overflow-scrolling: touch; align-items: flex-start; justify-content: flex-start; touch-action: pan-y; overscroll-behavior: contain; }

  /* ── MATCH END SCREEN ──────────────────────────────────────────────────
     Shown after a team crosses the target. Big celebratory headline,
     final score, condensed round-by-round history, "Play next match" CTA. */
  #match-end-screen {
    background:
      radial-gradient(ellipse at 50% 0%, rgba(201,162,39,0.22) 0%, transparent 55%),
      radial-gradient(ellipse at center, #220A10 0%, var(--bg) 70%);
    z-index: 60;
    overflow-y: auto;
    align-items: flex-start;
    justify-content: flex-start;   /* same top-clip fix as #end-screen */
    -webkit-overflow-scrolling: touch;
    touch-action: pan-y;
    overscroll-behavior: contain;
  }
  .match-end-inner {
    text-align: center;
    padding: calc(40px + var(--sa-t, env(safe-area-inset-top, 0px))) 24px calc(32px + var(--sa-b, env(safe-area-inset-bottom, 0px)));
    max-width: 400px;
    width: 100%;
    flex-shrink: 0;
    margin: auto;
  }
  /* Single centered row. Was a letter-spaced text string that wrapped to two
     lines and sat the trophy emoji higher than the suit glyphs; flex with
     align-items:center puts every glyph on a shared centre line. */
  .match-end-confetti { display: flex; justify-content: center; align-items: center; gap: 0.5em; flex-wrap: nowrap; font-size: 2.1rem; line-height: 1; margin-bottom: 10px; }
  .match-end-confetti .mec-suit { color: rgba(240,229,204,0.9); }
  .match-end-confetti .mec-cup  { font-size: 1.05em; }
  .match-end-headline {
    font-family: 'Cinzel', serif;
    font-size: clamp(2rem, 8vw, 2.8rem);
    font-weight: 700;
    letter-spacing: 0.08em;
    line-height: 1.1;
    margin-bottom: 6px;
  }
  .match-end-headline.win  { color: var(--gold); text-shadow: 0 4px 28px rgba(201,162,39,0.45); }
  .match-end-headline.loss { color: rgba(255,255,255,0.78); }
  .match-end-sub {
    font-size: 0.78rem;
    color: var(--muted);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    margin-bottom: 24px;
  }
  .match-end-scores {
    display: flex; align-items: center; justify-content: center;
    gap: 16px;
    margin: 0 auto 24px;
    padding: 18px 14px;
    background: rgba(255,255,255,0.03);
    border: 1px solid rgba(255,255,255,0.08);
    border-radius: 14px;
    max-width: 320px;
  }
  .met-team    { flex: 1; }
  .met-name    { font-size: 0.7rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 6px; }
  .met-val     { font-family: 'Cinzel', serif; font-size: 2.2rem; font-weight: 700; line-height: 1; }
  .met-us .met-val   { color: var(--gold); }
  .met-them .met-val { color: rgba(255,255,255,0.62); }
  .met-dash    { color: rgba(255,255,255,0.25); font-size: 1.4rem; }
  .match-end-rounds {
    margin: 0 auto 22px;
    max-width: 320px;
  }
  .met-rounds-label {
    font-size: 0.62rem;
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    margin-bottom: 8px;
  }
  .met-rounds-list { display: flex; flex-direction: column; gap: 4px; }
  .met-round-row {
    display: grid;
    grid-template-columns: 32px 1fr auto;
    align-items: center;
    gap: 10px;
    padding: 6px 10px;
    background: rgba(255,255,255,0.025);
    border: 1px solid rgba(255,255,255,0.05);
    border-radius: 8px;
    font-size: 0.78rem;
  }
  .met-round-row .mr-num   { color: var(--muted); font-weight: 700; text-align: center; }
  .met-round-row .mr-label { color: var(--text); }
  .met-round-row .mr-pts   { color: var(--gold-hi, #edd06a); font-weight: 700; }
  .met-round-row.win-us  .mr-label  { color: var(--gold-hi, #edd06a); }
  .met-round-row.win-them .mr-pts   { color: rgba(255,255,255,0.55); }
  .end-inner  { text-align: center; padding: calc(32px + var(--sa-t, env(safe-area-inset-top, 0px))) 24px calc(32px + var(--sa-b, env(safe-area-inset-bottom, 0px))); max-width: 380px; width: 100%; margin: auto; flex-shrink: 0; }
  .end-outcome { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.2em; margin-bottom: 8px; }
  .end-outcome.court     { color: var(--gold); }
  .end-outcome.gooncourt { color: var(--red); }
  .end-outcome.pees      { color: var(--muted); }
  .end-title { font-family: 'Cinzel', serif; font-size: 2.6rem; font-weight: 700; margin-bottom: 6px; letter-spacing: 0.05em; }
  .end-title.win  { color: var(--gold); }
  .end-title.lose { color: var(--text); }
  .end-shame { font-size: 0.8rem; font-weight: 700; margin-bottom: 6px; padding: 6px 14px; border-radius: 20px; display: inline-block; }
  .end-shame.court     { background: rgba(201,168,76,0.12); color: var(--gold); border: 1px solid rgba(201,168,76,0.35); }
  .end-shame.gooncourt { background: rgba(224,85,85,0.12);  color: var(--red);  border: 1px solid rgba(224,85,85,0.35); }
  .end-shame.pees      { background: rgba(122,126,142,0.12);color: var(--muted);border: 1px solid rgba(122,126,142,0.25); }
  .end-sub  { color: var(--muted); margin: 10px 0 20px; font-size: 0.85rem; line-height: 1.6; }
  .end-scores { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 16px; margin-bottom: 20px; }
  .eteam { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid var(--border); }
  .eteam:last-child { border-bottom: none; }
  .eteam .etname { color: var(--muted); font-size: 0.82rem; }
  .eteam .etval  { font-size: 1.7rem; font-weight: 700; }
  .eteam.won .etval { color: var(--gold); }
  .eteam.court-loss .etname::after { content: ' · Court'; color: var(--gold); font-size: 0.7rem; }
  .eteam.goon-loss  .etname::after { content: ' · Goon Court'; color: var(--red); font-size: 0.7rem; }
  .pees-note { font-size: 0.78rem; color: var(--muted); text-align: center; margin-top: 8px; }
  .pees-note strong { color: var(--text); }

  /* ── RULES MODAL ── */
  .rules-modal { max-width: 340px; max-height: 78vh; overflow-y: auto; padding: 0; text-align: left; }
  .rules-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px 12px; border-bottom: 1px solid var(--border); position: sticky; top: 0; background: var(--surface); border-radius: 14px 14px 0 0; }
  .rules-header h3 { font-size: 1rem; font-weight: 700; color: var(--gold); margin: 0; font-family: 'Cinzel', serif; letter-spacing: 0.05em; }
  .rules-close { background: none; border: none; color: var(--muted); font-size: 1.1rem; cursor: pointer; padding: 2px 6px; line-height: 1; }
  .rules-body { padding: 14px 20px 20px; }
  .rules-section { margin-bottom: 13px; }
  .rules-section:last-child { margin-bottom: 0; }
  .rules-stitle { font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.1em; color: var(--gold); font-weight: 700; margin-bottom: 4px; }
  .rules-section p { font-size: 0.81rem; color: var(--muted); line-height: 1.55; }
  .rules-section p strong { color: var(--text); }
  /* Rules button in game top-right */
  .rules-btn { display: none; }
  .rules-btn:hover { border-color: var(--gold); color: var(--gold); }
  /* ── Voice / Reaction labeled pill buttons ── */
  .v-label { font-size: 0.52rem; text-transform: uppercase; letter-spacing: 0.08em; line-height: 1; display: block; color: var(--muted); }
  .voice-btn { position: fixed; bottom: calc(clamp(120px, 20vw, 158px) + 110px + var(--sa-b, env(safe-area-inset-bottom, 0px))); right: calc(8px + var(--sa-r, env(safe-area-inset-right, 0px))); background: var(--surface); border: 1px solid rgba(255,255,255,0.18); border-radius: 14px; width: auto; height: auto; padding: 9px 12px; color: rgba(255,255,255,0.7); cursor: pointer; z-index: 30; box-shadow: 0 2px 12px rgba(0,0,0,0.5); display: flex; flex-direction: column; align-items: center; gap: 4px; transition: color 0.15s, border-color 0.15s, box-shadow 0.15s; }
  .voice-btn:hover  { color: rgba(255,255,255,0.95); border-color: rgba(255,255,255,0.35); }
  .voice-btn.v-active { color: #4ade80; border-color: rgba(74,222,128,0.5); box-shadow: 0 0 12px rgba(74,222,128,0.25); }
  .voice-btn.v-active .v-label { color: #4ade80; }
  .voice-btn.v-muted  { color: #F59E0B; border-color: rgba(245,158,11,0.45); }
  .voice-btn.v-muted .v-label  { color: #F59E0B; }
  /* Voice indicator on player labels */
  .vind { display: inline-flex; align-items: center; margin-left: 4px; vertical-align: middle; line-height: 1; }
  .vind.hidden { display: none !important; }
  .vind.vi-on    { color: #4ade80; }
  .vind.vi-muted { color: #F59E0B; }
  /* Pot inline badge next to Trick count */
  .pot-badge { font-size: 0.6rem; font-weight: 700; color: var(--gold); background: rgba(201,168,76,0.12); border: 1px solid rgba(201,168,76,0.28); border-radius: 4px; padding: 1px 4px; margin-left: 4px; vertical-align: middle; }
  /* sarTurn target arrow (before unlock) */
  .sar-target { font-size: 0.6rem; color: var(--muted); margin-left: 3px; }

  @media (max-width: 520px) {
    /* Taller, slightly wider hand cards for easier reading on mobile —
       overlap tracks the live count (--hand-n), same law as the base rule. */
    .hand-cards .card             { width: 58px; height: 100px; margin-left: clamp(-38px, calc((100vw - 32px) / var(--hand-n, 13) - 58px), 6px); }
    .hand-cards .card:first-child { margin-left: 0; }
    /* sel card keeps same margin — no layout shift on mobile */
    /* Scale up centre suit symbol and corner rank/suit for the player's hand */
    .hand-cards .card .csb        { font-size: 2.5rem; }
    .hand-cards .card .cc         { font-size: 0.95rem; }
    /* Taller mobile cards — hand-wrap just fits them flush at bottom */
    .hand-wrap                    { height: 110px; }
    .hand-cards                   { height: 106px; }
    /* Compact trump/score panel — label visible, symbol slightly larger */
    /* Mobile sizes — bigger than v1, slightly tighter than desktop */
    .trump-sym-big  { font-size: 3rem; width: 70px; height: 70px; }
    .trump-block    { padding-bottom: 8px; margin-bottom: 6px; }
    .trump-name     { font-size: 0.72rem; margin-top: 4px; }
    .score-panel    { min-width: 108px; max-width: 168px; padding: 9px 10px; font-size: 0.72rem; }
    .score-row      { padding: 6px 9px; margin-bottom: 4px; }
    .score-n        { font-size: 2rem; }
    /* Hide caller crown on self pill — reduces pill width on mobile */
    #pi0 .pcrown                  { display: none !important; }
    /* Name pill sits just above the hand */
    .pos-bottom                   { bottom: calc(118px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
  }

  /* ── MODE TABS ── */
  .mode-tabs { display: flex; background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; margin-bottom: 14px; overflow: hidden; }
  .mode-tab { flex: 1; padding: 10px; border: none; background: none; color: var(--muted); font-size: 0.82rem; font-weight: 600; cursor: pointer; transition: all 0.18s; letter-spacing: 0.03em; }
  .mode-tab.active { background: rgba(201,162,39,0.1); color: var(--text); border-bottom: 2px solid var(--gold); }
  /* ── FRIENDS SECTION ── */
  #bots-section { display: none; }
  .or-divider { text-align: center; color: var(--muted); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.1em; margin: 10px 0; }
  .join-row { display: flex; gap: 8px; }
  .join-row input { flex: 1; background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; color: var(--text); font-family: inherit; font-size: 0.95rem; outline: none; transition: border-color 0.2s; -webkit-appearance: none; appearance: none; }
  .join-row input:focus { border-color: var(--gold); }
  .join-row input::placeholder { color: var(--muted); }
  .join-row .btn-join { width: auto; padding: 12px 18px; border-radius: 8px; border: none; cursor: pointer; font-size: 1rem; font-weight: 700; background: linear-gradient(135deg, var(--gold) 0%, #8B6A10 100%); color: #0B0706; flex-shrink: 0; }
  /* ── INVITE SCREEN ── */
  #invite-screen { background: radial-gradient(ellipse at 50% 30%, #1a0a2e 0%, var(--bg) 70%); }
  .invite-banner { background: rgba(201,162,39,0.08); border: 1px solid rgba(201,162,39,0.2); border-radius: 14px; padding: 20px 20px 18px; margin-bottom: 4px; text-align: center; }
  .invite-banner-text { color: var(--text); font-size: 1.05rem; font-weight: 600; letter-spacing: 0.01em; line-height: 1.4; }
  /* Room code + occupancy. The one line that turns "someone invited you"
     into "there is a real table with three people already at it". */
  .invite-meta {
    margin-top: 10px; font-size: 0.72rem; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--muted); font-weight: 600;
  }
  .invite-seats {
    display: flex; flex-wrap: wrap; gap: 5px; justify-content: center; margin-top: 10px;
  }
  .invite-seat-chip {
    font-size: 0.72rem; padding: 3px 9px; border-radius: 999px;
    background: rgba(255,255,255,0.05); border: 1px solid rgba(201,162,39,0.28);
    color: var(--text); max-width: 100%; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap;
  }
  .invite-seat-chip.bot { color: var(--muted); border-color: rgba(255,255,255,0.12); }
  .invite-rules {
    display: flex; flex-wrap: wrap; gap: 4px; justify-content: center; margin-top: 12px;
  }
  .invite-rules .rule-pill { font-size: 0.55rem; padding: 2px 6px; }
  .invite-rules .rule-pill.match { font-size: 0.58rem; padding: 3px 9px; }
  /* Not-joinable reasons (room gone, host offline) and full-table notes share
     this line — both are "here is what you're walking into", not errors. */
  .invite-status {
    margin-top: 12px; font-size: 0.78rem; line-height: 1.45; color: #E8B4B8;
  }
  /* ── SHARE BUTTON (waiting room) ── */
  /* ── Waiting room utility row: code chip + single Share button ──
     The code was a 2rem hero for information that matters for about four
     seconds; it's reference material now and the rules card takes the space. */
  /* Same Cinzel wordmark as the lobby, scaled right down. Cinzel is the brand
     voice for fixed marks, so this is one of the few places it still belongs
     after the rules card moved to the body face. */
  .wait-brand {
    font-family: 'Cinzel', serif; font-weight: 700;
    /* One line, so size costs almost nothing: 18px -> 29px adds 11px total and
       the screen still clears 360x640 with ~90px to spare. Sized to read as the
       app's mark rather than a caption, while staying well under the lobby's
       clamp(2.8rem, 9vw, 4rem) — the lobby wordmark is the hero, this one only
       has to say which app you are in. */
    font-size: 1.8rem; letter-spacing: 0.26em;
    color: var(--gold); line-height: 1;
    text-indent: 0.26em;            /* offset the trailing letter-space so it optically centres */
    text-shadow: 0 2px 20px rgba(201,162,39,0.28);
    margin: 2px 0 14px;
  }
  .wait-topbar { display: flex; align-items: stretch; gap: 7px; margin: 0 0 14px; }
  .wait-code-chip { display: inline-flex; align-items: center; gap: 7px; padding: 6px 12px; background: rgba(201,162,39,0.06); border: 1px solid rgba(201,162,39,0.28); border-radius: 10px; cursor: pointer; font-family: inherit; transition: border-color 0.18s, background 0.18s; }
  .wait-code-chip:hover { border-color: rgba(201,162,39,0.55); background: rgba(201,162,39,0.12); }
  .wcc-k { font-size: 0.5rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); }
  .wcc-v { font-family: 'Cinzel', serif; font-size: 1.02rem; font-weight: 700; color: var(--gold); letter-spacing: 0.16em; line-height: 1.1; }
  .wait-share-btn { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px; padding: 7px 12px; background: rgba(201,162,39,0.07); border: 1px solid rgba(201,162,39,0.34); border-radius: 10px; color: var(--gold-hi); font-family: inherit; font-size: 0.84rem; font-weight: 600; cursor: pointer; transition: background 0.2s, border-color 0.2s; }
  .wait-share-btn:hover { background: rgba(201,162,39,0.18); border-color: rgba(201,162,39,0.55); }
  .wait-share-btn svg { stroke: currentColor; flex-shrink: 0; }
  /* ── WAITING ROOM ── */
  /* Goal: fit the entire waiting screen — title, room code, share row,
     rules card, seats, hint, action buttons — within one viewport with NO
     scrolling on phones down to 360×640. Every vertical spacing here is
     tuned for that. If you add anything tall, retest at iPhone SE size. */
  #waiting-screen { background: radial-gradient(ellipse at 30% 20%, #220A10 0%, var(--bg) 70%); }
  .waiting-inner {
    text-align: center;
    padding: 20px 20px 16px;
    max-width: 420px;
    width: 100%;
    max-height: 100dvh;
    overflow: hidden;            /* hard no-scroll; if content overflows, trim something */
  }
  .waiting-seats { display: grid; grid-template-columns: 1fr 1fr; gap: 7px; margin: 0 0 4px; }
  .seat-slot { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 9px 10px; text-align: center; transition: border-color 0.25s; min-height: 54px; display: flex; flex-direction: column; justify-content: center; position: relative; }
  .seat-slot.filled { border-color: rgba(201,162,39,0.38); }
  .seat-slot.me     { border-color: var(--gold); box-shadow: 0 0 10px rgba(201,162,39,0.1); }
  .seat-name { font-weight: 600; font-size: 0.88rem; max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .seat-name.empty  { color: rgba(240,229,204,0.45); font-weight: 400; font-style: italic; }
  .seat-tag  { font-size: 0.62rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.08em; margin-top: 2px; }
  /* Status line + the inline fill-bots control on one row. Wraps rather than
     squashing: on a 360px phone the hint is long enough that side-by-side would
     crush the button, so it drops beneath instead of truncating. */
  .wait-status-row {
    display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
    gap: 6px 10px; margin: 14px 0 16px; min-height: 20px;
  }
  .waiting-hint { color: var(--muted); font-size: 0.74rem; min-height: 0; }
  .wait-inline-btn {
    flex: 0 0 auto;
    font-size: 0.68rem; font-weight: 600; font-family: inherit;
    color: rgba(201,162,39,0.85); background: rgba(201,162,39,0.07);
    border: 1px solid rgba(201,162,39,0.30); border-radius: 999px;
    padding: 3px 11px; cursor: pointer; white-space: nowrap;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
  }
  .wait-inline-btn:hover { color: var(--gold-hi); border-color: rgba(201,162,39,0.6); background: rgba(201,162,39,0.15); }
  .wait-inline-btn:active { transform: scale(0.97); }

  /* ── Rules card ──
     Leads with the mode name; the pills carry the detail and only a rule that
     differs from that mode's preset is tinted. Quiet by default, loud exactly
     when something is unusual — which is what earns it a reader. */
  .rules-card {
    position: relative;
    background: rgba(237, 208, 106, 0.07);
    border: 1px solid rgba(237, 208, 106, 0.30);
    border-radius: 11px;
    padding: 9px 11px 8px;
    margin: 0 0 16px;
    /* Full content width, like the seats grid and the buttons. A max-width here
       left it inset by ~40px against everything else on the screen. */
    width: 100%;
    display: flex; flex-direction: column; align-items: center;
    transition: border-color 0.18s, background 0.18s;
  }
  /* Host only — the whole card is the tap target, the gear just marks it. */
  .rules-card.editable { border-color: rgba(237,208,106,0.55); background: rgba(237,208,106,0.10); cursor: pointer; }
  .rules-card.editable:hover { border-color: var(--gold-hi); background: rgba(237,208,106,0.15); }
  /* Body face, not Cinzel. Cinzel is the brand voice for fixed marks — the
     wordmark, the room code, scores. This is a live value that changes with the
     room's settings, and in a serif small-caps it read as a title rather than
     as state. */
  .rules-card-mode {
    font-family: inherit;
    font-size: 1.06rem; font-weight: 700; letter-spacing: 0.01em;
    color: var(--gold-hi); line-height: 1.25; text-align: center;
  }
  .rules-card-gear {
    position: absolute; top: 8px; right: 8px;
    width: 25px; height: 25px; display: grid; place-items: center;
    border: 1px solid rgba(201,162,39,0.5); background: rgba(201,162,39,0.16);
    border-radius: 7px; color: var(--gold-hi); cursor: pointer; padding: 0;
  }
  /* Stroked gear, not filled: at this size a filled gear loses its centre hole
     and reads as a star. Sized 17px inside the 25px box. */
  .rules-card-gear svg { width: 17px; height: 17px; display: block; }
  .rules-card-pills {
    display: flex; flex-wrap: wrap; justify-content: center;
    /* Row gap larger than column gap: with seven pills the card wraps to two or
       three lines on a phone, and equal gaps made that read as one grey mass.
       Separating the rows is what makes a wrapped row look like a list. */
    gap: 6px 4px;
    width: 100%; margin-top: 7px; padding-top: 7px;
    border-top: 1px solid rgba(201,162,39,0.16);
  }
  /* Zero-height full-width flex item — forces a line break at a chosen point. */
  .rules-pill-break { flex: 0 0 100%; height: 0; margin: 0; padding: 0; border: 0; }
  /* Was 0.54rem (8.6px) with `border: 1px solid var(--border)`. At that size it
     read as a legal disclaimer, and #2E1A1F against the card's warm background
     meant the outline was effectively not drawn — so an unchanged pill was
     faint text in an invisible box. Only `.changed` had a visible border, which
     is why the red ones looked right and nothing else did.
     Now: legible size, a warm outline with real contrast, and text lifted off
     --muted. Sized to still clear 360x640 without scrolling; see the media
     query below for the extra room wider screens can afford. */
  .rule-pill {
    font-size: 0.60rem; letter-spacing: 0.05em; text-transform: uppercase; font-weight: 600;
    color: #b9a894; border: 1px solid rgba(201,162,39,0.26);
    background: rgba(201,162,39,0.05); border-radius: 6px; padding: 3px 7px;
    white-space: nowrap;
  }
  .rule-pill.changed {
    color: #E8B4B8; border-color: rgba(232,180,184,0.42); background: rgba(181,30,30,0.17);
  }
  /* Match length sits on its own line and carries the card's only gold pill.
     It is the one line that tells you how long you are signing up for, so it
     should not be the seventh identical grey chip. */
  .rule-pill.match {
    color: var(--gold-hi); border-color: rgba(201,162,39,0.45);
    background: rgba(201,162,39,0.11); font-weight: 700;
    letter-spacing: 0.07em; padding: 4px 11px; font-size: 0.63rem;
  }
  .rule-pill.match.changed { border-color: rgba(201,162,39,0.75); background: rgba(201,162,39,0.18); }

  /* Two tiers, and the split is a hard constraint rather than taste.
     .waiting-inner is `overflow: hidden`, so on a 360x640 phone anything that
     does not fit is CUT OFF, not scrolled. At 0.69rem the card grew to four
     pill rows and left 3px of headroom — one longer bot name from clipping.
     0.60rem holds three rows with 36px spare and is still well up from the
     0.54rem it replaced.
     Above 430px there is room to spare, so the pills take the size they wanted.
     Only the pills grow: widening the whole column would turn a compact card
     table into a sparse web form. */
  @media (min-width: 430px) {
    .rules-card-pills { gap: 7px 4px; }
    .rule-pill        { font-size: 0.69rem; padding: 4px 9px; }
    .rule-pill.match  { font-size: 0.72rem; padding: 5px 13px; }
  }
  .rules-card-cue {
    font-size: 0.5rem; letter-spacing: 0.09em; text-transform: uppercase;
    color: rgba(237,208,106,0.6); font-weight: 700;
    /* Was 6px, which read as stuck to the pills above it. */
    margin-top: 12px;
  }
  /* Running-series banner. Only rendered when the table parked an unfinished
     match here, so it must read as "your score is safe" at a glance — hence the
     score gets the weight, not the label. */
  .waiting-series {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    background: rgba(237, 208, 106, 0.07);
    border: 1px solid rgba(237, 208, 106, 0.28);
    border-radius: 8px;
    padding: 6px 10px 7px;
    margin: 0 auto 8px;
    max-width: 340px;
  }
  .waiting-series .ws-label {
    font-size: 0.52rem;
    letter-spacing: 0.18em;
    color: rgba(255,255,255,0.45);
    text-transform: uppercase;
    font-weight: 600;
  }
  .waiting-series .ws-score {
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--gold);
    letter-spacing: 0.06em;
    line-height: 1.2;
  }
  .waiting-series .ws-dash  { color: rgba(255,255,255,0.35); font-weight: 400; }
  .waiting-series .ws-sub   { font-size: 0.6rem; color: var(--muted); letter-spacing: 0.02em; }

  /* ── WAITING ROOM — TEAM LAYOUT ── */
  .team-col { flex: 1; display: flex; flex-direction: column; gap: 8px; padding: 0 8px; }
  .team-col:first-child { padding-left: 0; border-right: 1px solid var(--border); padding-right: 14px; }
  .team-col:last-child  { padding-right: 0; padding-left: 14px; }
  .team-col-label { font-size: 0.78rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: var(--muted); margin-bottom: 3px; }
  .team-col-label.team-a { color: var(--gold); }
  .team-col-label.team-b { color: rgba(201,162,39,0.8); }
  .team-join-btn { margin-top: 4px; padding: 9px; border-radius: 9px; border: 1px solid var(--border); background: var(--surface2); color: var(--muted); font-family: inherit; font-size: 0.78rem; font-weight: 600; cursor: pointer; transition: all 0.18s; }
  .team-join-btn:hover:not(:disabled) { border-color: var(--gold); color: var(--text); }
  .team-join-btn:disabled { opacity: 0.35; cursor: default; }
  .team-join-btn.team-b:hover:not(:disabled) { border-color: #7eb8f7; }
  .seat-name-row { display: flex; align-items: center; gap: 6px; justify-content: center; }
  .host-badge { font-size: 0.58rem; font-weight: 700; color: var(--gold); background: rgba(201,168,76,0.12); border: 1px solid rgba(201,168,76,0.35); border-radius: 6px; padding: 1px 5px; letter-spacing: 0.04em; white-space: nowrap; line-height: 1.4; vertical-align: middle; }
  .bot-badge { font-size: 0.58rem; font-weight: 700; color: var(--muted); background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.15); border-radius: 6px; padding: 1px 5px; white-space: nowrap; line-height: 1.4; vertical-align: middle; }
  .stale-badge { font-size: 0.55rem; font-weight: 700; color: #ff6b6b; background: rgba(255,107,107,0.1); border: 1px solid rgba(255,107,107,0.3); border-radius: 6px; padding: 1px 5px; white-space: nowrap; }
  .seat-slot.stale { opacity: 0.6; }
  .remove-bot-btn { font-size: 0.72rem; color: var(--muted); background: transparent; border: none; cursor: pointer; padding: 0 2px; position: absolute; top: 8px; right: 8px; line-height: 1; transition: color 0.15s; }
  .remove-bot-btn:hover { color: #ff6b6b; }
  /* Tap-to-move: a seat you can move into. Dashed border reads as "not yours
     yet" and matches the affordance the old per-seat Add Bot button used. */
  .seat-slot.movable { cursor: pointer; border-style: dashed; border-color: rgba(201,162,39,0.32); }
  .seat-slot.movable .seat-tag { color: rgba(201,162,39,0.78); }
  .seat-slot.movable:hover { border-style: solid; border-color: var(--gold); background: rgba(201,162,39,0.06); }
  .seat-slot.movable:active { transform: scale(0.985); }
  /* ── Ace switches ──
     Toggles rather than dropdowns because these settings are binary. Grouped
     under one quiet heading so three Ace controls read as a single topic
     instead of the sheet looking preoccupied with one card rank. The ROW is the
     tap target: the switch alone is 44×25, under the 44px minimum. */
  .ace-group { border: 1px solid rgba(201,162,39,0.22); border-radius: 11px; padding: 8px 11px 4px; margin: 0 0 9px; background: rgba(201,162,39,0.035); }
  .ace-group-label { font-size: 0.56rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted); margin-bottom: 5px; text-align: left; }
  .ace-tog-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid rgba(201,162,39,0.12); cursor: pointer; }
  .ace-tog-row:last-child { border-bottom: none; }
  .ace-tog-text { text-align: left; min-width: 0; }
  .ace-tog-label { display: block; font-size: 0.82rem; color: var(--text); font-weight: 500; }
  .ace-tog-sub { display: block; font-size: 0.66rem; color: var(--muted); margin-top: 1px; }
  .ace-switch { flex: 0 0 auto; width: 44px; height: 25px; border-radius: 999px; border: 1px solid var(--border); background: rgba(255,255,255,0.05); position: relative; cursor: pointer; padding: 0; transition: background 0.18s, border-color 0.18s; }
  .ace-switch.is-on { background: rgba(201,162,39,0.28); border-color: rgba(201,162,39,0.65); }
  .ace-switch:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
  .ace-knob { position: absolute; top: 2px; left: 2px; width: 19px; height: 19px; border-radius: 50%; background: var(--muted); transition: transform 0.18s, background 0.18s; }
  .ace-switch.is-on .ace-knob { transform: translateX(19px); background: var(--gold-hi); }
  /* Joiners see the state; only the host can change it. */
  .ace-group.is-readonly .ace-tog-row { cursor: default; }
  .ace-group.is-readonly .ace-switch { opacity: 0.6; cursor: default; }
  /* Rung Choose — a highlighted feature card, not a fine-tune knob. Sits high,
     glows gold, carries its own helper line (2026-09-02). */
  .rc-card { border: 1.5px solid rgba(201,162,39,0.55); border-radius: 12px; padding: 11px 13px; margin: 0 0 12px; background: linear-gradient(180deg, rgba(201,162,39,0.10), rgba(201,162,39,0.03)); box-shadow: 0 0 18px rgba(201,162,39,0.12); }
  .rc-card.hidden { display: none; }
  .rc-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; cursor: pointer; }
  .rc-title { display: flex; align-items: center; gap: 7px; font-size: 0.92rem; font-weight: 600; color: var(--gold-hi); letter-spacing: 0.01em; }
  .rc-state { font-size: 0.6rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--gold); border: 1px solid rgba(201,162,39,0.5); border-radius: 20px; padding: 2px 8px; }
  .rc-help { font-size: 0.72rem; color: var(--muted); line-height: 1.45; margin: 7px 2px 0; }
  .rc-help b { color: var(--gold-hi); font-weight: 600; }
  /* The switch reuses the ace-switch visual so the whole sheet stays one look. */
  .rc-switch { flex: 0 0 auto; width: 46px; height: 26px; border-radius: 999px; border: 1px solid rgba(201,162,39,0.65); background: rgba(201,162,39,0.28); position: relative; cursor: pointer; padding: 0; transition: background 0.18s, border-color 0.18s; }
  .rc-switch .rc-knob { position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%; background: var(--gold-hi); transition: transform 0.18s, background 0.18s; }
  .rc-switch.is-on .rc-knob { transform: translateX(20px); }
  .rc-switch:not(.is-on) { background: rgba(255,255,255,0.06); border-color: var(--border); }
  .rc-switch:not(.is-on) .rc-knob { background: var(--muted); transform: translateX(0); }
  .rc-switch:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }
  .rc-card.is-readonly .rc-head { cursor: default; }
  .rc-card.is-readonly .rc-switch { opacity: 0.6; cursor: default; }

  /* ── SETTINGS POPUP MODAL ── */
  #settings-modal { position: fixed; inset: 0; background: rgba(0,0,0,0.72); z-index: 200; display: flex; align-items: center; justify-content: center; padding: 20px; }
  .settings-card { background: var(--surface); border: 1px solid var(--border); border-radius: 18px; width: 100%; max-width: 380px; padding: 22px 20px 18px; }
  .settings-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; }
  .settings-card-title { font-size: 0.9rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--gold); }
  .settings-close-btn { background: none; border: none; color: var(--muted); font-size: 1.2rem; cursor: pointer; padding: 0 2px; line-height: 1; }
  .settings-close-btn:hover { color: var(--text); }
  .settings-row { display: flex; gap: 12px; margin-bottom: 14px; }
  .settings-item { flex: 1; }
  .settings-item label { font-size: 0.65rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); display: block; margin-bottom: 5px; }
  .settings-item select { width: 100%; background: var(--surface2); border: 1px solid var(--border); border-radius: 9px; padding: 8px 10px; color: var(--text); font-size: 0.82rem; outline: none; -webkit-appearance: none; appearance: none; cursor: pointer; transition: border-color 0.18s; }
  .settings-item select:focus { border-color: var(--gold); }
  .settings-item select:disabled { opacity: 0.45; cursor: default; }
  .settings-host-note { font-size: 0.65rem; color: var(--muted); text-align: center; margin-top: 6px; font-style: italic; }

  /* ── Reaction system ── */
  .react-btn { position: fixed; bottom: calc(clamp(120px, 20vw, 158px) + 52px + var(--sa-b, env(safe-area-inset-bottom, 0px))); right: calc(8px + var(--sa-r, env(safe-area-inset-right, 0px))); background: var(--surface); border: 1px solid rgba(255,255,255,0.18); border-radius: 14px; width: auto; height: auto; padding: 9px 12px; font-size: 1.15rem; cursor: pointer; z-index: 30; box-shadow: 0 2px 12px rgba(0,0,0,0.5); display: flex; flex-direction: column; align-items: center; gap: 4px; transition: transform 0.12s; }
  .react-btn:active { transform: scale(0.88); }
  #react-picker { position: fixed; bottom: 270px; right: 8px; background: var(--surface); border: 1px solid var(--border); border-radius: 14px; padding: 8px; z-index: 31; display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; box-shadow: 0 4px 22px rgba(0,0,0,0.5); min-width: 210px; }
  .react-opt { background: none; border: none; border-radius: 10px; padding: 7px 6px; cursor: pointer; text-align: center; transition: background 0.12s; }
  .react-opt:hover, .react-opt:active { background: var(--surface2); }
  .react-opt .re { font-size: 1.65rem; display: block; }
  .react-opt .rt { font-size: 0.5rem; color: var(--muted); display: block; line-height: 1.3; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 68px; }
  .react-bubble { position: fixed; background: rgba(22,22,28,0.93); border: 1px solid var(--border); border-radius: 20px; padding: 6px 14px; font-size: 0.78rem; color: var(--text); white-space: nowrap; z-index: 28; pointer-events: none; transform: translateX(-50%); animation: rPop 0.18s ease; }
  @keyframes rPop { from { opacity:0; transform: translateX(-50%) scale(0.75); } to { opacity:1; transform: translateX(-50%) scale(1); } }
  .react-bubble.react-out { opacity: 0; transform: translateX(-50%) scale(0.85); transition: opacity 0.4s ease, transform 0.4s ease; }
  /* ── Open tables sheet + seat picker ─────────────────────────────────── */
  .ot-sheet { max-width: 420px; width: calc(100vw - 36px); text-align: left; }
  .ot-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
  .ot-heading { font-size: 1.05rem; font-weight: 700; color: var(--text); }
  .ot-close { background: none; border: none; color: var(--muted); font-size: 1rem; cursor: pointer; padding: 4px 8px; }
  .ot-return { background: rgba(201,162,39,0.1); border: 1px solid rgba(201,162,39,0.35); border-radius: 10px;
    padding: 9px 12px; font-size: 0.8rem; color: #e3c766; margin-bottom: 10px; cursor: pointer; }
  .ot-playnow { width: 100%; padding: 12px; font-size: 0.95rem; font-weight: 700; border-radius: 12px;
    background: linear-gradient(180deg, #d9b545, #b8912a); color: #1c1210; border: none; cursor: pointer; }
  .ot-list-label { font-size: 0.68rem; letter-spacing: 0.12em; color: var(--muted); text-transform: uppercase;
    margin: 14px 0 6px; }
  .ot-row { display: flex; align-items: center; justify-content: space-between; gap: 10px;
    background: rgba(255,255,255,0.03); border: 1px solid rgba(201,162,39,0.15); border-radius: 10px;
    padding: 9px 12px; margin-bottom: 7px; }
  .ot-info { min-width: 0; }
  .ot-title { font-size: 0.86rem; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .ot-sub { font-size: 0.72rem; color: var(--muted); margin-top: 2px; }
  .ot-actions { display: flex; gap: 6px; flex-shrink: 0; }
  .ot-btn { padding: 7px 12px; font-size: 0.78rem; border-radius: 9px; background: rgba(201,162,39,0.9);
    color: #1c1210; border: none; cursor: pointer; font-weight: 700; }
  .ot-btn-ghost { background: none; border: 1px solid rgba(201,162,39,0.4); color: #e3c766; padding: 7px 12px;
    font-size: 0.78rem; border-radius: 9px; cursor: pointer; }
  .ot-empty { font-size: 0.78rem; color: var(--muted); padding: 12px 4px; line-height: 1.5; }
  .ot-foot { font-size: 0.7rem; color: var(--muted); margin-top: 12px; border-top: 1px solid rgba(201,162,39,0.12);
    padding-top: 9px; }
  .sp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .sp-team-label { grid-column: 1 / -1; font-size: 0.66rem; letter-spacing: 0.1em;
    text-transform: uppercase; color: var(--muted); margin: 6px 0 0; }
  .sp-seat { border-radius: 10px; padding: 12px 8px; text-align: center; cursor: pointer;
    background: rgba(255,255,255,0.03); border: 1px solid rgba(201,162,39,0.2); }
  .sp-seat.sp-open { border-color: rgba(201,162,39,0.65); background: rgba(201,162,39,0.08); }
  .sp-seat.sp-locked { opacity: 0.45; cursor: default; }
  .sp-name { font-size: 0.82rem; font-weight: 700; color: var(--text); margin-bottom: 3px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .sp-tag { font-size: 0.64rem; letter-spacing: 0.08em; color: #e3c766; text-transform: uppercase; }
  .sp-seat.sp-locked .sp-tag { color: var(--muted); }

  /* Takeable bot seats say so in words — a pulse alone reads as decoration,
     and field evidence says nobody discovered the tap. */
  /* NO position:relative here — the pills are position:absolute (edge-
     anchored), and overriding that dropped takeable pills into normal flow:
     giant pill mid-screen, vertical name on the hand (both field screenshots,
     2026-08-22). ::after anchors against the absolute pill just fine. */
  .takeable::after {
    content: 'SIT';
    position: absolute;
    top: -7px; right: -6px;
    background: var(--gold, #C9A227);
    color: #1c1210;
    font-size: 0.5rem;
    font-weight: 800;
    letter-spacing: 0.08em;
    padding: 1px 5px;
    border-radius: 6px;
    pointer-events: none;
  }

  .pinfo.p-away { opacity: 0.45; filter: grayscale(0.6); }
  .spec-badge {
    font-size: 0.62rem; color: #c9b56a; margin-top: 4px; text-align: right;
    letter-spacing: 0.04em; cursor: pointer; padding: 1px 2px;
    border-top: 1px solid rgba(201,162,39,0.15);
  }
  .spec-badge:active { color: var(--gold); }
  /* Reclaim notice — brighter than .spec-badge on purpose. This is the only
     thing telling a rejoining player they are getting their seat back, and it
     has to hold up against a live hand competing for attention. */
  .reclaim-badge {
    font-size: 0.6rem;
    color: rgba(237, 208, 106, 0.95);
    background: rgba(237, 208, 106, 0.10);
    border: 1px solid rgba(237, 208, 106, 0.32);
    border-radius: 7px;
    padding: 3px 7px;
    margin-top: 4px;
    text-align: center;
    letter-spacing: 0.02em;
    line-height: 1.3;
  }
  /* Surrender — readable but still secondary. Used to be 0.58rem with a
     transparent background and muted text, which users couldn't find. Bigger
     text + visible red-tinted border now make it discoverable without
     promoting it visually above the score numbers. */
  .concede-btn {
    width: 100%;
    margin-top: 10px;
    padding: 8px 8px;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    background: rgba(224,85,85,0.08);
    border: 1px solid rgba(224,85,85,0.32);
    border-radius: 8px;
    color: #E08080;
    cursor: pointer;
    font-family: inherit;
    transition: border-color 0.2s, color 0.2s, background 0.2s;
  }
  .concede-btn:hover:not(:disabled) {
    background: rgba(224,85,85,0.16);
    border-color: rgba(224,85,85,0.7);
    color: #ff8a8a;
  }
  .concede-btn:disabled { opacity: 0.35; cursor: not-allowed; }
  .concede-btn.pending {
    border-color: rgba(201,162,39,0.5);
    color: var(--gold);
    background: rgba(201,162,39,0.10);
  }
  .wait-spec-list { font-size: 0.72rem; color: var(--muted); margin-top: 10px; text-align: center; line-height: 1.5; }
  /* ── SURRENDER ANNOUNCE ── */
  /* z 400: also worn by #redeal-announce, which must cover the rung picker
     takeover (z 340) while the proof five are on show. */
  .surrender-announce { position: fixed; inset: 0; background: rgba(0,0,0,0.88); display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 400; backdrop-filter: blur(6px); }
  .surrender-announce.hidden { display: none; }
  .surrender-flag { font-size: 4rem; margin-bottom: 16px; animation: sfadeIn 0.4s ease both; }
  .surrender-msg { font-family: 'Cinzel', serif; font-size: 1.3rem; color: var(--text); letter-spacing: 0.05em; text-align: center; animation: sfadeIn 0.4s ease 0.12s both; }
  @keyframes sfadeIn { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:translateY(0); } }
  .lg-surrender { color: var(--muted); font-style: italic; padding: 4px 0; border-top: 1px solid rgba(255,255,255,0.06); margin-top: 4px; }
  /* ── COUNTDOWN OVERLAY ── */
  .countdown-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.88); display: flex; align-items: center; justify-content: center; flex-direction: column; z-index: 200; backdrop-filter: blur(6px); }
  .countdown-overlay.hidden { display: none; }
  .countdown-num { font-family: 'Cinzel', serif; font-size: 5rem; font-weight: 700; color: var(--gold); line-height: 1; }
  .countdown-label { color: var(--muted); font-size: 0.85rem; margin-top: 10px; letter-spacing: 0.1em; text-transform: uppercase; }

/* ── GAME LOG PANEL ── */
/* Sound + log live in the top-right cluster alongside Rules/Quit. Used to
   float at the top-center and overlapped with the top player's name pill. */
.log-btn {
  position: fixed; top: calc(12px + var(--sa-t, env(safe-area-inset-top, 0px))); right: calc(140px + var(--sa-r, env(safe-area-inset-right, 0px)));
  background: rgba(0,0,0,0.6); backdrop-filter: blur(8px);
  border: 1px solid var(--border); color: var(--text);
  font-size: 0.8rem; padding: 7px 11px; border-radius: 10px;
  cursor: pointer; z-index: 60; letter-spacing: 0.05em;
  transition: background 0.2s, border-color 0.2s;
}
.log-btn:hover { background: rgba(255,255,255,0.06); border-color: var(--gold); }
.snd-btn {
  position: fixed; top: calc(12px + var(--sa-t, env(safe-area-inset-top, 0px))); right: calc(184px + var(--sa-r, env(safe-area-inset-right, 0px)));
  background: rgba(0,0,0,0.6); backdrop-filter: blur(8px);
  border: 1px solid var(--border); color: var(--text);
  font-size: 0.85rem; padding: 7px 10px; border-radius: 10px;
  cursor: pointer; z-index: 60;
  transition: background 0.2s, border-color 0.2s;
  display: flex; align-items: center; justify-content: center;
}
.snd-btn:hover { background: rgba(255,255,255,0.06); border-color: var(--gold); }
/* Native app only: with the safe-area offset the 4-button row lands on the
   top seat's name pill (top: 56px). Stack sound + log on a second row at the
   right edge instead — the pill is centered, so the right edge stays clear. */
body.native-shell .snd-btn { top: calc(60px + var(--sa-t, env(safe-area-inset-top, 0px))); right: 12px; }
body.native-shell .log-btn { top: calc(60px + var(--sa-t, env(safe-area-inset-top, 0px))); right: 62px; }
@media (max-width: 520px) {
  .log-btn { right: 124px; padding: 6px 9px; font-size: 0.75rem; }
  .snd-btn { right: 164px; padding: 6px 9px; }
}

.log-panel { position: fixed; top: 0; right: 0; width: min(320px, 90vw); height: 100dvh; background: var(--surface); border-left: 1px solid var(--border); z-index: 120; display: flex; flex-direction: column; box-shadow: -8px 0 32px rgba(0,0,0,0.4); }
.log-panel.hidden { display: none; }
.log-panel-hdr { display: flex; justify-content: space-between; align-items: center; padding: calc(14px + var(--sa-t, env(safe-area-inset-top, 0px))) 16px 14px; border-bottom: 1px solid var(--border); font-size: 0.85rem; font-weight: 600; letter-spacing: 0.05em; color: var(--gold); flex-shrink: 0; }
.log-close { background: none; border: none; color: var(--muted); font-size: 1.1rem; cursor: pointer; padding: 10px 14px; margin: -10px -14px -10px 0; border-radius: 8px; }
/* Tap-outside-to-close for the log drawer; also dims the table behind it. */
.log-backdrop { position: fixed; inset: 0; z-index: 119; background: rgba(0,0,0,0.35); }
.log-backdrop.hidden { display: none; }
.log-close:hover { color: var(--text); }
.log-content { flex: 1; overflow-y: auto; padding: 12px; font-size: 0.75rem; }

.lg-rhdr { font-size: 0.7rem; color: var(--gold); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; margin: 8px 0 4px; }
.lg-rscore { font-size: 0.75rem; padding: 3px 0; }
.lg-sep { border-top: 1px solid var(--border); margin: 8px 0; }
.lg-trick { background: rgba(255,255,255,0.03); border-radius: 6px; padding: 6px 8px; margin-bottom: 5px; }
.lg-trick-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 3px; }
.lg-tnum { font-size: 0.68rem; color: var(--muted); font-weight: 600; }
.lg-winner { font-size: 0.7rem; font-weight: 600; }
.lg-void { font-size: 0.68rem; color: var(--muted); font-style: italic; }
.lg-plays { color: var(--muted); line-height: 1.6; }
.lg-plays b { color: var(--text); }
.lg-win  { color: #5cb85c; }
.lg-lose { color: var(--red); }
.lg-empty { color: var(--muted); font-style: italic; text-align: center; padding: 20px 0; font-size: 0.75rem; }

/* ── END SCREEN LOG ACCORDION ── */
.end-log-wrap { margin-top: 18px; width: 100%; }
.end-log-toggle { width: 100%; background: rgba(255,255,255,0.05); border: 1px solid var(--border); color: var(--muted); font-size: 0.78rem; padding: 9px 14px; border-radius: 8px; cursor: pointer; text-align: left; display: flex; justify-content: space-between; align-items: center; font-family: inherit; letter-spacing: 0.04em; transition: background 0.2s; }
.end-log-toggle:hover, .end-log-toggle.open { background: rgba(255,255,255,0.09); color: var(--text); }
.end-log-arrow { transition: transform 0.2s; }
.end-log-toggle.open .end-log-arrow { transform: rotate(180deg); }
.end-log-body { margin-top: 8px; max-height: 55vh; overflow-y: auto; scroll-behavior: smooth; }
.end-log-close-btn { width: 100%; background: rgba(255,255,255,0.05); border: 1px solid var(--border); color: var(--muted); font-size: 0.75rem; padding: 8px 14px; border-radius: 8px; cursor: pointer; font-family: inherit; letter-spacing: 0.04em; margin-top: 8px; transition: background 0.2s; }
.end-log-close-btn:hover { background: rgba(255,255,255,0.09); color: var(--text); }

.elr-round { margin-bottom: 14px; }
.elr-hdr { font-size: 0.7rem; color: var(--gold); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; margin-bottom: 5px; }
.elr-trick { display: flex; align-items: baseline; gap: 6px; padding: 3px 0; border-bottom: 1px solid rgba(255,255,255,0.04); font-size: 0.72rem; flex-wrap: wrap; }
.elr-tnum { color: var(--muted); font-size: 0.65rem; min-width: 22px; flex-shrink: 0; }
.elr-plays { flex: 1; color: var(--muted); }
.elr-plays b { color: var(--text); }
.elr-win  { color: #5cb85c; font-size: 0.68rem; font-weight: 600; flex-shrink: 0; }
.elr-lose { color: var(--red); font-size: 0.68rem; font-weight: 600; flex-shrink: 0; }
.elr-void { color: var(--muted); font-size: 0.68rem; font-style: italic; flex-shrink: 0; }
.elr-score { font-size: 0.75rem; margin-top: 6px; font-weight: 600; padding-top: 4px; }

/* ── LOG — trump / auto / pot additions ── */
.lg-trump { font-size: 0.6rem; color: var(--gold); font-weight: 700; letter-spacing: 0.03em; margin-left: 2px; vertical-align: middle; }
.lg-auto  { font-size: 0.6rem; color: var(--red);  font-weight: 600; margin-left: 2px; vertical-align: middle; }
.lg-pot   { font-size: 0.7rem; color: var(--gold); font-weight: 600; margin-top: 3px; padding-top: 3px; border-top: 1px solid rgba(212,175,55,0.2); }

.elr-trump { color: var(--gold); font-weight: 700; font-size: 0.65rem; }
.elr-auto  { color: var(--red);  font-size: 0.65rem; margin-left: 1px; }
.elr-pot   { font-size: 0.68rem; color: var(--gold); padding: 2px 0 2px 22px; font-weight: 600; }

/* ────────────────────────────────────────────────────────────
   Phase 1: Auth, Splash, Profile
   ──────────────────────────────────────────────────────────── */

/* ── AUTH chip in lobby corner ── */
.lobby-auth-chip { position: absolute; top: calc(14px + var(--sa-t, env(safe-area-inset-top, 0px))); right: calc(14px + var(--sa-r, env(safe-area-inset-right, 0px))); z-index: 5; }
.auth-chip { display: inline-flex; align-items: center; gap: 8px; background: rgba(22,12,14,0.85); border: 1px solid rgba(201,162,39,0.28); color: var(--text); border-radius: 999px; padding: 8px 14px 8px 8px; min-height: 44px; font-size: 0.82rem; font-family: inherit; cursor: pointer; transition: all 0.18s; backdrop-filter: blur(6px); }
.auth-chip:hover { border-color: rgba(201,162,39,0.6); transform: translateY(-1px); }
.auth-chip-signin { padding: 11px 18px; color: rgba(240,229,204,0.85); }
.auth-chip-signed img { width: 28px; height: 28px; border-radius: 50%; object-fit: cover; }
.auth-chip-initial { width: 28px; height: 28px; border-radius: 50%; background: linear-gradient(135deg, var(--gold), #8B6A10); color: #0B0706; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 0.78rem; }
.auth-chip-name { max-width: 80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Sign-in overlay (modal — used as fallback when needed) ── */
.overlay { position: fixed; inset: 0; background: rgba(11,7,6,0.78); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 100; padding: 20px; }
.overlay.hidden { display: none; }
.signin-modal { background: linear-gradient(180deg, var(--surface) 0%, var(--surface2) 100%); border: 1px solid rgba(201,162,39,0.25); border-radius: 16px; padding: 32px 28px 24px; max-width: 360px; width: 100%; text-align: center; position: relative; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.signin-close { position: absolute; top: 10px; right: 12px; background: none; border: none; color: var(--muted); font-size: 1.1rem; cursor: pointer; padding: 4px 8px; border-radius: 6px; }
.signin-close:hover { color: var(--text); background: rgba(255,255,255,0.05); }
.signin-suit { font-size: 2.6rem; color: var(--gold); margin-bottom: 8px; line-height: 1; }
.signin-title { font-family: 'Cinzel', serif; font-size: 1.4rem; font-weight: 700; color: var(--gold); letter-spacing: 0.05em; margin-bottom: 8px; }
.signin-sub { font-size: 0.85rem; color: rgba(240,229,204,0.7); margin-bottom: 22px; line-height: 1.4; }
.signin-fineprint { font-size: 0.68rem; color: var(--muted); margin-top: 14px; line-height: 1.5; }

/* ── Google button ── */
.btn-google { background: #fff; color: #1f1f1f; display: flex; align-items: center; justify-content: center; gap: 10px; font-weight: 600; border: 1px solid rgba(0,0,0,0.1); }
.btn-google:hover { background: #f8f8f8; transform: translateY(-1px); box-shadow: 0 6px 18px rgba(0,0,0,0.4); }
.btn-google svg { flex-shrink: 0; }

/* ── WhatsApp button ── */
.btn-whatsapp { background: #25D366; color: #fff; display: flex; align-items: center; justify-content: center; gap: 10px; font-weight: 600; border: 1px solid rgba(255,255,255,0.08); text-decoration: none; }
.btn-whatsapp:hover { background: #1FB855; transform: translateY(-1px); box-shadow: 0 6px 18px rgba(37,211,102,0.35); }
.btn-whatsapp svg { flex-shrink: 0; }

/* ── SPLASH (sign-in gate) ── */
#splash-screen {
  background:
    radial-gradient(ellipse at 50% 25%, rgba(107,23,40,0.55) 0%, transparent 55%),
    radial-gradient(ellipse at 75% 80%, rgba(61,10,21,0.4) 0%, transparent 50%),
    var(--bg);
}
.splash-inner { text-align: center; padding: 24px; max-width: 400px; width: 100%; }
.splash-suit { font-size: 3.4rem; color: var(--gold); line-height: 1; margin-bottom: 6px; text-shadow: 0 4px 32px rgba(201,162,39,0.35); animation: floatPulse 3s ease-in-out infinite; }
.splash-title { font-family: 'Cinzel', serif; font-size: clamp(3rem,10vw,4.4rem); font-weight: 700; color: var(--gold); letter-spacing: 0.18em; line-height: 1; text-shadow: 0 2px 24px rgba(201,162,39,0.25); }
.splash-tag { color: var(--muted); font-size: 0.78rem; letter-spacing: 0.22em; text-transform: uppercase; margin: 8px 0 16px; }
.splash-suits { display: flex; gap: 12px; justify-content: center; margin-bottom: 36px; font-size: 1.4rem; opacity: 0.55; }
.splash-suits span:nth-child(2){animation-delay:.5s}.splash-suits span:nth-child(3){animation-delay:1s}.splash-suits span:nth-child(4){animation-delay:1.5s}
.splash-suits span { animation: floatPulse 3s ease-in-out infinite; }
.splash-btn { font-size: 1.02rem; padding: 16px 22px; }
.splash-fineprint { font-size: 0.78rem; color: rgba(240,229,204,0.55); margin-top: 18px; line-height: 1.5; }
.splash-divider { display: flex; align-items: center; gap: 12px; margin: 24px 0 14px; color: rgba(240,229,204,0.45); font-size: 0.7rem; letter-spacing: 0.18em; text-transform: uppercase; }
.splash-divider::before, .splash-divider::after { content: ''; flex: 1; height: 1px; background: rgba(240,229,204,0.12); }
.splash-btn-secondary { font-size: 0.95rem; padding: 13px 20px; }
/* In-app browser (WhatsApp/Instagram) warning — Google blocks OAuth there. */
.splash-inapp { background: rgba(255,107,107,0.10); border: 1px solid rgba(255,107,107,0.42); border-radius: 12px; padding: 14px 16px; margin-bottom: 18px; text-align: left; }
.splash-inapp-title { color: #ff8f8f; font-weight: 700; font-size: 0.92rem; margin-bottom: 6px; }
.splash-inapp-body  { color: rgba(240,229,204,0.78); font-size: 0.82rem; line-height: 1.5; margin-bottom: 12px; }
.splash-inapp .splash-btn-secondary { width: 100%; }

/* ── PROFILE SCREEN ── */
#profile-screen { background: radial-gradient(ellipse at 50% 0%, rgba(107,23,40,0.4) 0%, transparent 50%), var(--bg); align-items: center; justify-content: flex-start; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: calc(18px + var(--sa-t, env(safe-area-inset-top, 0px))) 0 calc(40px + var(--sa-b, env(safe-area-inset-bottom, 0px))); touch-action: pan-y; overscroll-behavior: contain; }
.profile-inner { width: 100%; max-width: 480px; padding: 0 20px; flex-shrink: 0; }
.profile-header { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; padding-top: 8px; }
.profile-back { background: rgba(22,12,14,0.85); border: 1px solid var(--border); color: var(--text); width: 36px; height: 36px; border-radius: 50%; font-size: 1.1rem; cursor: pointer; transition: all 0.15s; }
.profile-back:hover { border-color: rgba(201,162,39,0.6); transform: translateX(-2px); }
.profile-title { font-family: 'Cinzel', serif; font-size: 1.2rem; color: var(--gold); letter-spacing: 0.1em; }

.profile-card { background: linear-gradient(180deg, var(--surface) 0%, var(--surface2) 100%); border: 1px solid rgba(201,162,39,0.18); border-radius: 16px; padding: 22px 18px; text-align: center; margin-bottom: 24px; }
.profile-photo { width: 72px; height: 72px; border-radius: 50%; background: linear-gradient(135deg, var(--gold), #8B6A10); color: #0B0706; display: flex; align-items: center; justify-content: center; font-family: 'Cinzel', serif; font-size: 1.8rem; font-weight: 700; margin: 0 auto 12px; overflow: hidden; }
.profile-photo img { width: 100%; height: 100%; object-fit: cover; }
.profile-name { font-family: 'Cinzel', serif; font-size: 1.3rem; color: var(--text); font-weight: 600; margin-bottom: 4px; letter-spacing: 0.04em; }
.profile-email { font-size: 0.78rem; color: var(--muted); }

.profile-section-title { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.15em; color: var(--muted); margin-bottom: 10px; padding: 0 4px; }

.profile-stats-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-bottom: 24px; }
.stat-cell { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 14px 8px; text-align: center; }
.stat-num { font-family: 'Cinzel', serif; font-size: 1.4rem; color: var(--gold); font-weight: 700; line-height: 1.1; margin-bottom: 4px; min-height: 1.4rem; }
.stat-lbl { font-size: 0.65rem; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; line-height: 1.2; }

.profile-history { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.profile-loading, .profile-empty { background: var(--surface); border: 1px solid var(--border); border-radius: 10px; padding: 18px; text-align: center; color: var(--muted); font-size: 0.82rem; }
.hist-item { display: grid; grid-template-columns: 1fr auto auto; gap: 12px; align-items: center; background: var(--surface); border: 1px solid var(--border); border-left: 3px solid var(--muted); border-radius: 8px; padding: 10px 14px; font-size: 0.82rem; }
.hist-item.won  { border-left-color: var(--gold); }
.hist-item.lost { border-left-color: var(--red); }
.hist-label { color: var(--text); font-weight: 500; }
.hist-score { font-family: 'Cinzel', serif; color: var(--gold); font-weight: 600; font-size: 0.9rem; }
.hist-item.lost .hist-score { color: rgba(240,229,204,0.5); }
.hist-time  { color: var(--muted); font-size: 0.7rem; }

/* ── Quick Play card (top of lobby) ─────────────────────────────────── */
/* ── SESSION BANNER (lobby "Next Session" card) ── */
.session-banner { margin: 14px 0 10px; padding: 11px 14px 10px; background: linear-gradient(135deg, rgba(107,23,40,0.55) 0%, rgba(61,10,21,0.5) 100%); border: 1px solid rgba(201,162,39,0.35); border-radius: 12px; text-align: left; cursor: pointer; transition: all 0.15s; box-shadow: 0 4px 14px rgba(107,23,40,0.22); }
.session-banner:hover { border-color: rgba(201,162,39,0.6); transform: translateY(-1px); box-shadow: 0 6px 20px rgba(107,23,40,0.32); }
.session-banner-label { font-size: 0.6rem; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold); font-weight: 700; margin-bottom: 3px; }
.session-banner-name { font-family: 'Cinzel', serif; font-size: 1.02rem; color: var(--text); margin-bottom: 2px; letter-spacing: 0.02em; line-height: 1.2; }
.session-banner-time { font-size: 0.74rem; color: rgba(240,229,204,0.78); margin-bottom: 8px; }
.session-banner-meta { display: none; } /* merged into time line by JS */
.btn-session { width: auto; padding: 6px 14px; font-size: 0.74rem; background: var(--gold); color: #0B0706; border-radius: 999px; border: none; font-weight: 700; letter-spacing: 0.04em; cursor: pointer; transition: all 0.15s; }
.btn-session:hover:not(:disabled) { filter: brightness(1.08); transform: translateY(-1px); }
.btn-session:disabled { opacity: 0.6; cursor: default; }

/* ── ADMIN: SESSIONS (/admin/sessions) ── */
#admin-screen { background: var(--bg); align-items: center; justify-content: flex-start; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: calc(18px + var(--sa-t, env(safe-area-inset-top, 0px))) 0 calc(40px + var(--sa-b, env(safe-area-inset-bottom, 0px))); touch-action: pan-y; overscroll-behavior: contain; }
.admin-inner { width: 100%; max-width: 640px; padding: 0 20px; flex-shrink: 0; }
.admin-header { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; padding-top: 8px; }
.admin-denied { background: rgba(181,30,30,0.12); border: 1px solid rgba(181,30,30,0.35); border-radius: 12px; padding: 16px 18px; }
.admin-denied-title { color: var(--red); font-weight: 700; margin-bottom: 8px; }
.admin-denied-body  { color: var(--text); font-size: 0.85rem; line-height: 1.5; margin-bottom: 10px; }
.admin-denied-uid   { background: rgba(0,0,0,0.4); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; font-family: 'SF Mono', Consolas, monospace; font-size: 0.78rem; color: var(--gold); word-break: break-all; margin-bottom: 10px; user-select: all; }
.admin-denied-steps { color: rgba(240,229,204,0.75); font-size: 0.78rem; padding-left: 18px; line-height: 1.6; }
.admin-denied-steps code { background: rgba(255,255,255,0.06); padding: 1px 6px; border-radius: 4px; font-size: 0.85em; }
.admin-section { background: rgba(22,12,14,0.55); border: 1px solid var(--border); border-radius: 14px; padding: 16px 18px; margin-bottom: 16px; }
.admin-section-title { font-family: 'Cinzel', serif; color: var(--gold); font-size: 0.95rem; letter-spacing: 0.08em; margin-bottom: 12px; }
.admin-empty { color: var(--muted); font-size: 0.85rem; font-style: italic; padding: 8px 0; }
.admin-row { display: flex; align-items: center; gap: 12px; padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.05); flex-wrap: wrap; }
.admin-row:last-child { border-bottom: none; }
.admin-row-main { flex: 1; min-width: 180px; }
.admin-row-title { color: var(--text); font-weight: 600; font-size: 0.92rem; margin-bottom: 2px; }
.admin-row-meta  { color: var(--muted); font-size: 0.75rem; }
.admin-row-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.btn-mini { padding: 6px 12px; border-radius: 6px; border: none; background: var(--gold); color: #0B0706; font-weight: 700; font-size: 0.72rem; letter-spacing: 0.04em; cursor: pointer; text-decoration: none; display: inline-block; }
.btn-mini:hover:not(:disabled) { filter: brightness(1.08); }
.btn-mini-ghost { background: transparent; color: rgba(240,229,204,0.8); border: 1px solid rgba(201,162,39,0.3); }
.btn-mini-ghost:hover { border-color: rgba(201,162,39,0.6); color: var(--text); }
.btn-mini-danger { background: transparent; color: rgba(181,30,30,0.85); border: 1px solid rgba(181,30,30,0.35); }
.btn-mini-danger:hover { background: rgba(181,30,30,0.12); }
.admin-form { margin-top: 14px; padding-top: 14px; border-top: 1px dashed rgba(255,255,255,0.08); }
.admin-form-row { margin-bottom: 10px; }
.admin-form-row-grid { display: grid; grid-template-columns: 1fr 1fr 1.2fr; gap: 8px; }
.admin-form input, .admin-form select { width: 100%; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; padding: 9px 10px; color: var(--text); font-size: 0.85rem; outline: none; font-family: inherit; }
.admin-form input:focus, .admin-form select:focus { border-color: var(--gold); }
.admin-code-input { width: 100%; max-width: 130px; margin-top: 6px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; padding: 6px 10px; color: var(--gold); font-family: 'SF Mono', Consolas, monospace; text-transform: uppercase; letter-spacing: 0.15em; font-size: 0.85rem; outline: none; }
.admin-code-input:focus { border-color: var(--gold); }
.admin-state { padding: 1px 7px; border-radius: 4px; font-size: 0.62rem; font-weight: 700; letter-spacing: 0.1em; }
.admin-state-upcoming { background: rgba(201,162,39,0.15); color: var(--gold); }
.admin-state-open     { background: rgba(76,175,125,0.15); color: var(--green); }
.admin-state-live     { background: rgba(181,30,30,0.18); color: var(--red); }
.admin-state-ended    { background: rgba(255,255,255,0.05); color: var(--muted); }
.admin-state-cancelled{ background: rgba(255,255,255,0.05); color: var(--muted); }
.admin-state-stale    { background: rgba(181,30,30,0.18); color: var(--red); font-weight: 700; }
.admin-row-stale .admin-row-title { color: rgba(240,229,204,0.55); }
.admin-row-stale .admin-row-meta  { color: rgba(181,30,30,0.85); }

/* ── SESSION DETAIL SCREEN (/session/:id) ── */
#session-screen { background: radial-gradient(ellipse at 50% 0%, rgba(107,23,40,0.45) 0%, transparent 55%), var(--bg); align-items: center; justify-content: flex-start; overflow-y: auto; -webkit-overflow-scrolling: touch; padding: calc(18px + var(--sa-t, env(safe-area-inset-top, 0px))) 0 calc(40px + var(--sa-b, env(safe-area-inset-bottom, 0px))); touch-action: pan-y; overscroll-behavior: contain; }
.session-inner { width: 100%; max-width: 480px; padding: 0 20px; flex-shrink: 0; }
.session-header { display: flex; align-items: center; gap: 12px; margin-bottom: 22px; padding-top: 8px; }
.session-card { background: linear-gradient(180deg, var(--surface) 0%, var(--surface2) 100%); border: 1px solid rgba(201,162,39,0.22); border-radius: 16px; padding: 26px 20px 22px; text-align: center; margin-bottom: 20px; box-shadow: 0 8px 28px rgba(0,0,0,0.4); }
.session-card-suit { font-size: 2.2rem; color: var(--gold); margin-bottom: 10px; text-shadow: 0 4px 24px rgba(201,162,39,0.3); }
.session-card-name { font-family: 'Cinzel', serif; font-size: 1.6rem; color: var(--gold); margin-bottom: 6px; letter-spacing: 0.04em; }
.session-card-time { color: var(--text); font-size: 0.95rem; margin-bottom: 8px; }
.session-card-state { font-size: 0.72rem; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(240,229,204,0.55); margin-bottom: 12px; }
.session-card-rsvp { font-size: 0.85rem; color: var(--gold); }
.session-cta { margin-bottom: 16px; }
.session-share { display: flex; gap: 10px; margin-bottom: 22px; }
.session-share-btn { flex: 1; padding: 12px 14px; font-size: 0.88rem; }
.session-roster { background: rgba(22,12,14,0.6); border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; }
.session-roster-item { padding: 6px 0; border-bottom: 1px solid rgba(255,255,255,0.05); font-size: 0.9rem; color: var(--text); }
.session-roster-item:last-child { border-bottom: none; }
.session-roster-empty { color: var(--muted); font-size: 0.85rem; font-style: italic; }

.quickplay-card {
  margin: 18px 0 14px;
  padding: 18px 16px 14px;
  background: linear-gradient(135deg, rgba(201,162,39,0.10), rgba(237,208,106,0.04));
  border: 1px solid rgba(201,162,39,0.35);
  border-radius: 14px;
  text-align: center;
}
.btn-quickplay {
  width: 100%;
  background: linear-gradient(135deg, var(--gold) 0%, var(--gold-hi) 60%, var(--gold) 100%);
  color: #0B0706;
  border: none;
  font-family: 'Cinzel', serif;
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 14px 16px;
  border-radius: 12px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  box-shadow: 0 6px 20px rgba(201,162,39,0.28);
  transition: transform 0.12s, filter 0.12s, box-shadow 0.12s;
}
.btn-quickplay:hover { transform: translateY(-1px); filter: brightness(1.06); box-shadow: 0 10px 28px rgba(201,162,39,0.42); }
.btn-quickplay:active { transform: translateY(0); }
.qp-bolt { font-size: 1.15rem; line-height: 1; }
.qp-label { letter-spacing: 0.12em; }
.qp-rules {
  margin-top: 8px;
  font-size: 0.68rem;
  color: rgba(240,229,204,0.6);
  letter-spacing: 0.04em;
  text-transform: lowercase;
}

/* ── Trump-pick countdown ── */
.trump-countdown { font-size: 0.8rem; font-weight: 600; letter-spacing: 0.04em; color: var(--gold); margin: 10px 0 4px; min-height: 1.1em; }
.trump-countdown:empty { margin: 0; min-height: 0; }

/* ── Round-end auto-advance countdown ── */
.end-countdown { font-size: 0.78rem; font-weight: 600; letter-spacing: 0.04em; color: rgba(240,229,204,0.65); text-align: center; margin: 8px 0 2px; min-height: 1.1em; }
.end-countdown:empty { margin: 0; min-height: 0; }

/* ── Lobby corner help icon + collapsed Join + community link ── */
.lobby-help-icon {
  position: absolute; top: calc(14px + var(--sa-t, env(safe-area-inset-top, 0px))); left: calc(14px + var(--sa-l, env(safe-area-inset-left, 0px))); z-index: 5;
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(22,12,14,0.85);
  border: 1px solid rgba(201,162,39,0.28);
  color: var(--text);
  font-family: 'Cinzel', serif;
  font-size: 1.05rem; font-weight: 600;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  backdrop-filter: blur(6px);
  transition: all 0.18s;
}
.lobby-help-icon:hover {
  border-color: rgba(201,162,39,0.6);
  transform: translateY(-1px);
}
/* Trophy chip — sits just left of the auth chip in the top-right corner.
   Pill shape with the cup emoji + a small "Leaderboard" label so the
   action is self-explanatory. On narrow phones the label hides and the
   chip collapses to the circle icon to save space. */
.lobby-trophy-icon {
  position: absolute; top: calc(14px + var(--sa-t, env(safe-area-inset-top, 0px))); right: calc(110px + var(--sa-r, env(safe-area-inset-right, 0px))); z-index: 5;
  height: 44px;
  padding: 0 14px 0 12px;
  border-radius: 22px;
  background: rgba(22,12,14,0.85);
  border: 1px solid rgba(201,162,39,0.28);
  color: var(--text);
  cursor: pointer;
  display: flex; align-items: center; gap: 8px;
  backdrop-filter: blur(6px);
  transition: all 0.18s;
  line-height: 1;
  font-family: inherit;
}
.lobby-trophy-icon .lti-emoji {
  font-size: 1.15rem; line-height: 1;
}
.lobby-trophy-icon .lti-label {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: rgba(237, 208, 106, 0.95);
}
.lobby-trophy-icon:hover {
  border-color: rgba(201,162,39,0.6);
  transform: translateY(-1px);
}
/* Narrow phones (≤380px): collapse to circle, hide the label so the chip
   doesn't crowd the auth chip on small screens. */
@media (max-width: 380px) {
  .lobby-trophy-icon {
    width: 44px; padding: 0; border-radius: 50%;
    justify-content: center;
  }
  .lobby-trophy-icon .lti-label { display: none; }
}
.lobby-join-expand {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 8px 0;
  background: transparent;
  border: none;
  color: rgba(240,229,204,0.6);
  font-size: 0.78rem;
  font-family: inherit;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 0.18s;
}
.lobby-join-expand:hover { color: var(--gold); }
.lobby-community-link {
  display: block;
  text-align: center;
  margin-top: 22px;
  color: rgba(240,229,204,0.55);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-decoration: none;
  transition: color 0.18s;
}
.lobby-community-link:hover { color: var(--gold); }

/* ── Persistent community footer strip ─────────────────────────────────
   Sticky bottom bar across lobby / profile / end-screen / splash. Hidden
   on game-flow screens (game, dealing, waiting) so it doesn't crowd the
   hand area. WhatsApp green so brand recognition does the heavy lifting. */
.community-footer-strip {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 10px 16px calc(10px + var(--sa-b, env(safe-area-inset-bottom, 0px)));
  background: linear-gradient(180deg, rgba(37,211,102,0.96), rgba(18,140,68,0.96));
  color: #fff;
  text-decoration: none;
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-top: 1px solid rgba(255,255,255,0.18);
  box-shadow: 0 -6px 22px rgba(37,211,102,0.18);
  z-index: 70;
  transition: filter 0.18s, transform 0.18s;
}
.community-footer-strip:hover { filter: brightness(1.06); }
.community-footer-strip:active { transform: translateY(1px); }
.community-footer-strip svg { flex-shrink: 0; }
.community-footer-arrow { opacity: 0.85; }

/* Hide on game-flow screens so the strip doesn't crowd the player's hand.
   Primary guard: a body class toggled from show()/hide() in utils.js — works in
   every browser. The :has() rules below are a CSS-only fallback for any path that
   bypasses those helpers; older in-app webviews ignore :has() but get the class. */
body.hide-community-footer             .community-footer-strip,
body:has(#game:not(.hidden))           .community-footer-strip,
body:has(#dealing-screen:not(.hidden)) .community-footer-strip,
body:has(#waiting-screen:not(.hidden)) .community-footer-strip,
body:has(#end-screen:not(.hidden))     .community-footer-strip,
body:has(#trump-overlay:not(.hidden))  .community-footer-strip {
  display: none !important;
}

/* Reserve room at the bottom of the lobby so cards don't sit under the strip. */
#lobby   { padding-bottom: 56px; }
#splash-screen,
#profile-screen { padding-bottom: 56px; }
@media (max-width: 520px) {
  .community-footer-strip { font-size: 0.78rem; padding: 9px 14px calc(9px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
  #lobby, #splash-screen, #profile-screen { padding-bottom: 52px; }
}

.lobby-divider {
  position: relative;
  margin: 14px 0 10px;
  text-align: center;
}
.lobby-divider::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 1px;
  background: rgba(240,229,204,0.10);
}
.lobby-divider span {
  position: relative;
  background: var(--bg, #0B0706);
  padding: 0 12px;
  font-size: 0.7rem;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.12em;
}

/* ── Daily Session + Quick Match banner (nightly 9 PM PKT) ─────────────── */
.daily-session-banner {
  background: linear-gradient(160deg, rgba(201,162,39,0.10), rgba(201,162,39,0.03));
  border: 1px solid rgba(201,162,39,0.28);
  border-radius: 14px;
  padding: 16px 16px 14px;
  margin: 14px 0 12px;
  text-align: center;
}
.daily-session-banner.live {
  border-color: rgba(201,162,39,0.6);
  box-shadow: 0 0 28px rgba(201,162,39,0.18);
}
.daily-session-status {
  font-size: 0.84rem;
  color: var(--text);
  letter-spacing: 0.02em;
  margin-bottom: 12px;
}
.daily-session-status strong { color: var(--gold-hi); font-weight: 700; }
.daily-session-join { width: 100%; }
.daily-session-banner.live .daily-session-join { animation: dailySessionPulse 2.2s ease-in-out infinite; }
.daily-session-count {
  font-size: 0.74rem;
  color: var(--muted);
  margin-top: 10px;
  min-height: 1em;
}
.daily-session-cal {
  background: none;
  border: none;
  color: var(--muted);
  font-size: 0.72rem;
  margin-top: 8px;
  cursor: pointer;
  text-decoration: underline;
  letter-spacing: 0.02em;
  font-family: inherit;
}
.daily-session-cal:hover { color: var(--gold); }
.live-dot, .online-dot {
  display: inline-block;
  width: 8px; height: 8px;
  border-radius: 50%;
  margin-right: 5px;
  vertical-align: middle;
}
.live-dot {
  background: #e5484d;
  box-shadow: 0 0 0 0 rgba(229,72,77,0.6);
  animation: liveBlink 1.4s ease-in-out infinite;
}
.online-dot { background: #3fb950; }
@keyframes liveBlink {
  0%,100% { opacity: 1; box-shadow: 0 0 0 0 rgba(229,72,77,0.5); }
  50%     { opacity: 0.5; box-shadow: 0 0 0 5px rgba(229,72,77,0); }
}
@keyframes dailySessionPulse {
  0%,100% { box-shadow: 0 4px 18px rgba(201,162,39,0.25); }
  50%     { box-shadow: 0 8px 30px rgba(201,162,39,0.55); }
}

/* ── Force-hide override (used to disable old lobby surfaces without
   removing the DOM, so admin/session routes stay functional) ───────────── */
.force-hidden { display: none !important; }

/* ── Solo vs Bots: secondary lobby CTA below Quick Match ───────────────── */
.btn-solo-bots {
  width: 100%;
  margin: 0 0 14px;
  background: rgba(201,162,39,0.08);
  border: 1px solid rgba(201,162,39,0.28);
  color: var(--text);
  font-family: inherit;
  font-size: 0.84rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 11px 16px;
  border-radius: 10px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  transition: background 0.18s, border-color 0.18s, transform 0.12s;
}
.btn-solo-bots:hover {
  background: rgba(201,162,39,0.14);
  border-color: rgba(201,162,39,0.5);
  transform: translateY(-1px);
}
.btn-solo-bots .qp-bolt { color: var(--gold); font-size: 1rem; }

/* ── Quick Match disabled state (outside session window) ─────────────────
   The button is disabled outside the 5-min pre-window + active session,
   so users see a clear "this opens later" signal instead of clicking
   into a likely-bot-only matchmaking attempt. */
.daily-session-join:disabled,
.daily-session-join[disabled] {
  background: rgba(201,162,39,0.18) !important;
  color: rgba(240,229,204,0.55) !important;
  cursor: not-allowed;
  box-shadow: none !important;
  filter: none !important;
  transform: none !important;
  animation: none !important;
  opacity: 1;
}
.daily-session-join:disabled:hover,
.daily-session-join[disabled]:hover {
  background: rgba(201,162,39,0.18) !important;
  filter: none !important;
  transform: none !important;
}

/* ── Rules-in-play summary inside the score panel ─────────────────────────
   Compact line showing the active ruleset (Sar turn / Senior Ace / 12th Sar
   Void). Helps Quick Match joiners who never picked the settings. */
/* ── Compact Match line (under trump in score panel) ───────────────────
   Single line: "Match 45-30 to 100". Hidden when G.match isn't set.
   Tappable — opens the rules-info modal. */
.match-mini {
  margin: 6px 0 4px;
  padding: 4px 6px;
  border-top: 1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  font-size: 0.68rem;
  letter-spacing: 0.02em;
  color: rgba(237, 208, 106, 0.92);
  text-align: center;
  cursor: pointer;
  white-space: nowrap;
  display: flex; align-items: center; justify-content: center; gap: 5px;
}
.match-mini .mm-label {
  font-size: 0.5rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
  margin-right: 2px;
}
.match-mini .mm-us   b,
.match-mini .mm-them b,
.match-mini .mm-target b {
  font-family: 'Cinzel', serif;
  font-weight: 700;
}
.match-mini .mm-us     { color: var(--gold-hi, #edd06a); }
.match-mini .mm-them   { color: rgba(255,255,255,0.85); }
.match-mini .mm-dash   { color: rgba(255,255,255,0.35); }
.match-mini .mm-target { color: rgba(255,255,255,0.55); font-size: 0.6rem; margin-left: 3px; }
.match-mini:hover     { background: rgba(237,208,106,0.06); }
@media (max-width: 520px) {
  .match-mini { font-size: 0.62rem; gap: 4px; padding: 3px 4px; }
}

/* ── Brief rules line at bottom of score panel ────────────────────────
   Short shorthand summary (e.g. "5th sar · 12th void · sr demote · match
   100"). Sits between Tricks and the Surrender button. Tap → opens the
   full rules-info modal. */
.rules-brief {
  margin-top: 8px;
  padding-top: 6px;
  border-top: 1px solid rgba(255,255,255,0.06);
  font-size: 0.56rem;
  letter-spacing: 0.04em;
  color: rgba(237,208,106,0.62);
  text-align: center;
  font-variant: small-caps;
  font-weight: 600;
  line-height: 1.35;
  cursor: pointer;
}
.rules-brief:hover { color: rgba(237,208,106,0.95); }
@media (max-width: 520px) {
  .rules-brief { font-size: 0.52rem; }
}

/* ── In-panel info icon ────────────────────────────────────────────────
   Sits in the top-right corner of the score panel. Subtle but always
   visible; tap opens the rules-info modal (active settings + scoring
   breakdown + current match score). */
.score-info-btn {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 22px; height: 22px;
  padding: 0;
  border: 1px solid rgba(237,208,106,0.25);
  background: rgba(0,0,0,0.4);
  color: rgba(237,208,106,0.85);
  border-radius: 50%;
  font-size: 0.78rem;
  font-weight: 700;
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  line-height: 1;
  transition: all 0.15s;
}
.score-info-btn:hover {
  border-color: rgba(237,208,106,0.65);
  background: rgba(237,208,106,0.12);
  color: var(--gold-hi, #edd06a);
}
@media (max-width: 520px) {
  .score-info-btn { width: 20px; height: 20px; font-size: 0.72rem; top: 5px; right: 5px; }
}

/* ── Rules-info modal content ──────────────────────────────────────────
   Tap-to-open panel showing the active ruleset + match scoring breakdown
   + current match score. Reuses the existing .overlay + .modal framework. */
.rules-info-modal {
  max-width: 360px;
  width: 92%;
  padding: 20px 22px 18px;
  background: rgba(22,12,14,0.96);
  border: 1px solid rgba(201,162,39,0.28);
  border-radius: 14px;
}
.rules-info-header {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}
.rules-info-title {
  font-family: 'Cinzel', serif;
  font-size: 1rem;
  font-weight: 700;
  color: var(--gold-hi, #edd06a);
  letter-spacing: 0.08em;
}
.rules-info-close {
  background: transparent;
  border: none;
  color: rgba(255,255,255,0.55);
  font-size: 1.1rem;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
}
.rules-info-close:hover { background: rgba(255,255,255,0.08); color: var(--text); }
.rules-info-section { margin-bottom: 14px; }
.rules-info-section:last-child { margin-bottom: 0; }
.ri-label {
  font-size: 0.58rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
  margin-bottom: 8px;
  font-weight: 600;
}
/* Mode name above the rule rows — the same heading the waiting-room rules card
   leads with, so a player who read the card recognises the game they are in. */
.ri-mode {
  font-family: 'Cinzel', serif;
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--gold-hi, #edd06a);
  margin-bottom: 9px;
}
.ri-list { list-style: none; padding: 0; margin: 0; }
.ri-list li {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  padding: 7px 10px;
  background: rgba(255,255,255,0.025);
  border: 1px solid rgba(255,255,255,0.05);
  border-radius: 7px;
  margin-bottom: 4px;
  font-size: 0.82rem;
}
/* Six rules with sentence-length values, on a 340px modal — the key holds its
   line and the value wraps and stays right-aligned rather than squeezing the
   label to one word per line. */
.ri-list .ri-key { color: rgba(255,255,255,0.75); flex: 0 0 auto; }
.ri-list .ri-val { color: var(--gold-hi, #edd06a); font-weight: 600; text-align: right; }
/* A value the host moved off the mode default, tinted to match the `changed`
   pills on the lobby CTA and the waiting-room card. */
.ri-list .ri-val.changed { color: #E8B4B8; }
.ri-notes { margin-top: 9px; }
.ri-note { font-size: 0.72rem; line-height: 1.45; color: var(--muted); margin: 0 0 6px; opacity: 0.9; }
.ri-note:last-child { margin-bottom: 0; }
.ri-list.ri-points .ri-pts {
  font-family: 'Cinzel', serif;
  font-weight: 700;
  font-size: 0.92rem;
}
.ri-target-row {
  margin-top: 8px;
  text-align: center;
  font-size: 0.78rem;
  color: rgba(255,255,255,0.65);
  padding: 8px 10px;
  background: rgba(237,208,106,0.06);
  border: 1px solid rgba(237,208,106,0.20);
  border-radius: 7px;
}
.ri-target-row b {
  color: var(--gold-hi, #edd06a);
  font-family: 'Cinzel', serif;
  font-size: 0.92rem;
}
.ri-current-vals {
  display: flex; align-items: center; justify-content: center;
  gap: 14px;
  padding: 10px;
  background: rgba(255,255,255,0.025);
  border-radius: 8px;
}
.ri-current-vals b {
  font-family: 'Cinzel', serif;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--gold-hi, #edd06a);
}
.ri-current-vals .ri-cs-name {
  font-size: 0.62rem;
  text-transform: uppercase;
  color: rgba(255,255,255,0.5);
  letter-spacing: 0.1em;
  margin-right: 6px;
}
.ri-current-vals .ri-cs-dash {
  color: rgba(255,255,255,0.25);
  font-size: 1.2rem;
}

/* LEGACY — old in-panel rules block, no longer rendered. Selectors kept
   harmless until next sweep. */
   Two lines: a tiny "RULES" small-caps label + the active ruleset.
   Same gold tint + small-caps treatment as the lobby's primary-cta-rules
   so players see consistent phrasing before AND during the round. */
.rules-mini-wrap {
  margin-top: 6px;
  padding-top: 6px;
  border-top: 1px solid rgba(255,255,255,0.07);
  text-align: center;
}
.rules-mini-label {
  font-size: 0.46rem;
  letter-spacing: 0.16em;
  color: rgba(255,255,255,0.35);
  text-transform: uppercase;
  margin-bottom: 2px;
  font-weight: 600;
}
.rules-mini {
  font-size: 0.58rem;
  color: rgba(237, 208, 106, 0.78);
  letter-spacing: 0.02em;
  line-height: 1.3;
  font-family: inherit;
  font-variant: small-caps;
  font-weight: 600;
  word-break: break-word;
}
@media (max-width: 520px) {
  .rules-mini { font-size: 0.6rem; }
}

/* Voice + React intro pulse — draws the eye when these buttons first appear
   in MP. Class is added by JS when the buttons un-hide, and removed on first
   tap or after a 12s grace. Subtle gold ring + slight scale breath. */
@keyframes hudButtonIntro {
  0%   { transform: scale(1);     box-shadow: 0 0 0 0 rgba(201,162,39,0.65); }
  50%  { transform: scale(1.06);  box-shadow: 0 0 0 10px rgba(201,162,39,0); }
  100% { transform: scale(1);     box-shadow: 0 0 0 0 rgba(201,162,39,0); }
}
.voice-btn.intro-pulse,
.react-btn.intro-pulse {
  animation: hudButtonIntro 2.4s ease-in-out infinite;
  border-color: rgba(201,162,39,0.55) !important;
}

/* ── Shareable post-game moment ─────────────────────────────────────────
   Sits under the Pees / Back to Lobby buttons on the end screen. Quiet
   when not relevant; eye-catching when there's something worth bragging
   about (Court, Goon Court of opponents, win). */
.end-share {
  margin-top: 18px;
  padding: 14px 14px 12px;
  background: rgba(201,162,39,0.06);
  border: 1px solid rgba(201,162,39,0.22);
  border-radius: 12px;
  text-align: center;
}
.end-share.hidden { display: none; }
/* Match-end reuses the round share card; just tighten its spacing to sit
   between the history list and the primary buttons. */
.match-share { margin-top: 4px; margin-bottom: 16px; }
.end-share-prompt {
  font-size: 0.78rem;
  color: var(--gold);
  letter-spacing: 0.04em;
  margin-bottom: 10px;
  font-family: 'Cinzel', serif;
}
.end-share-buttons {
  display: flex;
  gap: 8px;
  justify-content: center;
  flex-wrap: wrap;
}
.end-share-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: 1px solid transparent;
  border-radius: 10px;
  font-family: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: filter 0.18s, transform 0.12s;
}
.end-share-btn:hover { filter: brightness(1.08); transform: translateY(-1px); }
.end-share-btn.hidden { display: none; }
/* The route the player used last is promoted to first position and given a
   gold ring, so repeat sharing to the same group is a single obvious tap. */
.end-share-btn.is-preferred { box-shadow: 0 0 0 2px rgba(201,162,39,0.55), 0 6px 18px rgba(0,0,0,0.35); }
.end-share-native {
  background: rgba(255,255,255,0.08);
  border-color: rgba(255,255,255,0.18);
  color: var(--text);
}
.end-share-wa {
  background: #25D366;
  color: #fff;
}
.end-share-fb {
  background: #1877F2;
  color: #fff;
}

/* ── Onboarding overlay (welcome splash for first-time signed-in users) ── */
.onboarding-overlay {
  position: fixed;
  inset: 0;
  background: rgba(11,7,6,0.92);
  backdrop-filter: blur(6px);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 200;
  animation: onbFadeIn 0.32s ease-out;
}
@keyframes onbFadeIn { from { opacity: 0; } to { opacity: 1; } }
.onboarding-card {
  max-width: 380px;
  width: 92%;
  padding: 28px 24px 22px;
  background: linear-gradient(180deg, rgba(33,18,22,0.96), rgba(11,7,6,0.96));
  border: 1px solid rgba(201,162,39,0.32);
  border-radius: 18px;
  text-align: center;
  box-shadow: 0 12px 48px rgba(0,0,0,0.6);
  animation: onbCardIn 0.4s ease-out;
}
@keyframes onbCardIn { from { transform: scale(0.94); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.onboarding-suits {
  font-size: 1.4rem;
  letter-spacing: 0.35em;
  color: rgba(201,162,39,0.55);
  margin-bottom: 14px;
}
.onboarding-greeting {
  font-family: 'Cinzel', serif;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--text);
  margin-bottom: 10px;
  letter-spacing: 0.03em;
}
.onboarding-name { color: var(--gold); }
.onboarding-sub {
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.55;
  margin-bottom: 20px;
}
/* Primary CTA in the welcome splash — user taps to start their first game */
.onboarding-start {
  width: 100%;
  padding: 14px 22px;
  font-size: 0.92rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  margin-bottom: 14px;
  border-radius: 12px;
  font-family: 'Cinzel', serif;
}
.onboarding-skip {
  display: block;
  margin: 0 auto;
  padding: 6px 12px;
  background: none;
  border: none;
  color: rgba(240,229,204,0.55);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  cursor: pointer;
  font-family: inherit;
  transition: color 0.18s;
}
.onboarding-skip:hover { color: var(--gold); }

/* Post-onboarding pulse on the Quick Match button after the user returns to
   the lobby from their auto-launched first game. */
@keyframes onbPostGamePulse {
  0%,100% { box-shadow: 0 6px 20px rgba(201,162,39,0.28), 0 0 0 0 rgba(201,162,39,0.6); }
  50%     { box-shadow: 0 10px 30px rgba(201,162,39,0.55), 0 0 0 12px rgba(201,162,39,0); }
}
#btn-quick-match.onboarding-pulse {
  animation: onbPostGamePulse 1.8s ease-in-out 3;
}

/* Download the win card — small secondary action under the WA/FB row */
.end-share-download {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin: 10px auto 0;
  padding: 6px 12px;
  background: none;
  border: 1px dashed rgba(201,162,39,0.32);
  border-radius: 8px;
  color: rgba(240,229,204,0.65);
  font-family: inherit;
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: color 0.18s, border-color 0.18s, background 0.18s;
}
.end-share-download:hover {
  color: var(--gold);
  border-color: rgba(201,162,39,0.6);
  background: rgba(201,162,39,0.06);
}

/* ── Lobby secondary actions (Play with friends / Customize game) ──────
   Native <details> collapsibles styled to feel like soft "next-step" rows
   beneath the single Quick Match CTA. Quiet by default, light hover, no
   visual competition with the primary button. */
.lobby-secondary {
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 12px;
  margin: 0 0 10px;
  overflow: hidden;
  transition: border-color 0.18s, background 0.18s;
}
.lobby-secondary:hover,
.lobby-secondary[open] {
  background: rgba(255,255,255,0.05);
  border-color: rgba(201,162,39,0.32);
}
.lobby-secondary > summary {
  list-style: none;
  cursor: pointer;
  padding: 14px 16px;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text);
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-family: inherit;
  user-select: none;
}
.lobby-secondary > summary::-webkit-details-marker { display: none; }
.lobby-secondary > summary::marker { display: none; }
.lobby-secondary-icon { font-size: 1.05rem; }
.lobby-secondary-arrow {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.9rem;
  transition: transform 0.2s, color 0.18s;
}
.lobby-secondary[open] .lobby-secondary-arrow {
  transform: rotate(90deg);
  color: var(--gold);
}
.lobby-secondary-body {
  padding: 4px 16px 18px;
  border-top: 1px solid rgba(255,255,255,0.06);
}

/* Tighten name-input vertical spacing in the new layout */
.form-group.lobby-name { margin: 14px 0 10px; }

/* Hide legacy lobby elements no longer wired into the UI flow */
.force-hidden { display: none !important; }

/* ── Primary Quick Match CTA card ─────────────────────────────────────
   Gold-bordered card with the big Quick Match button. No pulse — it's
   always-on, always works, doesn't need a session signal. */
.primary-cta-card {
  background: linear-gradient(180deg, rgba(33,18,22,0.55), rgba(11,7,6,0.55));
  border: 1px solid rgba(201,162,39,0.32);
  border-radius: 14px;
  padding: 18px 16px 14px;
  margin: 0 0 12px;
  text-align: center;
}
.primary-cta-btn {
  width: 100%;
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 14px 22px;
  border-radius: 12px;
  font-family: 'Cinzel', serif;
  margin: 0 0 8px;
}
.primary-cta-sub {
  font-size: 0.74rem;
  color: var(--muted);
  letter-spacing: 0.04em;
}
/* Now a pill row, matching the waiting-room rules card so the lobby and the
   room speak the same visual language about the same ruleset. One size down:
   this is a preview under a button, not the room's headline. */
.primary-cta-rules {
  margin-top: 8px;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: 5px 4px;
}
.primary-cta-rules .rule-pill       { font-size: 0.55rem; padding: 2px 6px; }
.primary-cta-rules .rule-pill.match { font-size: 0.58rem; padding: 3px 9px; }
@media (min-width: 430px) {
  .primary-cta-rules .rule-pill       { font-size: 0.62rem; padding: 3px 8px; }
  .primary-cta-rules .rule-pill.match { font-size: 0.65rem; padding: 4px 11px; }
}

/* ── Segmented mode toggle ─────────────────────────────────────────────
   Two-segment pill sitting above the Quick Match button (and inside the
   MP settings modal). One tap to switch mode. Active segment gets a
   gold fill; the other stays quiet. When settings deviate from either
   preset (Custom state) both segments stay unselected — no highlight
   IS the Custom indicator. */
.mode-toggle {
  display: flex;
  gap: 3px;
  padding: 3px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  margin: 0 0 10px;
}
.mode-toggle-seg {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 7px;
  /* Was 8px 10px with a 0.85rem Cinzel label, which made "Hidden Rung" wrap to
     two lines and dragged the whole control to ~65px tall — a mode switch
     looking heavier than the Quick Match button under it. Now one line, always:
     see the nowrap + clamped label below. */
  padding: 7px 6px;
  min-width: 0;
  color: var(--muted);
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.mode-toggle-seg:hover, .mode-toggle-seg:focus-visible {
  background: rgba(255, 255, 255, 0.03);
  color: var(--text);
  outline: none;
}
.mode-toggle-seg[aria-selected="true"] {
  background: rgba(201, 162, 39, 0.14);
  border-color: rgba(201, 162, 39, 0.55);
  color: rgba(237, 208, 106, 0.95);
}
.mode-toggle-emoji {
  font-size: 0.92rem;
  line-height: 1;
  flex: 0 0 auto;
}
.mode-toggle-label {
  font-family: 'Cinzel', serif;
  font-weight: 600;
  letter-spacing: 0.02em;
  /* Scales with the container so the longer label ("Hidden Rung") still fits
     on one line at 360px without shrinking it everywhere else. */
  font-size: clamp(0.68rem, 3.1vw, 0.82rem);
  white-space: nowrap;
}
/* Non-host joiners see the MP toggle but can't tap it. */
.mode-toggle.is-disabled .mode-toggle-seg {
  cursor: not-allowed;
  opacity: 0.6;
}
.mode-toggle.is-disabled .mode-toggle-seg:hover {
  background: transparent;
  color: var(--muted);
}
.mode-toggle.is-disabled .mode-toggle-seg[aria-selected="true"] {
  background: rgba(201, 162, 39, 0.10);
  color: rgba(237, 208, 106, 0.75);
}

/* ── MP settings modal — mode section wrapper ─────────────────────────
   Small MODE label above the segmented toggle, then a "Fine-tune below"
   divider separating mode from the individual dropdown grid. */
.mp-mode-picker { margin-bottom: 12px; }
.mp-mode-label {
  font-size: 0.72rem;
  color: var(--muted);
  letter-spacing: 0.08em;
  font-variant: small-caps;
  margin-bottom: 6px;
  font-weight: 600;
}
.mp-mode-divider {
  text-align: center;
  font-size: 0.65rem;
  color: var(--muted);
  letter-spacing: 0.1em;
  font-variant: small-caps;
  margin: 4px 0 10px;
  position: relative;
}
.mp-mode-divider::before, .mp-mode-divider::after {
  content: '';
  position: absolute;
  top: 50%;
  width: 30%;
  height: 1px;
  background: rgba(255, 255, 255, 0.08);
}
.mp-mode-divider::before { left: 0; }
.mp-mode-divider::after  { right: 0; }

/* ── Daily play streak strip ───────────────────────────────────────────
   Quiet by default — a strip above Quick Match that reads "🔥 Day N
   streak". Tiers shift color and intensity:
     .tier-warm  (1-6 days)  — soft gold
     .tier-hot   (7+)        — orange, slightly brighter
     .tier-saver (lapsing)   — red pulse, "ends in Xh Ym"
   On hover/long-press, the longest streak is shown as a quiet sub-line. */
.play-streak-strip {
  background: rgba(255, 255, 255, 0.025);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 10px;
  padding: 8px 12px;
  margin: 0 0 10px;
  font-size: 0.82rem;
  letter-spacing: 0.02em;
  display: flex; align-items: center; gap: 8px;
  color: rgba(255,255,255,0.75);
}
.play-streak-strip .ps-flame { font-size: 1.05rem; line-height: 1; }
.play-streak-strip .ps-main  { font-weight: 600; }
.play-streak-strip .ps-sub   { font-size: 0.72rem; opacity: 0.62; margin-left: auto; }
.play-streak-strip.tier-warm  { border-color: rgba(237, 208, 106, 0.30); color: rgba(237, 208, 106, 0.92); }
.play-streak-strip.tier-hot   { border-color: rgba(255, 140, 60, 0.45);  color: rgba(255, 180, 100, 0.95); }
.play-streak-strip.tier-saver {
  border-color: rgba(220, 80, 80, 0.55); color: rgba(255, 170, 170, 0.95);
  animation: ps-pulse 2.4s ease-in-out infinite;
}
@keyframes ps-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(220, 80, 80, 0.0); }
  50%      { box-shadow: 0 0 14px 0 rgba(220, 80, 80, 0.35); }
}

/* ── Push soft-prompt ──────────────────────────────────────────────────
   Custom card shown after first round completes. Asks once; if dismissed,
   we don't burn the browser's native permission prompt. Sits at the bottom
   centre so it doesn't compete with the result-share buttons up top. */
.push-soft-prompt {
  position: fixed;
  left: 50%; bottom: 80px;
  transform: translateX(-50%);
  width: min(420px, calc(100vw - 32px));
  background: rgba(28, 18, 18, 0.96);
  border: 1px solid rgba(237, 208, 106, 0.35);
  border-radius: 14px;
  padding: 14px 16px;
  display: flex; align-items: center; gap: 12px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.55);
  z-index: 9000;
  animation: psp-in 0.32s ease-out;
}
@keyframes psp-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%,  0); }
}
.push-soft-prompt .psp-icon  { font-size: 1.6rem; line-height: 1; }
.push-soft-prompt .psp-body  { flex: 1; min-width: 0; }
.push-soft-prompt .psp-title { font-weight: 700; font-size: 0.92rem; color: #f1e5c2; }
.push-soft-prompt .psp-sub   { font-size: 0.74rem; color: rgba(255,255,255,0.55); margin-top: 2px; }
.push-soft-prompt .psp-actions { display: flex; flex-direction: column; gap: 6px; }
.push-soft-prompt .psp-yes,
.push-soft-prompt .psp-no {
  border: none; border-radius: 8px; cursor: pointer;
  font-size: 0.78rem; font-weight: 600; padding: 7px 12px;
}
.push-soft-prompt .psp-yes { background: linear-gradient(180deg, #edd06a, #c9a227); color: #1c1212; }
.push-soft-prompt .psp-no  { background: transparent; color: rgba(255,255,255,0.55); }
.push-soft-prompt .psp-no:hover { color: rgba(255,255,255,0.85); }
@media (max-width: 480px) {
  .push-soft-prompt { padding: 10px 12px; gap: 8px; }
  .push-soft-prompt .psp-actions { flex-direction: row; }
}

/* ── Lobby leaderboard chip ────────────────────────────────────────────
   Subtle gold-tinted chip just above the push chip + WhatsApp footer.
   Tap → opens the leaderboard overlay. */
.lobby-leaderboard-chip {
  background: rgba(237, 208, 106, 0.06);
  border: 1px solid rgba(237, 208, 106, 0.22);
  border-radius: 10px;
  padding: 11px 14px;
  margin: 12px 0 0;
  width: 100%;
  display: flex; align-items: center; gap: 10px;
  font-size: 0.86rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: rgba(237, 208, 106, 0.95);
  cursor: pointer;
  font-family: inherit;
  text-align: left;
  transition: background 0.15s, border-color 0.15s;
}
.lobby-leaderboard-chip:hover {
  background: rgba(237, 208, 106, 0.12);
  border-color: rgba(237, 208, 106, 0.45);
}
.lobby-leaderboard-chip .llc-icon  { font-size: 1.1rem; line-height: 1; }
.lobby-leaderboard-chip .llc-text  { flex: 1; }
.lobby-leaderboard-chip .llc-arrow { opacity: 0.7; }

/* ── Leaderboard overlay ───────────────────────────────────────────────
   Full-screen overlay with 4 tab boards. Loads on open, refreshes on
   tab switch. Live Firestore queries; blocklist filtered at the
   leaderboard.js layer. */
/* Whole screen scrolls natively (no inner overflow container) so mobile
   address bar collapses and Brave/Chrome's pull-to-refresh works as
   users expect. Header sticks via position: sticky. */
#leaderboard-screen {
  background:
    radial-gradient(ellipse at 50% 0%, rgba(107,23,40,0.35) 0%, transparent 55%),
    var(--bg);
  align-items: stretch;
  justify-content: flex-start;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
  overscroll-behavior: contain;
}
.lb-inner {
  width: 100%;
  max-width: 480px;
  margin: 0 auto;
  padding-bottom: calc(100px + var(--sa-b, env(safe-area-inset-bottom, 0px))); /* clear WhatsApp footer at bottom */
  flex-shrink: 0;                 /* WebKit: column-flex scroller must not compress the child */
}
/* Sticky header band — solid background spans full lobby width so scrolled
   rows behind it stay hidden. Stays pinned to the top of the viewport
   regardless of scroll position. */
.lb-sticky-top {
  position: sticky;
  top: 0;
  padding: calc(18px + var(--sa-t, env(safe-area-inset-top, 0px))) 18px 8px;
  background:
    radial-gradient(ellipse at 50% 0%, rgba(107,23,40,0.55) 0%, rgba(11,7,6,0.96) 70%),
    var(--bg);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  z-index: 5;
  backdrop-filter: blur(8px);
}
.lb-list {
  padding: 12px 18px 0;
}
.lb-header {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 16px;
}
.lb-back {
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.10);
  color: var(--text);
  width: 36px; height: 36px;
  border-radius: 10px;
  font-size: 1.2rem;
  cursor: pointer;
}
.lb-back:hover { background: rgba(255,255,255,0.10); }
.lb-title {
  font-family: 'Cinzel', serif;
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--gold);
  letter-spacing: 0.06em;
}
.lb-tabs {
  display: flex; gap: 6px;
  overflow-x: auto;
  margin-bottom: 12px;
  scrollbar-width: none;
}
.lb-tabs::-webkit-scrollbar { display: none; }
.lb-tab {
  background: rgba(255,255,255,0.04);
  border: 1px solid rgba(255,255,255,0.08);
  border-radius: 8px;
  padding: 8px 12px;
  font-size: 0.82rem;
  color: rgba(255,255,255,0.65);
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.lb-tab:hover { color: var(--text); }
.lb-tab-active {
  background: rgba(237, 208, 106, 0.14);
  border-color: rgba(237, 208, 106, 0.50);
  color: var(--gold-hi);
}
.lb-period {
  font-size: 0.72rem;
  color: rgba(237, 208, 106, 0.65);
  letter-spacing: 0.03em;
  padding: 8px 4px 0;
  font-variant: small-caps;
}
.lb-me-rank {
  font-size: 0.78rem;
  color: rgba(255,255,255,0.55);
  padding: 6px 4px 12px;
  font-style: italic;
}
/* Inner gap (extra rules; the scroll container is defined above) */
.lb-list > .lb-row + .lb-row { margin-top: 4px; }
.lb-row {
  display: grid;
  grid-template-columns: 36px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: rgba(255,255,255,0.025);
  border: 1px solid rgba(255,255,255,0.05);
  border-radius: 8px;
  font-size: 0.88rem;
}
.lb-row-me {
  background: rgba(237, 208, 106, 0.08);
  border-color: rgba(237, 208, 106, 0.35);
}
.lb-rank {
  font-weight: 700;
  color: var(--gold);
  font-size: 0.9rem;
  text-align: center;
}
.lb-row:nth-child(1) .lb-rank { font-size: 1.05rem; }   /* slight emphasis on top 3 */
.lb-row:nth-child(2) .lb-rank,
.lb-row:nth-child(3) .lb-rank { font-size: 0.98rem; }
.lb-name {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.lb-val {
  color: var(--gold-hi);
  font-weight: 600;
  font-size: 0.86rem;
}
.lb-val-unit {
  color: rgba(255,255,255,0.45);
  font-weight: 400;
  font-size: 0.74rem;
  margin-left: 2px;
}
.lb-loading, .lb-empty, .lb-error {
  padding: 28px 16px;
  text-align: center;
  color: rgba(255,255,255,0.55);
  font-size: 0.88rem;
}
.lb-error-title {
  font-size: 0.95rem;
  color: var(--text);
  font-weight: 600;
  margin-bottom: 6px;
}
.lb-error-sub {
  font-size: 0.78rem;
  color: rgba(255,255,255,0.5);
  line-height: 1.5;
  margin-bottom: 14px;
}
.lb-error-cta {
  display: inline-block;
  padding: 9px 16px;
  background: rgba(237, 208, 106, 0.14);
  border: 1px solid rgba(237, 208, 106, 0.45);
  border-radius: 8px;
  color: var(--gold-hi);
  font-size: 0.86rem;
  font-weight: 600;
  text-decoration: none;
}
.lb-error-cta:hover {
  background: rgba(237, 208, 106, 0.24);
  border-color: rgba(237, 208, 106, 0.65);
}

/* ── Lobby push chip ───────────────────────────────────────────────────
   Subtle text link that lives just above the WhatsApp community footer.
   Same width as the lobby content card, low-contrast so the streak strip
   + Quick Match keep visual priority. Tapping triggers the soft prompt. */

/* ── Secondary "Match with players" card ──────────────────────────────
   Quieter than primary. Header row with icon + title + online count +
   arrow. Status text below shows the 9 PM session line, becomes the
   LIVE state during the session window with a subtle gold ring breath
   (not the dramatic pulse the primary used to have). */
.daily-session-banner.secondary-match {
  background: rgba(255,255,255,0.03);
  border: 1px solid rgba(255,255,255,0.10);
  border-radius: 12px;
  padding: 12px 14px;
  margin: 0 0 10px;
  text-align: left;
  cursor: pointer;
  transition: background 0.18s, border-color 0.18s, box-shadow 0.18s;
}
.daily-session-banner.secondary-match:hover {
  background: rgba(255,255,255,0.05);
  border-color: rgba(201,162,39,0.4);
}
@keyframes secondaryLiveBreath {
  0%,100% { box-shadow: 0 0 0 0 rgba(201,162,39,0.18); }
  50%     { box-shadow: 0 0 0 6px rgba(201,162,39,0); }
}
.daily-session-banner.secondary-match.live {
  border-color: rgba(201,162,39,0.5);
  background: rgba(201,162,39,0.06);
  animation: secondaryLiveBreath 3.6s ease-in-out infinite;
}
.match-players-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.92rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
}
.match-players-icon { color: var(--gold); font-size: 1rem; }
.match-players-title { color: var(--text); }
.match-players-count {
  margin-left: 4px;
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 0.02em;
}
.match-players-arrow {
  margin-left: auto;
  color: var(--muted);
  font-size: 0.95rem;
}
.daily-session-banner.secondary-match:hover .match-players-arrow {
  color: var(--gold);
}
.match-players-status {
  margin-top: 6px;
  font-size: 0.74rem;
  color: rgba(240,229,204,0.55);
  letter-spacing: 0.02em;
}
.match-players-status .live-dot { width: 7px; height: 7px; }
.match-players-status strong { color: var(--gold); font-weight: 600; }

/* Hide the old primary daily-session-* button styles (no longer used on
   the secondary card — different DOM structure). */
.daily-session-banner.secondary-match .daily-session-join,
.daily-session-banner.secondary-match .daily-session-status:not(.match-players-status) {
  display: none;
}


/* ══════════════════════════════════════════════════════════════════════════
   LANDSCAPE (phone)
   ══════════════════════════════════════════════════════════════════════════
   Every size in the portrait layout is measured against vw, because in
   portrait width is the scarce axis. Rotate the phone and that inverts: width
   becomes abundant and height halves — so every vw-based clamp INFLATES at
   exactly the moment there is least room for it. At 812x375 the hand claimed
   150px of 375, the play area 309px, and the cards were 89x122.

   None of the existing @media rules helped, because all fourteen are
   max-width: a landscape phone is 812px wide, so every mobile rule switched
   off and the desktop layout rendered into a 375px-tall window.

   The gate is orientation AND a short viewport, so a landscape tablet or a
   desktop window keeps the normal layout.
   ────────────────────────────────────────────────────────────────────────── */
/* max-height uses dvh too: with the address bar showing, a phone that reports
   560px of vh can have well under 400px actually visible. */
@media (orientation: landscape) and (max-height: 560px) {

  /* Cards measured against HEIGHT, the axis they have to fit in — and smaller
     than the first attempt, which left the top and bottom cards 26px apart and
     reading as one stack. */
  :root {
    --board-w: clamp(32px, 12.4dvh, 62px);
    --board-h: clamp(48px, 21dvh, 104px);
    /* The HAND has its own variables, and missing them was why your cards stayed
       at portrait size — 73x102 against a 49x68 played card, overflowing their
       own 71px container and spilling up into the table. Slightly larger than a
       played card is right (they are yours, and you tap them), 50% larger is
       not. Width stays above the 44px touch minimum. */
    --card-w: clamp(36px, 12.7dvh, 64px);
    --card-h: clamp(50px, 21.5dvh, 108px);
  }

  /* The hand goes from 40% of the screen to roughly a quarter. */
  .hand-wrap  { height: clamp(56px, 23.5dvh, 98px);
                padding-bottom: calc(2px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
  .hand-cards { height: clamp(52px, 22dvh, 94px); }
  /* No overlap. Thirteen cards at ~56px need 728px and landscape has 812 — the
     fan is a portrait compromise for a 375px screen, and carrying it over hid
     55% of every card on the one layout with room to show them all. This is the
     flat spread the design wanted and portrait could not afford. */
  .hand-cards .card { margin-left: clamp(-10px, calc((100vw - 48px) / var(--hand-n, 13) - var(--card-w)), 6px); }
  .hand-cards .card:first-child { margin-left: 0; }

  /* Wide and shallow: the trick spreads across the axis that has room. */
  /* A WIDE, shallow diamond. The first pass kept portrait's proportions in a
     2.17:1 box, which put the top and bottom cards almost on top of each other
     while the side cards sat in acres of empty felt. The trick should use the
     axis the screen actually has: the sides push out toward their players, and
     the vertical gap grows because the cards shrank. */
  /* Sized from portrait's actual geometry rather than by eye. Portrait leaves
     124px between the two side cards and 28px between top and bottom; these
     numbers reproduce those exact gaps with cards a little larger (55x77 against
     49x68), so the cluster reads the same on both orientations.

       portrait  card 68x96, play area 260x220 → gaps 124 / 28
       landscape card 55x77, play area 234x182 → gaps 124 / 28   */
  .play-area { width: clamp(190px, 26.7vw, 320px); height: clamp(104px, 49dvh, 216px); }

  /* Corner tiles shrink; they are read between plays, not during them. */
  .cnr-rung { min-width: 0; padding: 6px 10px 8px; border-radius: 14px; gap: 4px; }
  .cnr-sym  { width: 62px; height: 62px; font-size: 3.1rem; border-radius: 13px; }
  .cnr-lb   { font-size: 0.4rem; }
  .cnr-score { padding: 7px 10px 8px; gap: 3px; }
  .cnr-rd    { font-size: 0.92rem; }
  .sc-rows .cnr-rd { font-size: 0.96rem; gap: 0 8px; }
  .cnr-side  { font-size: 0.5rem; }
  .cnr-k, .cnr-sr { font-size: 0.42rem; }
  .cnr-trick { padding: 5px 10px 6px; }
  .cnr-tv    { font-size: 0.7rem; }

  /* Bottom row rides just above the hand. Deliberately WITHOUT the bottom inset:
     .hand-wrap is anchored to bottom:0 and absorbs the inset in its own padding,
     so its top edge does not move when a notch appears. Adding it here too
     counted it twice and lifted this row into the side seat. */
  .cnr-dots  { bottom: calc(clamp(64px, 27dvh, 112px)); }
  .cnr-trick { bottom: calc(clamp(64px, 27dvh, 112px));
               left: calc(96px + var(--sa-l, env(safe-area-inset-left, 0px))); }

  /* Seats. Width is the plentiful axis now, so the side pills move outward and
     the top seat tucks into the gap between the two corner tiles. */
  .pos-top    { top: calc(8px + var(--sa-t, env(safe-area-inset-top, 0px))); }
  /* YOUR name pill is gone in landscape. Bottom seat, bottom card and the hand
     all compete for the last third of a 375px screen, and of the three the pill
     is the only one carrying information you already have — it is your own
     name. Its one real job is telling you it is your turn, so the HAND takes
     that over: its top edge lights instead. */
  /* Your name and the trick counter sit side by side on one row just above the
     hand. Stacked in the bottom-left corner they ran into the LEFT SEAT's pill,
     which is vertically centred and reaches down into the same space. */
  .pos-bottom { left: calc(9px + var(--sa-l, env(safe-area-inset-left, 0px))); right: auto; transform: none; min-width: 0; padding: 6px 12px; font-size: 0.8rem;
                bottom: calc(clamp(64px, 27dvh, 112px)); }
  /* Card faces. Every glyph inside a card is sized in vw — so in landscape they
     clamp to their MAXIMUM while the card itself shrinks, and the centre suit
     collides with the corner index. Measured: the centre glyph is 0.49 of the
     card's height here against 0.28 in portrait.

     Sized against dvh instead, which tracks the card, so a card face in
     landscape has the same internal proportions as one in portrait. */
  .pslot .card .csb { font-size: clamp(1rem, 6dvh, 1.9rem); }
  .pslot .card .cc  { font-size: clamp(0.55rem, 2.7dvh, 0.9rem); }
  .csb              { font-size: clamp(0.9rem, 5.4dvh, 1.7rem); }
  .cc               { font-size: clamp(0.5rem, 2.5dvh, 0.85rem); }
  .cc.tl { top: 3px; left: 4px; }
  .cc.br { bottom: 3px; right: 4px; }

  /* No turn cue on the hand at all. It was a 2px line across the full width —
     a loud mark for something the name pill already says, and at that length it
     read as a UI seam rather than a cue. The pill glows; that is the indicator.
     Saying it twice is what made the line feel wrong in the first place. */
  .pos-left, .pos-right { min-height: clamp(52px, 22dvh, 80px); }
  .pos-left   { left: calc(8px + var(--sa-l, env(safe-area-inset-left, 0px))); }
  .pos-right  { right: calc(8px + var(--sa-r, env(safe-area-inset-right, 0px))); }

  /* A toast at 27% lands in the middle of the trick when the screen is this
     short. Across the top instead, in the clear span between the two tiles. */
  body:has(#game:not(.hidden)) .toast-stack { top: calc(8px + var(--sa-t, env(safe-area-inset-top, 0px))); }
  .toast { max-width: min(60%, 300px); padding: 6px 10px; font-size: 0.74rem; }

  /* Voice and React stack horizontally — there is no vertical room for two. */
  .voice-btn { bottom: calc(clamp(64px, 27dvh, 112px)); right: calc(60px + var(--sa-r, env(safe-area-inset-right, 0px))); }
  .react-btn { bottom: calc(clamp(64px, 27dvh, 112px)); right: calc(112px + var(--sa-r, env(safe-area-inset-right, 0px))); }
  /* Icon only. With their labels these are 100-115px wide, and any fixed
     spacing between them is then a guess that the label length can break — it
     did, twice. Without labels they are ~40px and the gap is certain. */
  .voice-btn, .react-btn { padding: 8px 10px; flex-direction: row; gap: 0; }
  .voice-btn .v-label, .react-btn .v-label { display: none; }

  /* Play Card is anchored to .hand-wrap via `bottom: 100%`, which in landscape
     lands it in the middle of the table. Fixed to the viewport instead, centred
     above the hand — the centre is free at that moment by definition, because
     your played card slot is empty until you play. */
  #play-confirm-btn { position: fixed; bottom: calc(clamp(80px, 25dvh, 106px));
                      margin-bottom: 0; padding: 8px 30px; font-size: 0.86rem; border-radius: 9px; }

  .hud-menu { bottom: calc(clamp(112px, 34dvh, 150px)); }
  #react-picker { bottom: calc(clamp(112px, 34dvh, 150px)); right: calc(9px + var(--sa-r, env(safe-area-inset-right, 0px)));
                  min-width: 0; grid-template-columns: repeat(6, 1fr); padding: 6px; }
  .hud-mi   { padding: 7px 11px; font-size: 0.74rem; }
}


/* ══════════════════════════════════════════════════════════════════════════
   SHORT PORTRAIT  (a small phone, or any phone with browser chrome showing)
   ══════════════════════════════════════════════════════════════════════════
   At the END of the file on purpose. These are overrides of .play-area,
   .hand-wrap and .pos-bottom, and a media query contributes NOTHING to
   specificity — written earlier in the file they lose to the base rules on
   source order and do nothing, with no warning.

   The trigger is dvh: a 320x568 phone showing an address bar has ~460px
   visible, and the portrait floors (a 220px play area, a 110px hand) were sized
   for the full 568.
   ────────────────────────────────────────────────────────────────────────── */
@media (orientation: portrait) and (max-height: 700px) {
  :root { --board-w: clamp(42px, 11.6dvh, 96px); --board-h: clamp(58px, 16dvh, 134px); }

  /* Must stay above 2x --board-h or the top and bottom cards overlap each
     other. 40dvh against a 16dvh card leaves 8dvh of gap. */
  .play-area  { height: clamp(150px, 40dvh, 320px); }
  .hand-wrap  { height: clamp(84px, 21dvh, 150px); }
  .hand-cards { height: clamp(76px, 19dvh, 140px); }

  /* Your name leaves the centre column and sits beside the trick counter, as it
     does in landscape: a card, a name pill and a hand do not fit between the
     middle of a 460px screen and its bottom edge, and the name is the one of
     the three that does not have to be there. */
  .pos-bottom { left: calc(9px + var(--sa-l, env(safe-area-inset-left, 0px))); right: auto; transform: none; min-width: 0; padding: 4px 10px;
                bottom: calc(clamp(92px, 23dvh, 158px) + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
  .cnr-trick  { left: calc(96px + var(--sa-l, env(safe-area-inset-left, 0px))); }
  .cnr-trick, .cnr-dots { bottom: calc(clamp(92px, 23dvh, 158px) + var(--sa-b, env(safe-area-inset-bottom, 0px))); }

  /* The top seat clears the compacted tile, and tracks the notch with it. */
  .pos-top  { top: calc(90px + var(--sa-t, env(safe-area-inset-top, 0px))); }
  .zone-top { top: calc(124px + var(--sa-t, env(safe-area-inset-top, 0px))); }

  /* Compact the top tiles. The tile, the top seat's pill and that seat's played
     card all stack in the upper third, and on a 320x568 screen — 47px shorter
     again with a notch — they do not fit at full size. The TILE gives way: it
     is read between plays, the card during them.

     These live here, at the end of the file, for the same reason as everything
     else in this block: a @media rule written above .cnr-score simply loses to
     it on source order, which is exactly how the first attempt at this did
     nothing at all. */
  .cnr-score { padding: 4px 7px 5px; gap: 1px; }
  .cnr-rd, .sc-rows .cnr-rd { font-size: 0.76rem; }
  .cnr-side  { font-size: 0.44rem; }
  .cnr-k, .cnr-sr { font-size: 0.36rem; }
  .cnr-rung  { padding: 5px 8px 7px; }
  .cnr-sym   { width: 46px; height: 46px; font-size: 2.3rem; }

  /* Voice and React are stacked above the overflow button, so they have to be
     expressed as offsets FROM it. Fixed pixel values drift into it the moment
     the row they sit above moves, which is exactly what happened here. */
  .react-btn { bottom: calc(clamp(92px, 23dvh, 158px) + 52px  + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
  .voice-btn { bottom: calc(clamp(92px, 23dvh, 158px) + 110px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
}


/* ══════════════════════════════════════════════════════════════════════════
   EVERY OTHER SCREEN, IN LANDSCAPE
   ══════════════════════════════════════════════════════════════════════════
   Until now the stylesheet had exactly ONE landscape rule and it styled the
   game table. Splash, lobby, dealing, end of round, match end, profile,
   leaderboard, waiting and invite had never been given a landscape treatment
   at all — they were only ever seen in portrait.

   Measured at 812x375: splash overflows by 51px and invite by 49px, and BOTH
   have overflow: visible, so the overflowing part is unreachable. On the splash
   that is the sign-in button. Lobby (468px), profile (382px), match end (86px)
   and end of round (64px) also overflow but do scroll.

   At the end of the file because these override base rules, and a media query
   adds no specificity — the same trap that silently voided two earlier blocks.
   ────────────────────────────────────────────────────────────────────────── */
@media (orientation: landscape) and (max-height: 560px) {

  /* Anything taller than the screen must be reachable. #game is excluded on
     purpose: the table is a fixed layout and a scrollbar there would be a bug,
     not a fix. */
  .screen:not(#game) {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;

    /* These are column flex containers with justify-content: center. When the
       content is taller than the box, centring splits the overflow BOTH ways —
       and the half above the top edge cannot be scrolled to, because there is
       no scroll position above 0. Measured at 844x390: match end put its
       "MATCH WON" headline at top: -113px, end of round at -101px. Making them
       scroll (the previous commit) only ever revealed content BELOW; the part
       above the fold stayed unreachable.

       An earlier attempt set align-items here, which on a column container is
       the CROSS axis — horizontal. Wrong axis: it left-aligned everything and
       fixed nothing.

       `safe center` is exactly this rule — centre until that would overflow,
       then behave like start. flex-start first as the fallback: a browser that
       does not know the `safe` keyword drops that declaration and keeps it. */
    justify-content: flex-start;
    justify-content: safe center;
  }

  /* The hero wordmarks are sized in vw, so on an 812px-wide phone they clamp to
     their maximum — 70px of a 375px screen — which is most of why these panels
     overflow at all. dvh instead: the axis that is actually short. */
  /* In landscape the notch does not go away, it ROTATES onto the side the camera
     is on — 44px of it — and the home indicator becomes a thin bottom strip.
     #game was taught this earlier; nothing else was, so on a notched phone every
     other screen ran its content under the cutout. The audit found it on eight
     of them: the profile back arrow, the waiting-room Leave button, the session
     Register button, both end-screen headlines.

     Both sides, not just the camera side: which side the notch lands on depends
     on which way the player rotated, and nothing tells us that in advance. */
  .screen:not(#game) {
    padding-left:  calc(10px + var(--sa-l, env(safe-area-inset-left, 0px)));
    padding-right: calc(10px + var(--sa-r, env(safe-area-inset-right, 0px)));
  }

  .splash-title  { font-size: clamp(1.7rem, 8dvh, 2.6rem); }
  .game-title    { font-size: clamp(1.5rem, 7dvh, 2.2rem); }
  .dealing-title { font-size: clamp(1.1rem, 4.6dvh, 1.7rem); }
  .match-end-headline { font-size: clamp(1.4rem, 6.5dvh, 2.1rem); }
}


/* ──────────────────────────────────────────────────────────────────────────
   .community-footer-strip is position: fixed at the bottom with z-index 70, so
   it does not sit in any screen's flow — it sits ON TOP of whichever one is
   showing, in BOTH orientations. Hit-testing the centre of every control found
   it covering the lobby's "Deal Cards (Custom)" button, the profile's "Sign
   out", the match-end "Back to Lobby" and both session share buttons. On a
   screen that fits, scrolling cannot rescue them: there is nowhere to scroll.

   Measured 39px; 52 leaves a margin, plus the home indicator underneath.
   #game excluded — the table's own bottom furniture already clears it and the
   strip is hidden in-game.
   ────────────────────────────────────────────────────────────────────────── */
.screen:not(#game) {
  padding-bottom: calc(52px + var(--sa-b, env(safe-area-inset-bottom, 0px)));
}

/* ──────────────────────────────────────────────────────────────────────────
   DESKTOP — bigger corner furniture, seats off the bezel. Nothing else.
   ──────────────────────────────────────────────────────────────────────────
   The layout is deliberately left alone. Two earlier attempts scaled more
   than this — first the seat pills and the top seat, then the whole table
   geometry — and both read as a magnified phone rather than a desktop
   design. The felt, the cards, the play area, the hand and every seat
   position stay exactly as they are on a phone; the only things that change
   are the three corner panels, which were sized in fixed rem (labels at
   0.38-0.5rem, i.e. 6-8px) and are simply unreadable across a desk, and the
   side seat pills, which sat 6px from the bezel.

   Sizes are clamp(floor, vw, ceiling) so they grow smoothly with the window
   and stop before they crowd the table.

   Scoped under #game because the score panel carries state classes
   (.scores-hidden, .sc-rows/.sc-plain/.sc-split) whose two-class selectors
   outrank a bare .cnr-rd however late it appears — an unscoped version of
   this block lost to them and left the score at 9.6px. Media queries add no
   specificity, which is also why this block sits at the end of the file.

   Gated on min-height as well as min-width: a landscape phone can be 900px
   wide, and those are handled by the (orientation: landscape) block above. */
@media (min-width: 900px) and (min-height: 620px) {

  /* ── The three corner panels ────────────────────────────────────────── */
  #game .cnr {
    padding: calc(7px + 0.35vw) calc(9px + 0.45vw) calc(8px + 0.4vw);
    border-radius: clamp(14px, 1.1vw, 22px);
  }
  #game .cnr-rung {
    top:  clamp(14px, 1.5vw, 34px);
    left: clamp(14px, 1.5vw, 34px);
    min-width: clamp(86px, 7vw, 132px);
    gap: 5px;
  }
  #game .cnr-score { top: clamp(14px, 1.5vw, 34px); right: clamp(14px, 1.5vw, 34px); }
  #game .cnr-trick { left: clamp(14px, 1.5vw, 34px); }

  /* The Rung suit is the most-glanced thing on the table. The BOX has to grow
     with the glyph: .cnr-sym is a fixed 62x62 tile, so scaling the font alone
     pushed a 64px+ suit through the top of its own container — the padlock in
     Hidden Rung worst of all, since an emoji fills its em box completely.
     Keep the tile a touch larger than the glyph so the suit sits in it. */
  #game .cnr-sym {
    font-size: clamp(3.6rem, 4.3vw, 5.6rem);
    width:  clamp(84px, 5.7vw, 114px);
    height: clamp(84px, 5.7vw, 114px);
    border-radius: clamp(16px, 1.3vw, 26px);
  }
  /* The lock badge rides the tile's corner, so it scales with it. */
  #game .cnr-sym.is-secret::after {
    font-size: clamp(0.68rem, 0.7vw, 1rem);
    right: -7px; top: -7px; padding: 4px;
  }
  #game .cnr-rung .trump-name { font-size: clamp(0.58rem, 0.62vw, 0.95rem); }

  /* Labels — RUNG, TRICKS, TRICK, US/THEM, and the series line. */
  #game .cnr-lb,
  #game .cnr-k          { font-size: clamp(0.62rem, 0.72vw, 1rem); }
  #game .cnr-side       { font-size: clamp(0.64rem, 0.74vw, 1rem); }
  #game .cnr-sr,
  #game .cnr-sr b       { font-size: clamp(0.62rem, 0.72vw, 0.98rem); }

  /* The numbers. */
  #game .cnr-rd { font-size: clamp(1.15rem, 1.45vw, 2.2rem); }
  #game .cnr-tv { font-size: clamp(1rem, 1.2vw, 1.85rem); }
  /* Scores stay muted until the Double Sar threshold — smaller than a live
     score, but not 9px smaller. */
  #game .scores-hidden .cnr-rd { font-size: clamp(0.8rem, 0.92vw, 1.25rem); }
  #game .scores-hidden .cnr-rd::after { font-size: clamp(0.58rem, 0.66vw, 0.9rem); }
  #game .sc-split .cnr-rd .cnr-side { font-size: clamp(0.56rem, 0.62vw, 0.85rem); }

  /* ── Side seats: off the bezel, and mirroring the top/bottom pills ──── */
  /* They were pressed against the edge, which reads as an accident on a wide
     screen. They were also built to different numbers than the seats above
     and below — 96px long with 9px/4px padding against the horizontal pills'
     104px and 7px/14px — so a side seat came out stubbier and thinner than
     the one at the bottom of the same table. Same length, and the padding
     axes swapped rather than re-guessed, so a side pill is now precisely the
     bottom pill turned ninety degrees.
     Phones keep the slimmer originals: there the narrow pill buys table
     width, which is exactly why it was drawn that way. */
  #game .pos-left  { left:  clamp(16px, 2.4vw, 60px); }
  #game .pos-right { right: clamp(16px, 2.4vw, 60px); }
  #game .pos-left,
  #game .pos-right { min-height: 104px; padding: 14px 7px; }
}

/* ──────────────────────────────────────────────────────────────────────────
   LANDSCAPE PHONES — the HUD was shrunk past readable
   ──────────────────────────────────────────────────────────────────────────
   Tester, 2026-08-15: "bohut small view ha… score window bohut ziada choti kar
   di hai… jab cards land view ma jaen to thora size bara hona chahiay."

   He is right, and the cause is a gap between two blocks. The landscape block
   above shrinks the corner furniture to buy vertical room on a 390px-tall
   phone — sensible in itself — and the desktop block that grows it again is
   gated on (min-width: 900px) AND (min-height: 620px). A landscape handset is
   844x390: wide enough, nowhere near tall enough. So it fell between the two
   and kept the shrunken sizes, with the score label and series line at 6.7px —
   smaller than the same labels in portrait.

   This block sits after both and only widens what is genuinely unreadable.
   The panels are corner furniture: their type can grow a long way before the
   BOX grows enough to crowd the felt, which is the thing landscape is short
   of. Card sizes are raised in dvh so they scale with the handset rather than
   jumping at a breakpoint.

   Verified against 844x390 and 926x428. */
@media (orientation: landscape) and (max-height: 560px) {

  /* ── The corner panels, back to legible ── */
  #game .cnr-k,
  #game .cnr-lb          { font-size: clamp(8.5px, 2.3dvh, 11px); }
  #game .cnr-side        { font-size: clamp(8.5px, 2.3dvh, 11px); }
  #game .cnr-sr,
  #game .cnr-sr b        { font-size: clamp(8.5px, 2.3dvh, 11px); }
  #game .cnr-rd          { font-size: clamp(17px, 4.8dvh, 23px); }
  #game .cnr-tv          { font-size: clamp(13px, 3.6dvh, 18px); }
  #game .scores-hidden .cnr-rd { font-size: clamp(10px, 2.7dvh, 13px); }
  #game .scores-hidden .cnr-rd::after { font-size: clamp(8px, 2.1dvh, 10px); }

  /* ── The trick in the middle: the cards being played are the game ── */
  /* Two board slots stack within .play-area (top edge and bottom edge), so the
     card height must stay under half of it — hence the play area grows too, or
     the trick clips. Landscape has width to spare and height to spend
     carefully, so the cards gain more in width than in height. */
  :root {
    --board-w: clamp(52px, 21dvh, 100px);
    --board-h: clamp(66px, 26.5dvh, 124px);
  }
  #game .play-area { height: clamp(104px, 53dvh, 230px); }

  /* ── The hand, down onto the bottom edge ──────────────────────────────
     It already sat 2px off the edge in a browser, which is why this looked
     fine here and wrong on a phone: the padding also carries
     safe-area-inset-bottom, and in landscape that is the home indicator —
     roughly 20px of felt between the cards and the edge, on the axis with the
     least room to give. Half the inset still clears the indicator (the cards
     are a large target and nothing here is a small tap) and buys that back
     for the table. */
  #game .hand-wrap {
    padding-bottom: calc(1px + var(--rules-band)
                         + var(--sa-b, env(safe-area-inset-bottom, 0px)) / 2);
  }

  /* The rules band costs height on the axis that has none, so landscape runs it
     tighter — 15px against portrait's 20px, and the pills shrink with it. */
  :root { --rules-band: 15px; }
  /* bottom:4px REMOVED (2026-08-26): the strip is top-anchored everywhere
     now, and a landscape bottom-pin fought the top rule — both edges set
     stretches the strip across the whole table. Sizing stays. */
  #game .rules-strip { gap: 3px; }
  #game .rules-strip .rule-pill { font-size: 0.52rem; padding: 1px 6px; letter-spacing: 0.03em; }

  /* ── Your name pill and the trick counter shared a row and collided ── */
  /* Measured at 844x390: the pill ran to x106 and the counter began at x96 —
     ten pixels of overlap, worse the longer the player's name. Cap the name
     and start the counter clear of it, so it holds for any name. */
  #game .pos-bottom .pname { max-width: 68px; overflow: hidden;
                             text-overflow: ellipsis; white-space: nowrap; }
  #game .cnr-trick { left: calc(112px + var(--sa-l, env(safe-area-inset-left, 0px))); }
}

/* ──────────────────────────────────────────────────────────────────────────
   END-OF-ROUND AND MATCH-END IN LANDSCAPE — fit, don't scroll
   ──────────────────────────────────────────────────────────────────────────
   Both screens stack a column of blocks: outcome, title, shame line, sentence,
   scores, buttons, share, summary. That is right in portrait and overflows a
   390px-tall landscape phone by 200px+ — and these are the two screens a
   player is THROWN INTO mid-hand. Nobody chose to be here, so nobody should
   have to scroll to find the score or the button that continues the game.

   Landscape has width where it lacks height, so the column becomes two: what
   happened on the left, the numbers and what to do next on the right. Explicit
   line-based grid areas rather than auto-flow, so the order cannot drift when
   an optional block (share, countdown) appears or hides.

   Share is dropped here on purpose — it is an invitation, not part of the
   round, and it is still there in portrait where there is room for it. */
@media (orientation: landscape) and (max-height: 560px) {

  /* ── End of round ── */
  #end-screen .end-inner {
    display: grid;
    grid-template-columns: 1.15fr 0.85fr;
    align-content: center;
    justify-items: center;
    column-gap: clamp(16px, 3vw, 34px);
    row-gap: 2px;
    max-width: 720px;
    margin: 0 auto;
    padding: 8px 16px;
  }
  #end-screen .end-outcome { grid-area: 1 / 1; }
  #end-screen .end-title   { grid-area: 2 / 1; }
  #end-screen .end-shame   { grid-area: 3 / 1; }
  #end-screen .end-sub     { grid-area: 4 / 1; max-width: 34ch; }
  #end-screen .end-scores      { grid-area: 1 / 2 / 4 / 3; width: 100%; margin: 0; }
  #end-screen #btn-pees        { grid-area: 4 / 2; width: 100%; }
  #end-screen .end-countdown   { grid-area: 5 / 2; }
  #end-screen #btn-back-lobby  { grid-area: 6 / 2; width: 100%; }
  #end-screen .end-log-wrap    { grid-area: 5 / 1 / 7 / 2; width: 100%; }
  #end-screen .end-share       { display: none; }
  /* The stacked column was sized for a tall screen. */
  #end-screen .end-title   { font-size: clamp(1.5rem, 7dvh, 2.1rem); }
  #end-screen .end-sub     { font-size: clamp(0.68rem, 3dvh, 0.82rem); }
  #end-screen .end-outcome { margin-bottom: 2px; }

  /* ── Match end ── */
  #match-end-screen .match-end-inner {
    display: grid;
    grid-template-columns: 1fr 1fr;
    align-content: center;
    justify-items: center;
    column-gap: clamp(18px, 3.5vw, 40px);
    row-gap: 3px;
    max-width: 760px;
    margin: 0 auto;
    padding: 8px 16px;
  }
  #match-end-screen .match-end-headline { grid-area: 1 / 1; font-size: clamp(1.5rem, 7.5dvh, 2.2rem); }
  #match-end-screen .match-end-sub      { grid-area: 2 / 1; }
  #match-end-screen .match-end-scores   { grid-area: 3 / 1; }
  #match-end-screen .match-end-rounds   { grid-area: 1 / 2 / 3 / 3; width: 100%; }
  #match-end-screen #btn-next-match     { grid-area: 3 / 2; width: 100%; }
  #match-end-screen .match-end-inner > .btn-ghost { grid-area: 4 / 2; width: 100%; }
  #match-end-screen .match-share        { display: none; }
  /* The rounds list is the one thing here that can grow without bound. */
  #match-end-screen .met-rounds-list { max-height: clamp(64px, 26dvh, 130px); overflow-y: auto; }
  /* Decoration is the first thing to go when the screen is 360px tall. */
  #match-end-screen .match-end-confetti { display: none; }
  #match-end-screen .match-end-sub { font-size: clamp(0.66rem, 3dvh, 0.8rem); }
}

/* Batch D2 — a spectator's door into the game: bot seat pills are takeable.
   Gold breathing border says "this one is a button", cursor confirms. */
.pinfo.takeable, .takeable { cursor: pointer; }
.takeable { animation: takePulse 2.2s ease-in-out infinite; border-radius: 10px; }
@keyframes takePulse {
  0%, 100% { box-shadow: 0 0 0 1px rgba(201,162,39,0.35); }
  50%       { box-shadow: 0 0 0 3px rgba(201,162,39,0.65); }
}

  /* Partner's surrender ask — rail chip above VOICE, pulsing for attention.
     Deliberately NOT a modal: the overlay used to appear under a finger
     mid-tap and a different card got played (field note 2026-09-12). */
  .concede-ask-btn { position: fixed; bottom: calc(clamp(120px, 20vw, 158px) + 168px + var(--sa-b, env(safe-area-inset-bottom, 0px))); right: calc(8px + var(--sa-r, env(safe-area-inset-right, 0px))); background: var(--surface); border: 1px solid var(--gold); border-radius: 14px; padding: 9px 12px; font-size: 1.15rem; cursor: pointer; z-index: 30; display: flex; flex-direction: column; align-items: center; gap: 4px; color: var(--gold); animation: concede-ask-pulse 1.1s ease-in-out infinite; }
  .concede-ask-btn .v-label { font-size: 0.56rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; }
  .concede-ask-btn:active { transform: scale(0.88); }
  @keyframes concede-ask-pulse {
    0%, 100% { box-shadow: 0 2px 12px rgba(0,0,0,0.5); }
    50%      { box-shadow: 0 0 16px 3px rgba(201,168,76,0.55); }
  }
  @media (prefers-reduced-motion: reduce) { .concede-ask-btn { animation: none; box-shadow: 0 0 14px 2px rgba(201,168,76,0.45); } }

  /* ═══ Compact game layout — phones ≤480px ═══════════════════════════════
     From the approved "Rung Small-Screen Gameplay" canvas (2026-09-18). The
     problem being solved: a 13-card hand in one row leaves ~22-25px per card
     (half the 44px touch minimum), the two 100px corner tiles spend a third
     of a 568px screen before the table starts, and at ≤340px the side pills
     collide with played cards.

     Four moves, all cascade-order overrides (this block stays LAST in the
     file — the base rules above share its specificity):
       1. The top tiles drop to a 64px band; the trick tile joins it in the
          centre slot the rules pills vacate.
       2. The rules go back to the BOTTOM as one tappable row above the
          timer. _fitRulesStrip sees flex-direction:row here and trims by
          row; _syncRulesBand measures the strip's real height + bottom and
          reserves the hand's clearance — no constants to keep in sync.
       3. The hand splits into two stacked rows ≥8 cards (renderAll toggles
          .two-rows) — every card keeps a ≥40px face at 320px.
       4. Everything parked above the hand (⋯, your pill, the FABs) tracks
          one --hand-h variable instead of five copies of the height math. */
  @media (max-width: 480px) {
    /* The whole top band scales FLUIDLY inside the phone range (Abu,
       2026-09-21: on a 430px phone the fixed compact sizes read small).
       Bounded clamps: a 320px SE keeps exactly the sizes tuned there, a big
       phone gets ~20-25% more — never free-scaling. */
    :root { --hud-tile-h: clamp(64px, 17vw, 80px); }
    /* Rung: still the loudest object on the table — and on big phones,
       visibly so. Gold border + glow carry the rest. */
    .cnr-rung { min-width: clamp(64px, 17vw, 80px); padding: 5px 8px 6px; gap: 3px;
                border-radius: 13px; border-color: rgba(201,162,39,0.65);
                box-shadow: 0 0 14px rgba(201,162,39,0.22); }
    .cnr-sym  { font-size: clamp(2rem, 10.5vw, 3rem);
                width: clamp(40px, 13.5vw, 58px); height: clamp(40px, 13.5vw, 58px);
                border-radius: 10px; }
    .cnr-lb   { font-size: clamp(0.46rem, 1.6vw, 0.56rem); }
    /* Scoreboard: ONE centred bar — Us on the left, Them on the right, the
       series line beneath — floating on the felt with no tile chrome (Abu,
       2026-09-19: the stacked corner banners read long and thin; the centre
       has the horizontal room). The whole area stays one tap target for the
       rules modal. */
    .cnr-score { top: calc(11px + var(--sa-t, env(safe-area-inset-top, 0px)));
                 right: auto; left: 50%; transform: translateX(-50%);
                 flex-direction: row; flex-wrap: wrap; justify-content: center; align-items: center;
                 gap: 3px 6px; min-width: 0; min-height: 0; padding: 0;
                 background: none; border: none; backdrop-filter: none; box-shadow: none; }
    .sc-banner { padding: 2px clamp(10px, 3vw, 14px); gap: 7px; }
    .sc-b-n    { font-size: clamp(0.98rem, 3.4vw, 1.2rem); }
    .sc-b-team { font-size: clamp(0.6rem, 2.1vw, 0.74rem); letter-spacing: 0.05em; }
    .cnr-score .match-mini { width: 100%; justify-content: center;
                             margin: 0; padding: 0; font-size: clamp(0.48rem, 1.7vw, 0.58rem); }
    .cnr-score .match-mini .mm-label  { font-size: clamp(0.44rem, 1.55vw, 0.54rem); }
    .cnr-score .match-mini .mm-target { font-size: clamp(0.5rem, 1.75vw, 0.6rem); }

    /* Trick tile: the top-right corner the scoreboard vacated. Overrides
       both stock placements (.tk-bottomleft and the under-score variant). */
    #game .cnr-trick { top: calc(11px + var(--sa-t, env(safe-area-inset-top, 0px)));
                       left: auto; right: calc(9px + var(--sa-r, env(safe-area-inset-right, 0px)));
                       bottom: auto; transform: none;
                       margin-bottom: 0; padding: 5px clamp(11px, 3.2vw, 15px) 6px; }
    .cnr-trick .cnr-tv { font-size: clamp(0.72rem, 3vw, 1rem); }
    .cnr-trick .cnr-k  { font-size: clamp(0.38rem, 1.4vw, 0.48rem); }

    /* Played cards: the design's 60×90, not the stock 68×96 floor — the
       centre stays airy and the side cards clear the rail. */
    #game { --board-w: 60px; --board-h: 90px; }

    /* The minimal card face itself is now GLOBAL (see .cc.br / .csb in the
       base card rules); compact keeps only its pip sizing — the base clamp
       bottoms out too small next to the fatter compact indices. */
    .hand-cards .card .csb, .pslot .card .csb { font-size: 1.7rem; }

    /* Toasts dock under the top band. At the base rule's 27% they sat on the
       played cards — the exact real estate this layout protects. */
    body:has(#game:not(.hidden)) .toast-stack {
      top: calc(16px + var(--hud-tile-h) + var(--sa-t, env(safe-area-inset-top, 0px))); }

    /* Top seat's pill follows the tiles — 50px under whatever height the
       fluid band resolves to. */
    .pos-top { top: calc(50px + var(--hud-tile-h) + var(--sa-t, env(safe-area-inset-top, 0px))); }

    /* Rules: one tappable row at the bottom, directly above the timer — the
       inline left/right that box-mode measured get cleared by _fitRulesStrip
       the moment it sees row mode. */
    .rules-strip { top: auto; bottom: 13px; left: 0; right: 0; height: auto;
                   flex-direction: row; flex-wrap: wrap; justify-content: center;
                   align-items: flex-end; gap: 4px; padding: 0 8px; overflow: visible; }
    .rules-strip .rule-pill { font-size: 0.56rem; padding: 2px 7px; max-width: none;
                              overflow: visible; text-overflow: clip; white-space: nowrap; }

    /* Timer: a real bar, not a hairline. */
    .timer-bar-wrap { height: 6px; background: rgba(255,255,255,0.08); }
    .timer-bar        { box-shadow: 0 0 8px rgba(201,162,39,0.55); }
    .timer-bar.urgent { box-shadow: 0 0 8px rgba(224,85,85,0.6); }

    /* Two-row hand (renderAll adds .two-rows to #h0 and #hw0 at ≥8 cards).
       The rows overlap vertically so they read as one stacked fan; the later
       row paints on top, and .sel's z-index still lifts the chosen card.
       Overlap math: 7 cards of 58px must fit 100vw − 24px of gutters —
       −18px at 320w leaves a 40px face, −8px is the roomy cap. */
    /* Count-aware single-row spacing now lives in the base + 520px rules —
       one law at every size. What stays compact-only: the Play button clears
       a lifted card (.sel rises ~25px above the wrap — −22px translate +
       1.05 scale — and the stock 10px clearance parked the button ON the
       card; field screenshot, 2026-09-19). */
    #play-confirm-btn { margin-bottom: calc(40px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }

    .hand-cards.two-rows { flex-direction: column; align-items: center; height: auto; }
    .hand-cards.two-rows .hand-row { display: flex; align-items: flex-end; }
    .hand-cards.two-rows .hand-row + .hand-row { margin-top: -28px; }
    /* The design's card, not the stock phone card: 54×88 with a modest
       centre pip. The 100px-tall stock card × two rows read as a wall of
       cardboard next to the mockup (Abu, 2026-09-19). Overlap math tracks
       the 54px width (7 × 54 = 378). */
    .hand-cards.two-rows .card { width: 54px; height: 88px; }
    .hand-cards.two-rows .card .cc { font-size: 0.95rem; }
    .hand-cards.two-rows .hand-row .card { margin-left: clamp(-16px, calc((100vw - 24px - 378px) / 6), -8px); }
    .hand-cards.two-rows .hand-row .card:first-child { margin-left: 0; }
    .hand-wrap.two-rows { height: auto; }

    /* One clearance variable for the shelf above the hand. The single-row
       value IS the old clamp — behaviour there is unchanged; the two-row
       value is the measured two-row hand top (172px of cards + the wrap's
       4px, with --rules-band already added by each element's margin-bottom).
       :has() misses only on ancient WebViews, where the shelf sits low but
       everything still works. */
    :root { --hand-h: clamp(120px, 20vw, 158px); }
    body:has(.hand-cards.two-rows) { --hand-h: 158px; }

    /* The rail: small spaced icons, as designed — not labelled slabs. The
       stock VOICE/REACT buttons are ~90px tall columns that reached into the
       played-card zone; icon-only 44px squares leave the table breathing. */
    .voice-btn, .react-btn, .concede-ask-btn {
      width: 44px; height: 44px; padding: 0; border-radius: 12px;
      justify-content: center; font-size: 1.05rem; gap: 0;
    }
    .voice-btn .v-label, .react-btn .v-label, .concede-ask-btn .v-label { display: none; }
    .cnr-dots        { bottom: calc(var(--hand-h) + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
    .pos-bottom      { bottom: calc(var(--hand-h) + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
    .react-btn       { bottom: calc(var(--hand-h) + 52px  + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
    .voice-btn       { bottom: calc(var(--hand-h) + 110px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
    .concede-ask-btn { bottom: calc(var(--hand-h) + 168px + var(--sa-b, env(safe-area-inset-bottom, 0px))); }
    .hud-menu        { bottom: calc(var(--hand-h) + 30px  + var(--sa-b, env(safe-area-inset-bottom, 0px))); }

    /* Played cards keep clear of the side pills and of the taller hand:
       narrower area, centre nudged up. */
    .play-area { width: min(74vw, 300px); height: 210px; top: 43%; }

    /* End-of-round / end-of-match: on short phones (iPhone SE class) the
       COURT!/YOU WIN! headline sat flush against the top edge — give the
       column real air above it and take the display type down a notch so
       the page reads composed, not crammed (field screenshot, 2026-09-19). */
    .end-inner, .match-end-inner {
      padding-top: calc(52px + var(--sa-t, env(safe-area-inset-top, 0px))); }
    .end-title          { font-size: 2.15rem; }
    .match-end-headline { font-size: 2.2rem; }

    /* Side seats: horizontal chips ABOVE their played cards, not vertical
       pills riding the screen edge — the edge pills are what sat on top of
       played cards at ≤340px (canvas "Today" board, measured live). The
       rotated crown/star transforms are reset with the writing mode. */
    .pos-left, .pos-right { writing-mode: horizontal-tb; transform: none;
                            min-height: 0; top: calc(43% - 84px); padding: 5px 11px;
                            max-width: 42vw; overflow: hidden; text-overflow: ellipsis; }
    .pos-left  { left: 8px; }
    .pos-right { right: 8px; }
    .pos-left .psenior, .pos-left .pcrown { transform: none; }

    /* The strip's mode chip + ⓘ badge (compact layout only — hidden on wide
       screens by the rule after this block). Gold: the mode is the headline,
       and the ⓘ says the whole strip opens the rules modal. */
    .rules-strip .mode-pill { color: var(--gold-hi, #EDD06A); font-weight: 700;
                              border-color: rgba(201,162,39,0.5);
                              background: rgba(201,162,39,0.12); }
    .rules-strip .info-pill { width: 15px; height: 15px; padding: 0;
                              display: inline-grid; place-items: center;
                              border-radius: 50%; border-color: rgba(201,162,39,0.55);
                              color: var(--gold-hi, #EDD06A);
                              font-family: Georgia, serif; font-style: italic; font-weight: 700; }
  }
  /* Wider screens keep the strip they had: rules only, no mode chip, no badge
     — up there it competes with the top tiles for width. */
  @media (min-width: 480.02px) {
    .rules-strip .mode-pill, .rules-strip .info-pill { display: none; }
  }

  /* ═══ Lobby bottom tabs — variant E of the Homepage Tabs canvas (chosen
     2026-09-19). Phones only: desktop keeps today's lobby untouched. The
     tab bar is fixed inside #lobby, so it exists exactly when the lobby
     does; Ranks and Profile navigate to the screens that already existed —
     the trophy icon hides here because the tab replaces it. ══════════════ */
  .lobby-tabs { display: none; }
  @media (max-width: 480px) {
    .lobby-tabs { position: fixed; left: 0; right: 0; bottom: 0; z-index: 40;
                  display: flex; align-items: stretch; padding: 7px 4px calc(9px + var(--sa-b, env(safe-area-inset-bottom, 0px)));
                  background: rgba(14,9,11,0.96); backdrop-filter: blur(12px);
                  border-top: 1px solid rgba(255,255,255,0.08); }
    .lt-item { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
               background: none; border: 0; cursor: pointer; font-family: inherit;
               color: #9b9084; font-size: 0.58rem; letter-spacing: 0.1em; text-transform: uppercase;
               padding: 2px 0; min-height: 44px; justify-content: center; }
    .lt-item svg { width: 20px; height: 20px; fill: none; stroke: currentColor;
                   stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
    .lt-item.on { color: var(--gold); }

    /* The lobby breathes: centred wordmark keeps the stage, content clears
       the tab bar, and the blocks stop stacking against the top edge. */
    #lobby .lobby-inner { padding-top: 40px; padding-bottom: 96px; }
    #lobby .game-title { margin-top: 6px; font-size: 2.5rem; }
    #lobby .primary-cta-card { margin-top: 18px; }
    #lobby .lobby-secondary { margin-top: 12px; }
    /* The trophy corner icon is the Ranks tab now; the community strip's job
       moved into the Friends sheet — neither fights the tab bar for the
       bottom edge while the lobby is up. */
    #lobby .lobby-trophy-icon { display: none; }
    body:has(#lobby:not(.hidden)) .community-footer-strip { display: none; }
  }
