/**
 * Least Count \u2014 styles
 * Version notes: v+13 — round summary dialog pop-in animation (.modal.glass.fx). v+12 — lobby bot buttons stay on one line. v+11 — round summary: hand cards smaller (font .82em, still 4 per line) = hand column ~1 card narrower; name column min-width 7.5em. v+10 — lobby 'Offline' tag; reaction row scrolls sideways (12 emoji). v+9 — seat name pill: bot icon in front of the name (.xl-name.has-bot); summary glass blur lightened (smoother on PCs). v+8 — black glass polish (stronger sheen/edge, clearer banner, tighter bottom, LOWEST pill). v+7 — brighter maroon headline, compact banner, less transparent black glass. v+6 — opponent wrong-show headline/icon maroon; right-show row light green. v+5 — summary: transparent banner for opponent wrong show, green banner for your right show, softer wrong-show row. v+4 — black glass variant for the round summary. v+3 — frosted-glass round summary (.modal.glass). v+2 — toast no longer wraps at half the screen width (width:max-content). v+1 — round summary: padding between highlight bar and player name.
 * ---------------------------------------------------------------
 * Design tokens (colors, fonts) live in :root as CSS variables.
 * Sections below are ordered to roughly match the screen flow:
 * header -> setup screen -> score strip -> table -> playing card
 * -> hand & actions -> modal -> game-over banner.
 * ---------------------------------------------------------------
 */
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=IBM+Plex+Mono:wght@500;600&display=swap');
@import url('card-backs.css');

  :root{
    --felt-1:#0d3b2c;
    --felt-2:#124a37;
    --felt-edge:#0a2e22;
    --card-face:#f8f3e7;
    --ink:#20281f;
    --gold:#c9a227;
    --gold-soft:#e6c766;
    --cream:#ece3ce;
    --muted:rgba(236,227,206,0.58);
    --red:#a3372f;
    --danger:#c05a4f;
    --good:#5f9e73;
    --panel:#0f4235;
    --panel-line:rgba(236,227,206,0.14);
    /* Safe-area insets (notch / camera hole / system bars), kept in one place. Env() only: the Capacitor
       --safe-area-inset-* variables must NOT be added on top, because in this app the Android WebView is
       already inset by the native layer, so adding them doubled the top and bottom gaps. */
    --sat:env(safe-area-inset-top,0px);
    --sab:env(safe-area-inset-bottom,0px);
    --sal:env(safe-area-inset-left,0px);
    --sar:env(safe-area-inset-right,0px);
  }

  *{box-sizing:border-box;}
  html,body{margin:0;padding:0;}
  body{
    background: radial-gradient(ellipse at 50% -10%, var(--felt-2), var(--felt-1) 55%, var(--felt-edge) 100%);
    min-height:100vh;
    font-family:'Inter', sans-serif;
    color:var(--cream);
    -webkit-font-smoothing:antialiased;
    /* Mobile browsers treat a press-and-hold (or even a slightly slow tap)
       on any text as a selection gesture, popping up the native
       copy/select toolbar instead of registering the game's click action —
       card ranks, the deck count, log lines, etc. all fell victim to
       this. Lock selection down everywhere by default, then explicitly
       re-enable it only on real form inputs below. The room code used to
       be selectable too, but Android Chrome's Touch-to-Search then
       latched onto it whenever a card was tapped (nearest selectable
       text) — it is tap-to-copy instead (see .code-copy). */
    -webkit-user-select:none;
    -moz-user-select:none;
    -ms-user-select:none;
    user-select:none;
    -webkit-touch-callout:none; /* stops iOS Safari's press-and-hold callout menu too */
  }
  input, textarea{
    -webkit-user-select:text;
    -moz-user-select:text;
    -ms-user-select:text;
    user-select:text;
  }
  @media (prefers-reduced-motion: reduce){
    *{animation-duration:0.001ms !important; transition-duration:0.001ms !important;}
  }

  button{font-family:inherit; cursor:pointer;}
  button:focus-visible, .card[tabindex]:focus-visible{
    outline:2px solid var(--gold-soft);
    outline-offset:2px;
  }

  /* on-screen keyboard open (set by game.js): room for the keyboard so the focused field can scroll above it */
  body.kb-open:not(.xl-active){ padding-bottom:calc(var(--kb, 0px) + 16px); }
  #app{
    max-width:920px;
    margin:0 auto;
    padding:20px 16px 40px;
    min-height:100vh;
  }

  /* ---------- Header ---------- */
  .titlebar{
    display:flex;
    align-items:baseline;
    justify-content:space-between;
    margin-bottom:14px;
    flex-wrap:wrap;
    gap:8px;
  }
  .titlebar h1{
    font-family:'Fraunces', serif;
    font-weight:600;
    font-size:28px;
    letter-spacing:0.3px;
    margin:0;
    color:var(--gold-soft);
  }
  .titlebar .meta{
    font-size:13px;
    color:var(--muted);
    flex-shrink:0;
    white-space:nowrap;
  }
  /* The in-game titlebar packs quite a few controls into one row (room
     code, ping, mute, vibrate, tour, rules, leave). Without an explicit
     wrap + no-shrink policy, a flex row this busy on a narrow phone falls
     back to the browser default of shrinking every child below its own
     content width — which is what turns a "How to play" pill into a
     squashed 3-line-text circle and pushes "Leave" off the right edge
     instead of just flowing onto a second line. */
  .titlebar-controls{
    display:flex;
    gap:10px;
    align-items:center;
    flex-wrap:wrap;
    justify-content:flex-end;
  }
  /* Home screen header: title + Go Pro chip on the first line, the usual controls on the line below. */
  .home-titlebar{align-items:center;}
  /* One line for all controls. Spare width is spread between them (not left empty on one side), the
     "Ping" word is dropped (the value is self-explanatory), and the gaps/side padding are trimmed a little;
     button height - and so the tap target - is unchanged. */
  .home-controls{flex-basis:100%;justify-content:flex-start;gap:12px;}
  @media (max-width:520px){ .home-controls{gap:8px;} }  /* slightly tighter on phones so everything fits one line */
  .home-controls .ping-lbl{display:none;}
  .home-controls .rules-btn{padding-left:12px;padding-right:12px;}
  .ping-badge{
    font-size:12px;
    font-weight:600;
    font-family:'IBM Plex Mono', monospace;
    letter-spacing:0.3px;
    flex-shrink:0;
    white-space:nowrap;
  }
  .rules-btn{
    background:transparent;
    border:1px solid var(--panel-line);
    color:var(--cream);
    border-radius:999px;
    padding:6px 14px;
    font-size:13px;
    flex-shrink:0;
    white-space:nowrap;
  }
  .rules-btn:hover{border-color:var(--gold-soft); color:var(--gold-soft);}
  /* True fixed-size circle for icon-only controls (help, mute, vibrate) —
     distinct from .rules-btn's pill, which only LOOKS roughly circular by
     accident when its content happens to be a single short glyph. Giving
     icon buttons their own explicit width/height stops them from ever
     being squeezed down into a misshapen oval by a crowded row. */
  .icon-btn{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    width:34px;
    height:34px;
    flex-shrink:0;
    background:transparent;
    border:1px solid var(--panel-line);
    color:var(--cream);
    border-radius:50%;
    font-size:15px;
    font-weight:700;
    padding:0;
  }
  .icon-btn:hover{border-color:var(--gold-soft); color:var(--gold-soft);}

  .server-dropdown{
    position:relative;
    display:inline-block;
  }
  .server-dropdown-trigger{
    display:inline-flex;
    align-items:center;
    gap:6px;
  }
  .server-dropdown-chevron{
    font-size:11px;
    transition:transform .15s;
  }
  .server-dropdown.open .server-dropdown-chevron{
    transform:rotate(180deg);
  }
  .server-dropdown-menu{
    position:absolute;
    top:calc(100% + 6px);
    left:0;
    min-width:120px;
    background:var(--panel);
    border:1px solid var(--panel-line);
    border-radius:10px;
    box-shadow:0 8px 24px rgba(0,0,0,0.45);
    overflow:hidden;
    z-index:60;
    display:none;
  }
  .server-dropdown.open .server-dropdown-menu{
    display:block;
  }
  .server-dropdown-option{
    padding:10px 14px;
    font-size:13.5px;
    color:var(--cream);
    cursor:pointer;
    white-space:nowrap;
  }
  .server-dropdown-option:hover{
    background:rgba(201,162,39,0.14);
    color:var(--gold-soft);
  }
  .server-dropdown-option.selected{
    color:var(--gold-soft);
    font-weight:600;
  }
  .server-dropdown-option + .server-dropdown-option{
    border-top:1px solid var(--panel-line);
  }

  .settings-strip{
    display:flex;
    align-items:center;
    gap:8px;
    font-size:11.5px;
    color:var(--muted);
    letter-spacing:0.3px;
    padding:6px 2px 10px;
    flex-wrap:wrap;
  }
  .strip-dot{color:var(--panel-line); font-size:10px;}

  /* ---------- Setup screen ---------- */
  .setup-card{
    background:var(--panel);
    border:1px solid var(--panel-line);
    border-radius:16px;
    padding:36px 30px;
    text-align:center;
    margin-top:40px;
  }
  .setup-card h2{
    font-family:'Fraunces', serif;
    font-size:24px;
    font-weight:600;
    margin:0 0 8px;
    color:var(--card-face);
  }
  .setup-card p{
    color:var(--muted);
    font-size:14.5px;
    line-height:1.6;
    max-width:440px;
    margin:0 auto 26px;
  }
  .opt-row{display:flex; gap:10px; justify-content:center; margin-bottom:26px; flex-wrap:wrap;}
  /* lobby: '+ Add bot' and '+ Fill with bots' share one line at any width / label / scrollbar change */
  .opt-row.bot-row{ flex-wrap:nowrap; }
  .opt-row.bot-row .ghost-btn{ flex:1 1 0; min-width:0; padding-left:10px; padding-right:10px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
  /* ---------- Settings rows: label-left, controls-right (mobile-friendly) ---------- */
  .opt-line{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    margin-bottom:14px;
    flex-wrap:wrap;
  }
  .opt-line-label{
    font-size:13px;
    color:var(--muted);
    letter-spacing:0.2px;
    flex-shrink:0;
  }
  .opt-line .opt-row{
    justify-content:flex-end;
    margin-bottom:0;
    gap:6px;
    flex:1;
  }
  .opt-line-wrap{
    /* Rows with a custom-input cluster (Call Show / penalty / elimination)
       are the ones most likely to need a second line on a narrow phone —
       this just gives that fallback a bit of breathing room instead of
       looking cramped. */
    row-gap:8px;
  }
  /* A short one-line explainer under a compact opt-line toggle — used in
     place of the much taller toggle-item card when a setting doesn't need
     its own bordered box, just a brief note (e.g. Wild Joker on the Game
     tab, which already has a fuller explanation in the Rules tab). */
  .opt-line-caption{
    font-size:11.5px;
    color:var(--muted);
    line-height:1.4;
    margin:-8px 0 14px;
    text-align:left;
    white-space:nowrap;
    overflow:hidden;
    text-overflow:ellipsis;
  }
  /* Home-screen tabs (Game / Rules): segmented control - a dark inset track with a gold pill that slides under the
     active label (the slide is driven by data-active on .tab-bar, see renderHome / setHomeTab in game.js). */
  .tab-bar{
    position:relative;
    display:flex;
    gap:0;
    padding:4px;
    margin-bottom:20px;
    background:rgba(0,0,0,.28);
    border:1px solid var(--panel-line);
    border-radius:14px;
    box-shadow:inset 0 2px 6px rgba(0,0,0,.35);
  }
  .tab-bar::before{
    content:'';
    position:absolute; top:4px; bottom:4px; left:4px;
    width:calc((100% - 8px) / 2);
    border-radius:10px;
    background:linear-gradient(180deg, var(--gold-soft) 0%, var(--gold) 100%);
    box-shadow:0 2px 8px rgba(0,0,0,.4), inset 0 1px 0 rgba(255,255,255,.35);
    -webkit-transform:translateX(0); transform:translateX(0);
    transition:transform .28s cubic-bezier(.3,.8,.3,1), -webkit-transform .28s cubic-bezier(.3,.8,.3,1);
  }
  .tab-bar[data-active="rules"]::before{ -webkit-transform:translateX(100%); transform:translateX(100%); }
  .tab-btn{
    position:relative; z-index:1;
    flex:1;
    background:transparent;
    border:none;
    border-radius:10px;
    color:var(--muted);
    padding:10px 12px;
    font-size:15px;
    font-weight:700;
    letter-spacing:.2px;
    transition:color .2s;
  }
  .tab-btn.active{ color:#1a1405; }
  .tab-btn:hover:not(.active){ color:var(--cream); }

  .opt-label{
    font-size:12px;
    color:var(--muted);
    letter-spacing:0.3px;
    margin-bottom:10px;
  }
  .difficulty-note{
    font-size:13px;
    color:var(--muted);
    max-width:400px;
    margin:-10px auto 22px;
    line-height:1.5;
  }
  .toggle-row{
    display:flex;
    flex-direction:column;
    gap:10px;
    margin-bottom:20px;
    max-width:480px;
    margin-left:auto;
    margin-right:auto;
  }
  .toggle-item{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:16px;
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    border-radius:10px;
    padding:12px 16px;
    text-align:left;
  }
  .toggle-info{ flex:1; }
  .toggle-title{
    font-size:13.5px;
    font-weight:600;
    color:var(--card-face);
    margin-bottom:3px;
  }
  .toggle-desc{
    font-size:12px;
    color:var(--muted);
    line-height:1.5;
  }
  .toggle-btn{
    flex-shrink:0;
    background:var(--felt-edge);
    border:1.5px solid var(--panel-line);
    color:var(--muted);
    border-radius:999px;
    padding:6px 16px;
    font-size:12px;
    font-weight:700;
    letter-spacing:0.5px;
    min-width:54px;
    transition:background .15s, border-color .15s, color .15s;
  }
  .toggle-btn.on{
    background:rgba(201,162,39,0.18);
    border-color:var(--gold);
    color:var(--gold-soft);
  }
  /* one-line toggle-item card (title + (i) + ON/OFF); description is only in the (i) popup */
  .toggle-row.toggle-row-compact{ max-width:none; margin:0 0 14px; }
  .toggle-row-compact .toggle-item{ flex-wrap:nowrap; padding:9px 14px; }
  .toggle-row-compact .toggle-title{ margin-bottom:0; white-space:nowrap; }
  /* ---------- Compact info-toggle row (label + (i) popup + ON/OFF) ----------
     Replaces the old full-width toggle-item card for settings whose
     description doesn't need to be visible at all times — tapping (i)
     reveals it in a small anchored popup instead. */
  .info-toggle-row .opt-line-label{ display:flex; align-items:center; gap:0; }
  .info-tip-wrap{ position:relative; display:inline-flex; align-items:center; }
  .info-btn{
    flex-shrink:0;
    width:16px; height:16px;
    margin-left:6px;
    border-radius:50%;
    border:1px solid var(--panel-line);
    background:rgba(255,255,255,0.06);
    color:var(--muted);
    font-size:10px;
    font-style:italic;
    font-family:'Fraunces', serif;
    line-height:1;
    display:inline-flex;
    align-items:center;
    justify-content:center;
    padding:0;
  }
  .info-btn.open, .info-btn:hover{
    border-color:var(--gold-soft);
    color:var(--gold-soft);
    background:rgba(201,162,39,0.14);
  }
  .info-tooltip{
    position:absolute;
    left:0;
    top:100%;
    margin-top:6px;
    width:max-content;
    max-width:240px;
    background:var(--felt-edge);
    border:1px solid rgba(201,162,39,0.4);
    border-radius:10px;
    padding:9px 11px;
    font-size:12px;
    line-height:1.5;
    color:var(--cream);
    text-align:left;
    white-space:normal;
    box-shadow:0 6px 18px rgba(0,0,0,0.45);
    z-index:5;
  }
  .custom-pts-input{
    width:90px;
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    color:var(--cream);
    border-radius:10px;
    padding:12px 10px;
    font-size:14px;
    font-family:'IBM Plex Mono', monospace;
    font-weight:600;
    text-align:center;
    outline:none;
    transition:border-color .15s;
    -moz-appearance:textfield;
  }
  .custom-pts-input::-webkit-outer-spin-button,
  .custom-pts-input::-webkit-inner-spin-button{-webkit-appearance:none;}
  .custom-pts-input::placeholder{color:var(--muted); font-family:'Inter',sans-serif; font-weight:400; font-size:13px;}
  .custom-pts-input:focus{border-color:var(--gold-soft);}
  .custom-pts-input.active{border-color:var(--gold); color:var(--gold-soft);}
  
  /* ---------- Points settings (Call Show / Wrong show / Eliminated) ----------
     Each setting is a bordered card, same look as the toggle-item cards below it: a title line
     ("Call Show at  pts"), then ONE row of four equal cells - the three preset chips + the custom
     input - so the layout is identical for all three and never wraps on a phone. */
  .pts-card, .set-card, .set-card-block{
    display:block;
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    border-radius:10px;
    padding:10px 14px 12px;
    margin-bottom:14px;
    text-align:left;
  }
  .pts-card .opt-line-label, .set-card .opt-line-label, .set-card-block .opt-line-label{ display:block; margin-bottom:8px; font-size:13.5px; font-weight:600; color:var(--card-face); }
  .opt-unit{ color:var(--muted); font-size:11px; font-weight:600; letter-spacing:.3px; margin-left:3px; }
  .pts-card .opt-row{ display:grid !important; grid-template-columns:repeat(4, minmax(0,1fr)); gap:6px !important; margin:0; justify-content:stretch; flex-wrap:nowrap; }
  .pts-card .opt-btn{ width:100%; min-width:0; padding:9px 0; }
  .pts-card .opt-btn:not(.selected){ background:var(--felt-edge); }
  .pts-card .custom-input-wrap{ min-width:0; }
  .pts-card .custom-input-suffix{ display:none; }
  .pts-card .custom-pts-input{ width:100%; min-width:0; box-sizing:border-box; padding:9px 4px; font-size:13.5px; }
  .pts-card .custom-pts-input:not(.active){ background:var(--felt-edge); }
  .pts-card .custom-pts-input::placeholder{ font-size:12px; }
  /* same card look for the Game-tab settings: one-line cards (title left, control right) ... */
  .set-card{ display:flex; flex-wrap:nowrap; align-items:center; justify-content:space-between; gap:12px; padding:9px 14px; }
  .set-card .opt-line-label{ margin-bottom:0; }
  .set-card .opt-row{ margin:0; flex-wrap:nowrap; gap:6px; }
  .set-card .opt-btn:not(.selected){ background:var(--felt-edge); }
  /* ... a 3-cell variant of the points grid (Bot difficulty) ... */
  .pts-card.pts-3 .opt-row{ grid-template-columns:repeat(3, minmax(0,1fr)); }
  /* ... and the card-back picker inside its own card */
  .set-card-block .opt-line-label{ margin-bottom:6px; }
  .set-card-block .backpick-row{ margin:0; padding-bottom:6px; }
  .set-card-block .backpick-selected{ margin-bottom:0; }
  .custom-input-wrap{
    display:flex;
    align-items:center;
    gap:6px;
  }
  .custom-input-suffix{
    color:var(--muted);
    font-size:13px;
    font-weight:600;
    letter-spacing:0.3px;
  }
  .custom-pts-input.active ~ .custom-input-suffix{
    color:var(--gold-soft);
  }
  .opt-btn{
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    color:var(--cream);
    border-radius:10px;
    padding:14px 22px;
    font-size:14px;
    font-weight:600;
    min-width:150px;
    transition:border-color .15s, transform .1s;
  }
  .opt-btn.selected{border-color:var(--gold); background:rgba(201,162,39,0.14); color:var(--gold-soft);}
  .opt-btn:hover{transform:translateY(-1px);}
  .opt-btn-sm{
    padding:9px 14px;
    min-width:0;
    font-size:13.5px;
  }
  .primary-btn{
    background:var(--gold);
    color:#231c08;
    border:none;
    border-radius:10px;
    padding:14px 30px;
    font-size:15px;
    font-weight:700;
    letter-spacing:0.2px;
  }
  .primary-btn:hover{background:var(--gold-soft);}
  .primary-btn:disabled{opacity:0.4; cursor:not-allowed;}
  .ghost-btn{
    background:transparent;
    border:1px solid var(--panel-line);
    color:var(--cream);
    border-radius:10px;
    padding:12px 24px;
    font-size:14px;
    font-weight:600;
  }
  .ghost-btn:hover{border-color:var(--gold-soft); color:var(--gold-soft);}
  .ghost-btn:disabled{ opacity:.45; pointer-events:none; cursor:default; }   /* e.g. Show while your drop/draw is in flight */

  /* ---------- Seat box ---------- */
  /* One .player-box per seat, absolutely positioned by tableLayout.js. The
     avatar layout (see "TABLE LAYOUT" at the end of this file) restyles it
     as a round avatar; the turn glow / out / left states below still apply. */
  .player-box{
    position:absolute;
    transform:translate(-50%, -50%);
    box-sizing:border-box;
    background:rgba(0,0,0,0.72);
    border:1.5px solid var(--panel-line);
    border-radius:12px;
    padding:8px 10px;
    text-align:center;
    transition:border-color .15s;
    z-index:2;
  }
  /* At rest, your own box is deliberately identical to an opponent's —
     no permanent "this is you" styling any more. The blue identity color
     now appears ONLY while it's actually your turn (.is-you.thinking
     below), so a blue box always, unambiguously means "act now", with
     nothing to visually distinguish it from an opponent the rest of the
     time. */
  /* An opponent's turn gets the gold glow already used for "important/
     active" everywhere else on the table (drop targets, the deal pulse). */
  .player-box.thinking{
    border-color:var(--gold);
    box-shadow:0 0 0 2px rgba(201,162,39,0.55), 0 0 16px 2px rgba(201,162,39,0.4);
    animation:player-turn-glow-gold 1.6s ease-in-out infinite;
  }
  @keyframes player-turn-glow-gold{
    0%, 100%{ box-shadow:0 0 0 2px rgba(201,162,39,0.5), 0 0 14px 2px rgba(201,162,39,0.35); }
    50%{ box-shadow:0 0 0 2px rgba(201,162,39,0.9), 0 0 22px 6px rgba(201,162,39,0.6); }
  }
  /* YOUR OWN turn: the same blue used before, but now it ONLY appears
     here — background included — rather than being a permanent marker.
     Gold always means "an opponent is acting", blue always means "it's
     you, act now", and at rest both look the same plain box.
     (.is-you.thinking has higher specificity than the plain .thinking
     rule above, so this correctly wins on your own box.) */
  .player-box.is-you.thinking{
    background:linear-gradient(145deg, rgba(42,68,102,0.95), rgba(14,22,34,0.95));
    border-color:#6dafe0;
    box-shadow:0 0 0 2px rgba(109,174,224,0.6), 0 0 16px 2px rgba(109,174,224,0.45);
    animation:player-turn-glow-blue 1.6s ease-in-out infinite;
  }
  @keyframes player-turn-glow-blue{
    0%, 100%{ box-shadow:0 0 0 2px rgba(109,174,224,0.55), 0 0 14px 2px rgba(109,174,224,0.4); }
    50%{ box-shadow:0 0 0 2px rgba(109,174,224,0.95), 0 0 22px 6px rgba(109,174,224,0.65); }
  }
  .player-box.is-out{opacity:0.45;}
  /* A player who left mid-game reads the same as an eliminated one —
     faded out of the table — rather than looking like a normal, active
     seat with just a small "player left…" label easy to miss below it. */
  .player-box.has-left{opacity:0.45;}

  /* ---------- Card-back scaled frame ----------
     A pattern is authored once at full card size (60x84, see .card-back
     below) so it always looks identical to the big deck-pile card. Any
     SMALLER place that needs to show it (opponents' mini card-back fan,
     the setup-screen swatches) does NOT get its own cropped copy of the
     gradients \u2014 it wraps a full-size .card-back in a fixed, overflow
     hidden frame and shrinks the whole thing with a CSS transform, via
     the --cb-scale custom property the frame sets inline. This is what
     keeps the mini icons looking like a shrunk version of the real
     design instead of a corner-cropped fragment of it. */
  .cardback-frame{ position:relative; overflow:hidden; }
  .cardback-frame > .card-back{
    position:absolute;
    top:50%; left:50%;
    width:60px; height:84px;
    transform:translate(-50%,-50%) scale(var(--cb-scale, 1));
    transform-origin:center;
  }

  /* ---------- Card-back picker (setup screen, Game tab) ----------
     A wrapping grid works for 6 designs but grows tall and unwieldy once
     there are 12+, especially on a phone. Scrolling sideways instead keeps
     this section's height constant no matter how many designs get added —
     only the individual swatch buttons change, never the row's own layout
     rules. The selected design's name is shown once, below the strip,
     rather than under every swatch, which is what lets each swatch shrink
     down to just its image. */
  .backpick-row{
    display:flex;
    gap:8px;
    overflow-x:auto;
    overflow-y:hidden;
    scroll-snap-type:x proximity;
    -webkit-overflow-scrolling:touch;
    padding:2px 2px 8px;
    margin:0 0 2px;
  }
  .backpick-row::-webkit-scrollbar{height:4px;}
  .backpick-row::-webkit-scrollbar-thumb{background:var(--panel-line); border-radius:4px;}
  .backswatch-btn{
    flex:0 0 auto;
    scroll-snap-align:start;
    background:transparent;
    border:2px solid transparent;
    border-radius:8px;
    padding:3px;
    transition:border-color .15s, transform .1s;
  }
  .backswatch-btn:hover{transform:translateY(-1px);}
  .backswatch-btn.selected{border-color:var(--gold);}
  .backswatch{
    width:30px; height:42px;
    border-radius:5px;
    pointer-events:none;
    display:block;
  }
  .backpick-selected{
    text-align:center;
    font-size:12px;
    font-weight:600;
    color:var(--gold-soft);
    margin-bottom:22px;
  }
  .backswatch-random{
    display:flex;
    align-items:center;
    justify-content:center;
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    font-size:16px;
    box-shadow:0 3px 8px rgba(0,0,0,0.35);
  }
  .backswatch-btn.selected .backswatch-random{border-color:var(--gold);}

  /* ---------- Center piles (now positioned on the table image itself) ---------- */
  /* Deck + discard sit inside the table artwork; tableLayout.js sets
     left/top (as % of .xl-arena) and the --xl-ps scale. */
  .center-piles{
    position:absolute;
    left:50%;
    top:50%;
    transform:translate(-50%, -50%);
    display:flex;
    justify-content:center;
    align-items:flex-start;  /* flex-start not center: prevents box-shadow/animation
                                on one pile from vertically shifting the other */
    gap:36px;
    z-index:1;
  }
  @media (orientation: portrait){
    /* Portrait pill is narrow — side-by-side piles would crowd or overflow
       it, so stack them vertically instead. */
    .center-piles{ flex-direction:column; align-items:center; gap:14px; }
  }
  .pile{
    display:flex;
    flex-direction:column;
    align-items:center;
    text-align:center;
    width:110px;    /* fixed width, not min-width — prevents any child content
                      (hint text, card, glow) from changing the column's footprint
                      and shifting sibling piles horizontally */
    flex-shrink:0;
  }
  /* 10px (was 6px) so the drop-target ring, which reaches ~10px outside the
     card, clears the "Deck" / "Discard pile" label above it. Styled like a
     physical table marker (small-caps, cream, letter-spaced, faint shadow)
     instead of the low-contrast muted grey it used to be. */
  .pile-label{
    font-size:11px;
    font-weight:700;
    color:var(--cream);
    text-transform:uppercase;
    letter-spacing:1.4px;
    margin-bottom:10px;
    text-shadow:0 1px 3px rgba(0,0,0,0.65);
  }
  /* Fixed height + nowrap: hint text never causes the pile column to
     reflow or shift its sibling pile, regardless of how long the text is.
     This prevents the deck pile from moving when the skip-draw hint
     ("Drop — no draw needed!") is longer than the default hint text.
     white-space:nowrap also works correctly in Capacitor Android WebView.
     display:inline-flex + align-items/justify-content center the text
     exactly, regardless of how the pill's fixed height compares to the
     font's own line-height (a height/line-height mismatch was previously
     clipping the text off-center within the pill). */
  .pile-hint{
    display:inline-flex;
    align-items:center;
    justify-content:center;
    font-size:10.5px;
    font-weight:700;
    color:#2a2108;                 /* dark ink instead of gold-on-gold — stays
                                       legible even sitting directly on top of
                                       the bright pulsing drop-target glow */
    background:var(--gold-soft);
    border-radius:999px;
    padding:0 9px;
    margin-top:5px;
    letter-spacing:0.2px;
    height:22px;           /* always occupies same vertical space whether visible or not */
    line-height:1;
    box-sizing:border-box;
    white-space:nowrap;    /* never wraps — keeps pile column width stable */
    overflow:hidden;
    box-shadow:0 1px 4px rgba(0,0,0,0.35);
  }
  .deal-hint{font-size:12px; color:var(--muted); align-self:center; padding:10px;}

  .deckpile{
    width:60px; height:84px;
    display:flex; align-items:center; justify-content:center;
    font-family:'IBM Plex Mono',monospace; font-size:12px;
    transition:border-color .15s, transform .12s;
  }
  /* Plain gold text sitting directly on the pattern was unreadable on
     lighter/busier designs (Sapphire, Houndstooth); a solid pill behind
     it fixed that but then covered up the pattern's own centerpiece
     (visible in Ruby Rays, for example). A hard black outline + glow
     keeps the number legible against ANY design while leaving the
     artwork underneath fully visible. */
  .deckpile-count{
    color:#fdf6e3;
    font-weight:700;
    letter-spacing:0.5px;
    /* A stroke traces each glyph's own outline, unlike the four offset
       copies of the text a text-shadow approach draws \u2014 those copies
       sit close enough at this font size to visually bleed into the
       NEXT digit (e.g. two-digit counts like "87" reading as smeared
       together), especially obvious when the page is pinch-zoomed. */
    -webkit-text-stroke:1.5px #000;
    paint-order:stroke fill;
    text-shadow:0 0 4px rgba(0,0,0,0.7);
  }
  .deckpile.droptarget{
    cursor:pointer;
  }
  .deckpile.droptarget:hover{transform:translateY(-2px);}

  /* Wrapper so the discard card itself can be a drop target */
  .discard-wrap{
    display:block;
    width:60px;     /* matches card width exactly — fixed, never grows with glow/shadow */
    margin:0 auto 10px; /* bottom gap mirrors .deck-stack so both piles' hints sit the
                           same distance below their card, and clear the drop-target ring */
    position:relative; /* anchors the multi-drop-badge */
    transition:transform .12s;
    border-radius:9px;
  }
  .discard-wrap.droptarget{
    cursor:pointer;
    border-radius:9px;
  }
  .discard-wrap.droptarget:hover{transform:translateY(-2px);}
  .deck-stack.deck-ring:hover::after{transform:translateY(-2px);}   /* ring follows the deck's hover lift */

  /* Drop-target highlight, shared by the deck and the discard pile.
     The old glow was a translucent gold box-shadow, which mostly
     disappeared against the green felt (and on the deck it also
     replaced the card-back's own inset border while pulsing). This is a
     separate ring drawn by a pseudo-element instead: a solid bright
     yellow border, a thin dark keyline outside it so it separates from
     the green, and a strong glow — none of which touch the card's own
     box-shadow, so every card-back design keeps its border. Both piles
     use the same rule so they always read as "the same kind of target". */
  /* The deck's ring is drawn on its wrapper (.deck-stack.deck-ring), NOT on
     the .deckpile itself: the card-back designs (card-backs.css) are
     applied to .deckpile and the design is re-rolled each round, so a
     design that uses its own ::after or overflow:hidden would hide the
     ring for the whole round (fixed itself next round). The wrapper has
     no card-back class, so no design can touch it. */
  .deck-stack.deck-ring::after,
  .discard-wrap.droptarget::after{
    content:'';
    position:absolute;
    z-index:3;
    transition:transform .12s;
    top:-5px; left:-5px; right:-5px; bottom:-5px;
    border:3px solid #ffe066;
    border-radius:12px;
    pointer-events:none;
    box-shadow:0 0 0 2px rgba(0,0,0,0.45), 0 0 14px 4px rgba(255,224,102,0.85);
    animation:pulse-droptarget 1s ease-in-out infinite;
  }
  @keyframes pulse-droptarget{
    0%,100%{
      border-color:#ffe066;
      box-shadow:0 0 0 2px rgba(0,0,0,0.45), 0 0 12px 3px rgba(255,224,102,0.75);
    }
    50%{
      border-color:#ffffff;
      box-shadow:0 0 0 2px rgba(0,0,0,0.55), 0 0 26px 9px rgba(255,236,150,1);
    }
  }

  /* Confirms how many cards were just dropped — the pile itself only ever
     shows one card once landed, so this is the lasting visual proof of a
     multi-card drop for anyone who glances at the wrong moment. */
  /* Base state is static (no animation) and fully visible — the app
     re-renders by rebuilding innerHTML, so this element gets torn down
     and recreated on every render() call that happens while it's on
     screen (a drop+draw turn causes several). A static, idempotent
     resting style means recreating it mid-life looks identical to the
     node it replaced, instead of replaying an entrance animation each
     time. Only the brief entrance (.is-entering, applied for the first
     ~260ms) and the exit fade (an inline opacity set from JS as it nears
     its display deadline) are animated — see renderMultiDropBadge(). */
  .multi-drop-badge{
    position:absolute;
    top:-8px;
    right:-10px;
    background:var(--gold);
    color:#231c08;
    font-family:'IBM Plex Mono', monospace;
    font-size:11px;
    font-weight:700;
    line-height:1;
    padding:4px 6px;
    border-radius:999px;
    box-shadow:0 2px 8px rgba(0,0,0,0.4), 0 0 0 2px var(--felt-1);
    transform:scale(1);
    opacity:1;
    pointer-events:none;
  }
  .multi-drop-badge.is-entering{
    animation:badge-pop-in 0.26s ease-out;
  }
  @keyframes badge-pop-in{
    0%{ transform:scale(0.4); opacity:0; }
    60%{ transform:scale(1.15); opacity:1; }
    100%{ transform:scale(1); opacity:1; }
  }
  /* Fade-out is a real CSS animation (not a JS-computed opacity value per
     render) so it plays smoothly every frame via the compositor, instead
     of jumping between whatever few opacity values happened to be
     computed at the app's infrequent render() calls. */
  .multi-drop-badge.is-fading{
    animation-name:badge-fade-out;
    animation-timing-function:linear;
    animation-fill-mode:forwards;
  }
  @keyframes badge-fade-out{
    0%{ opacity:1; transform:scale(1); }
    100%{ opacity:0; transform:scale(0.9); }
  }


  /* Card deal fly-in â handled via JS overlay, no keyframes needed here.
     The .flying-card elements are absolutely positioned in #deal-overlay
     and transitioned in JS. Just ensure hand cards appear cleanly. */
  .hand-card.actionable{}

  /* ---------- Playing card ---------- */
  .card{
    width:calc(60px * var(--card-scale, 1)); height:calc(84px * var(--card-scale, 1));
    border-radius:7px;
    background:var(--card-face);
    color:var(--ink);
    display:flex;
    flex-direction:column;
    justify-content:space-between;
    padding:5px 6px;
    font-family:'IBM Plex Mono', monospace;
    font-weight:600;
    font-size:calc(15px * var(--card-scale, 1));
    box-shadow:0 3px 8px rgba(0,0,0,0.35);
    border:1px solid rgba(0,0,0,0.08);
    position:relative;
    user-select:none;
    flex-shrink:0;
  }
  .card.red{color:var(--red);}
  .card .rank-top{line-height:1; text-align:left;}
  .card .suit-big{
    position:absolute;
    inset:0;
    display:flex;
    align-items:center;
    justify-content:center;
    font-size:calc(22px * var(--card-scale, 1));
    opacity:0.9;
    text-align:center;
  }
  .card .rank-bottom{align-self:flex-end; transform:rotate(180deg); line-height:1; text-align:left;}
  .card.joker{
    font-family:'Fraunces', serif;
    color:var(--gold);
  }
  .card.joker .joker-corner{
    font-size:calc(9px * var(--card-scale, 1));
    letter-spacing:0.5px;
  }
  .joker-center{
    position:absolute;
    inset:0;
    display:flex;
    align-items:center;
    justify-content:center;
    font-size:calc(11px * var(--card-scale, 1));
    letter-spacing:0.5px;
    text-align:center;
    pointer-events:none;
  }
  .card.empty-pile{
    background:rgba(255,255,255,0.04);
    border:1.5px dashed var(--panel-line);
    box-shadow:none;
  }
  .card.wild-card{
    border:1.5px solid var(--gold);
    box-shadow:0 3px 8px rgba(0,0,0,0.35), 0 0 0 1px rgba(201,162,39,0.35), 0 0 10px 1px rgba(201,162,39,0.3);
  }
  .card .wild-tag{
    position:absolute;
    top:calc(-7px * var(--card-scale, 1));
    left:50%;
    transform:translateX(-50%);
    background:var(--gold);
    color:#231c08;
    font-family:'IBM Plex Mono', monospace;
    font-size:calc(8px * var(--card-scale, 1));
    font-weight:700;
    letter-spacing:0.5px;
    padding:calc(1.5px * var(--card-scale, 1)) calc(5px * var(--card-scale, 1));
    border-radius:999px;
    box-shadow:0 1px 4px rgba(0,0,0,0.4);
    white-space:nowrap;
  }
  .wild-strip-tag{
    color:var(--gold-soft);
    font-weight:700;
  }
  /* Wrapper giving the deckpile and the wild-indicator card a shared
     positioning context, so the wild card can sit tucked UNDER the deck
     (overlapping, peeking out from beneath) rather than stacked below it
     with a visible gap. */
  .deck-stack{
    position:relative;
    width:60px;
    height:84px;
    margin-bottom:10px;
  }
  .deckpile{
    position:relative;
    z-index:2; /* stacks on top of the wild card peeking out below it */
  }
  /* Wild Joker indicator: sits behind the deck for the whole round, face
     up so everyone can always see the wild rank at a glance, peeking out
     from underneath rather than floating in its own separate box. The
     card itself is a fixed 60x84 box rotated -90deg (anti-clockwise) in
     place, so the wrapper is sized to its rotated (84x60) footprint —
     without that, the rotated card's actual on-screen box wouldn't match
     its layout box, and it would either get clipped or misaligned.
     Aligned to the left of the deck (not centered) and offset down just
     enough that the deck covers its top corner while its rank stays
     clearly readable in the visible portion below/left of the deck.
     Anti-clockwise (not clockwise) specifically so the WILD tag — which
     sits at the card's top edge before rotation — ends up on the LEFT
     side after rotating, rather than the right. */
  .wild-indicator-wrap{
    position:absolute;
    top:12px;
    left:-33px;
    width:84px;
    height:60px;
    display:flex;
    align-items:center;
    justify-content:center;
    z-index:1;
  }
  .wild-indicator-card{
    transform:rotate(-90deg);
  }
  .wild-label{
    color:var(--gold-soft);
    font-weight:700;
  }

  .hand-card{
    transition:transform .12s;
  }
  /* Only .actionable cards respond to pointer and lift on hover —
     spectator/read-only cards are completely inert visually */
  .hand-card.actionable{cursor:pointer;}
  .hand-card.actionable:hover{transform:translateY(-4px);}
  .hand-card.selected{
    transform:translateY(-10px);
    box-shadow:0 6px 14px rgba(201,162,39,0.55), 0 0 0 2px var(--gold);
  }

  /* ---------- Log ---------- */
  .log{
    background:rgba(0,0,0,0.22);
    border:1px solid var(--panel-line);
    border-radius:10px;
    padding:8px 12px;
    font-size:12.5px;
    color:var(--muted);
    height:64px;
    overflow-y:auto;
    line-height:1.6;
    margin-bottom:16px;
  }
  .log div:last-child{color:var(--cream);}

  /* ---------- Hand & actions ---------- */
  /* ---------- Hand dock (pinned to the bottom of the viewport) ---------- */
  /* Keeps "Your hand" and the action bar reachable at all times on a
     phone, no matter how much content is above it. pointer-events:none on
     the outer dock (re-enabled on the inner content) lets taps pass
     through the transparent top edge of its background gradient, so it
     doesn't create an invisible dead zone over the table above the
     actual cards. (Inside the avatar table layout the dock is in normal
     flow instead - see ".xl-root .hand-dock".) */
  .hand-dock{
    position:fixed;
    left:0;
    right:0;
    bottom:0;
    z-index:60;
    background:linear-gradient(180deg, transparent, var(--felt-1) 26%, var(--felt-1) 100%);
    padding:14px 16px calc(10px + var(--sab));
    pointer-events:none;
  }
  .hand-dock-inner{
    max-width:920px;
    margin:0 auto;
    pointer-events:auto;
  }
  .hand-dock-inner .hand-section{margin-top:0;}

  /* On a real desktop (mouse + wide viewport) the hand doesn't need to be
     pinned to the bottom of the viewport the way it does on a phone —
     there's plenty of room to just show it in normal flow below the
     table, which is how it originally looked before the sticky-dock
     mobile-layout work. Gated on (hover:hover) and (pointer:fine) rather
     than only viewport width, so a phone rotated to landscape (still
     touch-only) keeps the sticky behavior it actually needs, while an
     actual desktop browser window gets the plain in-flow layout.
     */
  @media (hover:hover) and (pointer:fine) and (min-width: 700px){
    .hand-dock{
      position:static;
      background:none;
      padding:0;
      pointer-events:auto;
    }
  }

  .hand-section{margin-top:18px;}
  .hand-header{
    display:flex;
    justify-content:space-between;
    align-items:baseline;
    margin-bottom:8px;
  }
  .hand-header .label{font-size:13px; font-weight:600; color:var(--card-face);}
  .hand-header .total{font-family:'IBM Plex Mono', monospace; font-size:13px; color:var(--gold-soft);}
  .hand-row{
    display:flex;
    gap:calc(8px * var(--card-scale, 1));
    flex-wrap:nowrap;
    justify-content:center;
    margin-bottom:14px;
    min-height:calc(84px * var(--card-scale, 1));
    padding-top:12px; /* room for .hand-card.selected's -10px lift, so it never clips */
  }
  .action-bar{
    display:flex;
    gap:10px;
    flex-wrap:wrap;
    align-items:center;
    min-height:38px; /* keeps the hand dock a consistent height turn to turn, whether this row is a button, a short hint, or a longer one */
  }
  .hint{font-size:12.5px; color:var(--muted); margin-left:auto; text-align:right;}

  /* ---------- Modal ---------- */
  .modal-backdrop{
    position:fixed; inset:0;
    background:rgba(5,20,15,0.72);
    display:flex; align-items:center; justify-content:center;
    padding:20px;
    z-index:160; /* above the flying-card overlays (100+) and the play-history panel (150), below confetti (200) and the tour (300+); a modal must always take priority over the table UI beneath it */
  }
  .modal{
    background:var(--panel);
    border:1px solid var(--panel-line);
    border-radius:16px;
    padding:28px 26px;
    max-width:520px;
    width:100%;
    max-height:85vh;
    overflow-y:auto;
  }
  .modal h2{
    font-family:'Fraunces', serif;
    font-size:22px;
    margin:0 0 6px;
    color:var(--gold-soft);
  }
  /* Round-result banner: emoji + headline + explanation (+ who really had the lowest hand). The caller ("mine") gets a
     big glowing emoji and, via .fx, a pop (right Show) or a shake (wrong Show); everyone else's Show is a quiet fade-in.
     .fx is only present while the animation is young, and --fx-age keeps it continuous across re-renders. */
  .modal .verdict{
    text-align:center;
    padding:14px 12px 12px;
    border-radius:12px;
    margin:10px 0 16px;
    background:rgba(255,255,255,0.06);
  }
  .modal .verdict.win{color:var(--good); border:1px solid rgba(95,158,115,0.4);}
  .modal .verdict.lose{ color:var(--cream); border:1px solid rgba(232,115,90,.6); background:linear-gradient(180deg, rgba(120,32,42,.78), rgba(78,20,28,.78)); }
  .verdict .vd-emoji{ font-size:30px; line-height:1; }
  .verdict .vd-emoji .icon{ vertical-align:0; }
  .verdict.win .vd-emoji{ color:var(--gold-soft); }
  .verdict.lose .vd-emoji{ color:#ff8a70; }
  .verdict.mine .vd-emoji{
    width:76px; height:76px; margin:0 auto 4px; border-radius:50%;
    display:flex; align-items:center; justify-content:center; font-size:44px;
  }
  .verdict.win.mine{ border-color:rgba(230,199,102,.55); background:rgba(201,162,39,.09); }
  .verdict.win.mine .vd-emoji{ background:rgba(201,162,39,.16); box-shadow:0 0 0 2px var(--gold), 0 0 24px rgba(230,199,102,.45); }
  .verdict.lose.mine .vd-emoji{ background:rgba(255,138,112,.14); box-shadow:0 0 0 2px #ff8a70, 0 0 22px rgba(255,138,112,.35); }
  .verdict .vd-head{ font-family:'Fraunces', serif; font-size:18px; font-weight:600; margin-top:6px; }
  .verdict.win.mine .vd-head{ color:var(--gold-soft); }
  .verdict.lose .vd-head{ color:#ffb09c; }
  .verdict.lose .vd-sub{ color:var(--cream); }
  .verdict.win .vd-sub{ color:var(--cream); }
  .verdict .vd-sub{ font-size:13.5px; margin-top:4px; line-height:1.4; }
  .verdict .vd-lowest{
    display:inline-block; margin-top:9px; padding:5px 12px; border-radius:999px;
    font-size:12.5px; color:var(--cream); background:rgba(0,0,0,.25);
  }
  .verdict .vd-lowest b{ color:var(--gold-soft); }
  .verdict.fx.mine .vd-emoji{ animation:vdPop .55s cubic-bezier(.2,1.4,.4,1) both; animation-delay:calc(var(--fx-age, 0ms) * -1); }
  .verdict.fx.lose.mine{ animation:vdShake .5s ease-in-out both; animation-delay:calc(var(--fx-age, 0ms) * -1); }
  .verdict.fx:not(.mine){ animation:vdFade .4s ease-out both; animation-delay:calc(var(--fx-age, 0ms) * -1); }
  @keyframes vdPop{ from{ transform:scale(.2); opacity:0; } to{ transform:scale(1); opacity:1; } }
  @keyframes vdShake{ 0%,100%{ transform:none; } 15%{ transform:translateX(-7px); } 35%{ transform:translateX(7px); } 55%{ transform:translateX(-5px); } 75%{ transform:translateX(4px); } }
  @keyframes vdFade{ from{ opacity:0; transform:translateY(6px); } to{ opacity:1; transform:none; } }
  /* result table: the caller's row is tinted (gold if the Show was right, red if wrong); on a wrong Show the real lowest hand is tagged */
  table.results tr.rs-win td{ background:rgba(230,199,102,.20); }
  table.results tr.rs-lose td{ background:rgba(150,38,50,.62); color:#fff3e6; }
  table.results tr.rs-win td:first-child, table.results tr.rs-lose td:first-child{ padding-left:14px; }
  table.results tr.rs-win td:first-child{ box-shadow:inset 4px 0 0 var(--gold-soft); }
  table.results tr.rs-lose td:first-child{ box-shadow:inset 4px 0 0 #ff8a70; }
  table.results tr.rs-win td, table.results tr.rs-lose td{ font-weight:600; }
  table.results td.rs-name .rs-lowest{ display:block; font-size:10.5px; color:var(--good); letter-spacing:.04em; text-transform:uppercase; }
  /* score pop: the number itself (an inline-block span, so it scales around its own centre) swells, holds, then settles.
     Round pts first, then Total a beat later. */
  .pts-num{ display:inline-block; }
  table.results.fx .pts-num.pop{ animation:vdPts 1.3s ease-in-out both; animation-delay:calc(.55s - var(--fx-age, 0ms)); }
  table.results.fx .pts-num.pop-total{ animation-delay:calc(.95s - var(--fx-age, 0ms)); }
  @keyframes vdPts{
    0%{ transform:scale(1); }
    30%{ transform:scale(1.8); color:var(--gold-soft); }
    65%{ transform:scale(1.8); color:var(--gold-soft); }
    100%{ transform:scale(1); }
  }
  .house-rule-note{
    font-size:12.5px;
    color:var(--gold-soft);
    background:rgba(201,162,39,0.08);
    border:1px solid rgba(201,162,39,0.25);
    border-radius:6px;
    padding:7px 10px;
    margin-bottom:12px;
  }
  table.results{width:100%; border-collapse:collapse; font-size:13.5px; margin-bottom:10px;}
  table.results th, table.results td{
    text-align:left; padding:7px 8px; border-bottom:1px solid var(--panel-line);
  }
  table.results th{color:var(--muted); font-weight:600; font-size:12px;}
  table.results td.num{font-family:'IBM Plex Mono', monospace;}
  /* Round summary: when space is tight the NAME wraps first (even mid-word, min ~6em); the hand
     shows up to 4 cards per line (the min/max widths below) and wraps onto more lines after that. */
  table.results td.rs-name{overflow-wrap:anywhere; word-break:break-word; min-width:7.5em;}
  .rs-bot{ font-size:.95em; margin-right:2px; }
  table.results td.rs-name .rs-takeover{display:block; font-size:10.5px; color:var(--muted); letter-spacing:.04em; text-transform:uppercase;}
  .rs-takeover-inline{ color:var(--muted); }
  table.results td.rs-name .rs-called{display:block; font-size:10.5px; color:var(--gold-soft); letter-spacing:.04em; text-transform:uppercase;}
  table.results td.rs-hand .rs-cards{font-size:.82em; max-width:9.8em; white-space:normal;}   /* cards ~18% smaller, so 4 per line now take ~8em instead of 9.8em (about one card less); the freed space goes to the NAME column. The em-based widths here and in game.js scale with this font-size. */
  @media (max-width:480px){
    table.results{font-size:12.5px;}
    table.results th{font-size:11px;}
    table.results th, table.results td{padding:7px 4px;}
    .modal{padding-left:16px; padding-right:16px;}
  }
  .rules-text{font-size:13.5px; line-height:1.7; color:var(--cream);}
  .rules-text h3{font-size:14px; color:var(--gold-soft); margin:14px 0 4px;}
  .rules-text ul{margin:4px 0 0; padding-left:18px;}

  .champion-banner{
    max-width:520px;  /* same width as .standings-list below it, centred - on a wide screen they used to be 920px vs 520px */
    margin-left:auto; margin-right:auto;
    text-align:center;
    padding:22px 16px 20px;
    margin-bottom:18px;
    border-radius:16px;
    background:radial-gradient(ellipse at 50% -20%, rgba(201,162,39,0.32), rgba(201,162,39,0.08) 60%, transparent 100%), var(--panel);
    border:1px solid var(--gold);
    box-shadow:0 0 0 1px rgba(201,162,39,0.2) inset, 0 8px 28px rgba(0,0,0,0.35);
  }
  .champion-trophy{
    font-size:40px;
    line-height:1;
    margin-bottom:8px;
    filter:drop-shadow(0 3px 8px rgba(201,162,39,0.5));
    animation:trophy-pop 1.4s cubic-bezier(.22,.68,0,1.25) both;
  }
  @keyframes trophy-pop{
    0%{transform:scale(0.2) translateY(10px); opacity:0;}
    35%{opacity:1;}
    100%{transform:scale(1) translateY(0); opacity:1;}
  }
  .champion-text{
    font-family:'Fraunces', serif;
    font-weight:600;
    font-size:19px;
    color:var(--gold-soft);
  }

  /* ---------- Final standings (game-over screen) ---------- */
  .standings-list{
    display:flex;
    flex-direction:column;
    gap:10px;
    max-width:520px;
    margin:0 auto;
  }
  .standing-row{
    display:flex;
    align-items:center;
    gap:14px;
    background:var(--panel);
    border:1.5px solid var(--panel-line);
    border-radius:14px;
    padding:11px 16px;
    transition:transform .15s;
  }
  .standing-row.rank-1{
    border-color:var(--gold);
    background:linear-gradient(135deg, rgba(201,162,39,0.2), var(--panel) 65%);
    box-shadow:0 0 0 1px rgba(201,162,39,0.28) inset, 0 4px 14px rgba(201,162,39,0.18);
  }
  .standing-row.rank-2{
    border-color:#cfd3d6;
    background:linear-gradient(135deg, rgba(207,211,214,0.16), var(--panel) 65%);
    box-shadow:0 0 0 1px rgba(207,211,214,0.22) inset;
  }
  .standing-row.rank-3{
    border-color:#c98a52;
    background:linear-gradient(135deg, rgba(201,138,82,0.14), var(--panel) 65%);
  }
  .standing-row.is-you{ box-shadow:0 0 0 2px var(--gold-soft) inset; }
  .standing-rank{
    flex-shrink:0;
    width:34px;
    text-align:center;
    font-size:21px;
    line-height:1;
  }
  .standing-place-num{
    font-family:'Inter', sans-serif;
    font-size:24px;
    font-weight:700;
    color:var(--gold-soft);
    line-height:1;
  }
  .standing-info{ flex:1; min-width:0; text-align:left; }
  .standing-name{
    display:flex;
    align-items:center;
    gap:6px;
    font-weight:600;
    font-size:14.5px;
    color:var(--card-face);
    white-space:nowrap;
    overflow:hidden;
  }
  .standing-name span:first-child{ overflow:hidden; text-overflow:ellipsis; }
  .standing-sub{
    font-size:11px;
    color:var(--muted);
    margin-top:2px;
    letter-spacing:0.2px;
  }
  .standing-row.rank-1 .standing-sub{ color:var(--gold-soft); font-weight:600; }
  .standing-pts{
    flex-shrink:0;
    font-family:'IBM Plex Mono', monospace;
    font-size:15px;
    font-weight:700;
    color:var(--gold-soft);
  }
  .standing-pts-unit{
    font-size:11px;
    font-weight:500;
    color:var(--muted);
  }

  /* ---------- Debug mode ---------- */
  .rules-btn.debug-active{
    background:rgba(201,162,39,0.15);
    border-color:var(--gold);
    color:var(--gold-soft);
  }

  .debug-panel{
    margin:16px 0 0;
    border:1.5px solid rgba(201,162,39,0.35);
    border-radius:12px;
    background:rgba(0,0,0,0.25);
    padding:16px;
    display:flex;
    flex-direction:column;
    gap:16px;
  }
  .debug-header{
    font-size:12px;
    font-weight:700;
    letter-spacing:1px;
    text-transform:uppercase;
    color:var(--gold-soft);
    border-bottom:1px solid rgba(201,162,39,0.2);
    padding-bottom:10px;
  }
  .debug-section{}
  .debug-label{
    font-size:11.5px;
    color:var(--muted);
    margin-bottom:8px;
    letter-spacing:0.3px;
  }
  .debug-card-row{
    display:flex;
    flex-wrap:wrap;
    gap:5px;
  }
  .debug-card{
    transform:scale(0.82);
    transform-origin:top left;
    margin-right:-11px;
    margin-bottom:-11px;
  }

  /* ---------- Multiplayer: home / lobby / connection UI ---------- */
  .field-label{
    font-size:12px;
    color:var(--muted);
    letter-spacing:0.3px;
    margin-bottom:6px;
    text-align:left;
  }
  .text-input{
    width:100%;
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    color:var(--cream);
    border-radius:10px;
    padding:12px 14px;
    font-size:15px;
    font-family:inherit;
    outline:none;
    transition:border-color .15s;
    margin-bottom:18px;
  }
  .text-input:focus{border-color:var(--gold-soft);}
  .text-input.code-input{
    text-transform:uppercase;
    letter-spacing:4px;
    font-family:'IBM Plex Mono', monospace;
    font-weight:600;
    text-align:center;
  }
  .home-divider{
    display:flex;
    align-items:center;
    gap:12px;
    color:var(--muted);
    font-size:12px;
    margin:22px 0;
  }
  .home-divider::before, .home-divider::after{
    content:'';
    flex:1;
    height:1px;
    background:var(--panel-line);
  }
  .room-code-banner{
    background:var(--felt-1);
    border:1.5px dashed var(--gold);
    border-radius:12px;
    padding:18px;
    margin-bottom:20px;
    text-align:center;
  }
  .room-code-banner .code{
    font-family:'IBM Plex Mono', monospace;
    font-size:34px;
    font-weight:700;
    letter-spacing:8px;
    color:var(--gold-soft);
    cursor:pointer;
  }
  .room-code-banner .hint{
    font-size:12.5px;
    color:var(--muted);
    margin:6px auto 0;      /* the global .hint has margin-left:auto + text-align:right, which skewed this line on wide screens */
    text-align:center;
    max-width:420px;
  }
  .seat-list{
    display:flex;
    flex-direction:column;
    gap:8px;
    margin-bottom:22px;
    text-align:left;
  }
  .seat-row{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:12px;
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    border-radius:10px;
    padding:11px 14px;
    min-height:56px; /* fixed slot height: rows with a Remove button / Bot tag no longer make the list (and Start button) grow */
  }
  .seat-row.you{border-color:var(--gold);}
  .seat-row .seat-info{display:flex; align-items:center; gap:10px;}
  .seat-row .seat-tag{
    font-size:10px;
    font-weight:700;
    letter-spacing:0.5px;
    padding:3px 8px;
    border-radius:999px;
    background:rgba(201,162,39,0.16);
    color:var(--gold-soft);
  }
  .seat-row .seat-tag.bot{background:rgba(236,227,206,0.1); color:var(--muted);}
  .seat-row .seat-tag.off{ margin-left:6px; background:rgba(192,90,79,.18); color:var(--danger); border:1px solid rgba(192,90,79,.55); }
  .seat-row .seat-tag.empty{background:transparent; border:1px dashed var(--panel-line); color:var(--muted);}
  .seat-row .seat-name{font-weight:600; color:var(--card-face); font-size:14px;}
  .seat-row .seat-name.empty{color:var(--muted); font-weight:400; font-style:italic;}
  .seat-row .seat-actions{display:flex; gap:6px;}
  .mini-btn{
    background:transparent;
    border:1px solid var(--panel-line);
    color:var(--muted);
    border-radius:8px;
    padding:5px 10px;
    font-size:11.5px;
    font-weight:600;
  }
  .mini-btn:hover{border-color:var(--gold-soft); color:var(--gold-soft);}
  .connection-banner{
    position:fixed;
    top:0; left:0; right:0;
    background:var(--danger);
    color:#fff;
    text-align:center;
    font-size:13px;
    font-weight:600;
    padding:8px;
    z-index:200;
  }
  /* ===== TOAST: cream card on the green felt, coloured stripe + icon, springs up, timer bar drains ===== */
  .toast{
    --toast-accent:var(--danger);
    position:fixed; left:50%; bottom:calc(20px + env(safe-area-inset-bottom, 0px));
    transform:translateX(-50%);
    display:flex; align-items:center; gap:10px;
    width:max-content; max-width:min(92vw, 420px);   /* left:50% would otherwise cap the width at half the screen and wrap the text */
    padding:11px 16px 14px 18px;
    background:var(--cream); color:#10281d;
    border-radius:14px; overflow:hidden;
    font-size:13.5px; font-weight:600; line-height:1.3;
    z-index:210;
    box-shadow:0 10px 28px rgba(0,0,0,.5), 0 0 0 1px rgba(0,0,0,.08);
    animation:toastIn .38s cubic-bezier(.2,.9,.25,1.25) both;
  }
  .toast.ok{ --toast-accent:#2e8b57; }
  .toast.err{ --toast-accent:#c0453a; }
  .toast::before{ content:''; position:absolute; left:0; top:0; bottom:0; width:5px; background:var(--toast-accent); }
  .toast-ic{
    flex:0 0 auto; width:24px; height:24px; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    background:var(--toast-accent); color:#fff;
  }
  .toast-msg{ min-width:0; overflow-wrap:anywhere; }
  .toast-bar{
    position:absolute; left:0; bottom:0; height:3px; width:100%;
    background:var(--toast-accent); opacity:.55; transform-origin:left center;
    animation:toastBar var(--toast-ms, 2000ms) linear forwards;
  }
  .toast{ cursor:pointer; }
  .toast-x{ flex:0 0 auto; display:flex; opacity:.5; margin-left:2px; }
  /* not in a game: anchored to the TOP so it can never sit over the Join / Create buttons */
  .toast.top{ bottom:auto; top:calc(14px + env(safe-area-inset-top, 0px)); animation-name:toastInTop; }
  @keyframes toastInTop{ from{ opacity:0; transform:translate(-50%,-24px) scale(.94); } to{ opacity:1; transform:translate(-50%,0) scale(1); } }
  .toast.out{ animation:toastOut .2s ease-in forwards; }
  @keyframes toastIn{ from{ opacity:0; transform:translate(-50%,24px) scale(.94); } to{ opacity:1; transform:translate(-50%,0) scale(1); } }
  @keyframes toastOut{ to{ opacity:0; transform:translate(-50%,10px) scale(.97); } }
  @keyframes toastBar{ from{ transform:scaleX(1); } to{ transform:scaleX(0); } }
  .code-copy{
    cursor:pointer;
    border-bottom:1px dotted var(--muted);
  }
  .ready-status{
    font-size:12.5px;
    color:var(--muted);
    text-align:center;
    margin-top:10px;
  }
  .ready-status .count{color:var(--gold-soft); font-weight:700;}
  .countdown{color:var(--gold-soft); font-weight:700; font-family:'IBM Plex Mono', monospace;}
  /* ---------- Spectator / eliminated state ---------- */
  .spectator-banner{
    position:relative;
    overflow:hidden;
    background:linear-gradient(135deg, rgba(20,20,20,0.85) 0%, rgba(40,20,20,0.75) 100%);
    border:1px solid rgba(163,55,47,0.5);
    border-radius:12px;
    padding:16px 20px;
    margin-bottom:18px;
    text-align:center;
    /* Subtle inner glow to give it depth */
    box-shadow:inset 0 1px 0 rgba(255,255,255,0.06), 0 4px 24px rgba(0,0,0,0.4);
  }
  /* Decorative left + right accent lines — like a game UI frame */
  .spectator-banner::before,
  .spectator-banner::after{
    content:'';
    position:absolute;
    top:50%;
    transform:translateY(-50%);
    width:3px;
    height:60%;
    border-radius:2px;
    background:linear-gradient(180deg, transparent, rgba(163,55,47,0.7), transparent);
  }
  .spectator-banner::before{ left:10px; }
  .spectator-banner::after{  right:10px; }

  .spectator-banner-label{
    display:block;
    font-family:'IBM Plex Mono', monospace;
    font-size:10px;
    font-weight:600;
    letter-spacing:2.5px;
    text-transform:uppercase;
    color:rgba(163,55,47,0.7);
    margin-bottom:6px;
  }
  .spectator-banner-title{
    display:block;
    font-family:'Fraunces', serif;
    font-size:18px;
    font-weight:600;
    color:var(--card-face);
    letter-spacing:0.3px;
    margin-bottom:4px;
  }
  .spectator-banner-sub{
    display:block;
    font-size:12px;
    color:var(--muted);
    letter-spacing:0.2px;
  }

  /* ---------- Pre-game loading ring ---------- */
  .load-overlay{
    position:fixed; inset:0; z-index:300;
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px;
    background:radial-gradient(ellipse at 50% 30%, var(--felt-2), var(--felt-edge) 85%);
    text-align:center; padding:24px;
  }
  .load-ring-wrap{ position:relative; width:132px; height:132px; }
  .load-ring{ width:100%; height:100%; }
  .load-track{ fill:none; stroke:var(--panel-line); stroke-width:8; }
  .load-arc{
    fill:none; stroke:var(--gold-soft); stroke-width:8; stroke-linecap:round;
    transition:stroke-dashoffset .25s ease;
  }
  .load-pct{
    position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
    font-family:'IBM Plex Mono', monospace; font-weight:600; font-size:24px; color:var(--gold-soft);
  }
  .load-title{ font-family:'Fraunces', serif; font-size:22px; font-weight:600; color:var(--card-face); }
  .load-sub{ font-size:13.5px; color:var(--muted); }

  /* ---------- Inline setting errors ---------- */
  .field-error{
    color:var(--danger);
    font-size:12.5px;
    margin-top:6px;
    text-align:left;
    width:100%;
  }
  .field-error[hidden]{ display:none; }
  .custom-pts-input.invalid{ border-color:var(--danger); color:var(--danger); }

  /* ---------- Onboarding tour ---------- */
  /* Lives outside #app, appended directly to <body>, so the full-rebuild
     render() (and the partial outerHTML patches in actionToggleCard) never
     touch or destroy it. It re-measures and repositions itself after every
     such DOM change instead of relying on a persistent node reference. */
  #onboarding-overlay{
    position:fixed; inset:0; z-index:400;
    pointer-events:none; /* re-enabled selectively on the backdrop + card below */
  }
  .ob-backdrop{
    position:absolute; inset:0;
    pointer-events:auto; /* blocks taps on the game underneath; the dimming itself comes from .ob-spotlight's huge box-shadow spread below */
    background:transparent;
  }
  .ob-spotlight{
    position:absolute;
    border-radius:10px;
    box-shadow:0 0 0 4000px rgba(5,20,15,0.68), 0 0 0 2px var(--gold-soft), 0 0 18px rgba(201,162,39,0.55);
    transition:left .22s cubic-bezier(.22,.68,0,1), top .22s cubic-bezier(.22,.68,0,1),
               width .22s cubic-bezier(.22,.68,0,1), height .22s cubic-bezier(.22,.68,0,1);
    pointer-events:none;
  }
  /* 'try it' steps: the spotlighted element is really tappable (see renderTourStep) and the ring pulses */
  .ob-spotlight.ob-interactive{ pointer-events:auto; cursor:pointer; animation:obTapPulse 1.3s ease-in-out infinite; }
  @keyframes obTapPulse{
    0%,100%{ box-shadow:0 0 0 4000px rgba(5,20,15,0.68), 0 0 0 2px var(--gold-soft), 0 0 14px rgba(201,162,39,0.45); }
    50%{ box-shadow:0 0 0 4000px rgba(5,20,15,0.68), 0 0 0 4px #ffe066, 0 0 26px rgba(255,224,102,0.9); }
  }
  .ob-tap-note{ display:inline-block; margin-top:2px; color:var(--gold-soft); font-weight:700; }
  .ob-card{
    position:absolute;
    pointer-events:auto;
    width:min(300px, calc(100vw - 32px));
    background:var(--panel);
    border:1px solid var(--gold);
    border-radius:14px;
    padding:16px 18px;
    box-shadow:0 10px 32px rgba(0,0,0,0.5);
    transition:left .22s cubic-bezier(.22,.68,0,1), top .22s cubic-bezier(.22,.68,0,1);
  }
  .ob-step-count{
    font-size:11px;
    letter-spacing:0.5px;
    color:var(--gold-soft);
    font-weight:700;
    text-transform:uppercase;
    margin-bottom:6px;
  }
  .ob-title{
    font-family:'Fraunces', serif;
    font-size:17px;
    font-weight:600;
    color:var(--card-face);
    margin:0 0 6px;
  }
  .ob-body{
    font-size:13.5px;
    line-height:1.55;
    color:var(--cream);
    margin:0 0 14px;
  }
  .ob-actions{
    display:flex;
    align-items:center;
    justify-content:space-between;
    gap:10px;
  }
  .ob-skip{
    background:transparent;
    border:none;
    color:var(--muted);
    font-size:12.5px;
    padding:6px 2px;
    text-decoration:underline;
    text-underline-offset:2px;
  }
  .ob-skip:hover{ color:var(--cream); }
  .ob-nav{ display:flex; gap:8px; }
  .ob-btn{
    background:var(--felt-1);
    border:1.5px solid var(--panel-line);
    color:var(--cream);
    border-radius:8px;
    padding:8px 14px;
    font-size:13px;
    font-weight:600;
  }
  .ob-btn:hover{ border-color:var(--gold-soft); color:var(--gold-soft); }
  .ob-btn.primary{
    background:var(--gold);
    border-color:var(--gold);
    color:#231c08;
  }
  .ob-btn.primary:hover{ background:var(--gold-soft); }
  /* gated step (e.g. 'Recent moves'): Next is disabled until the player does the action */
  .ob-btn:disabled{ opacity:.38; cursor:not-allowed; pointer-events:none; filter:grayscale(.4); }
  .ob-tap-note.done{ color:var(--good, #7fc79a); }

  .replay-tour-btn{
    background:transparent;
    border:1px solid var(--panel-line);
    color:var(--cream);
    border-radius:999px;
    padding:6px 14px;
    font-size:13px;
    flex-shrink:0;
    white-space:nowrap;
  }
  .replay-tour-btn:hover{ border-color:var(--gold-soft); color:var(--gold-soft); }

  /* Replay-tour banner at the top of the How-to-play dialog. Sticky, so it stays reachable while the rules scroll.
     It spans the full width of the dialog (negative margins cancel the .modal side padding) and has a SOLID
     background (tint over --panel), so text scrolling underneath never shows through. */
  .rules-tour-banner{
    position:sticky; top:-28px; z-index:2; /* -28px = the .modal top padding, so it sticks flush with the dialog edge */
    display:flex; align-items:center; justify-content:space-between; gap:12px;
    margin:0 -26px 10px; padding:9px 26px;
    background:linear-gradient(rgba(214,176,58,.13), rgba(214,176,58,.13)), var(--panel);
    border-top:1px solid rgba(214,176,58,.25); border-bottom:1px solid rgba(214,176,58,.25);
  }
  .rules-tour-label{ font-size:12.5px; line-height:1.3; color:var(--cream); }
  .rules-tour-banner .replay-tour-btn{ border-color:rgba(214,176,58,.55); color:var(--gold-soft); background:rgba(214,176,58,.16); font-weight:600; }
  .rules-tour-banner .replay-tour-btn:hover{ background:rgba(214,176,58,.26); }
  @media (max-width: 480px){
    .rules-tour-banner{ margin-left:-16px; margin-right:-16px; padding-left:16px; padding-right:16px; }
  }
  /* very narrow phones: label above, button full width */
  @media (max-width: 360px){
    .rules-tour-banner{ flex-direction:column; align-items:stretch; gap:6px; text-align:center; }
    .rules-tour-banner .replay-tour-btn{ width:100%; }
  }
  /* short landscape: one slim row */
  @media (orientation:landscape) and (max-height:520px){
    .rules-tour-banner{ padding-top:5px; padding-bottom:5px; }
    .rules-tour-label{ font-size:11.5px; }
    .rules-tour-banner .replay-tour-btn{ padding:4px 12px; font-size:12px; }
  }

  @media (max-width: 480px){
    .ob-card{ padding:14px 16px; }
  }

  @media (orientation:landscape) and (max-height:520px){
    .ob-card{ width:min(270px, calc(100vw - 32px)); padding:10px 14px; }
    .ob-card .ob-body{ font-size:13px; line-height:1.35; margin-bottom:8px; }
    .ob-card .ob-title{ font-size:17px; margin-bottom:4px; }
  }



  /* =====================================================================
     NUMBER-OF-PLAYERS DROPDOWN (home screen, Game tab)
     Reuses the .server-dropdown look; only the menu geometry differs.
     ===================================================================== */
  .num-players-dd .server-dropdown-menu{
    left:auto; right:0; min-width:84px; max-height:240px;
    overflow-y:auto; -webkit-overflow-scrolling:touch;
  }

  /* =====================================================================
     TABLE LAYOUT (avatar table, 2..16 players)  -  see public/tableLayout.js
     Full-screen play area, minimal avatars around the screen edge,
     deck/discard in the middle, hand docked at the bottom.
     ===================================================================== */
  /* page must not scroll while the full-screen table is showing */
  html.xl-active, body.xl-active{ overflow:hidden; overscroll-behavior:none; }
  /* a dialog / bottom sheet is open: the page behind must not scroll (class toggled by game.js) */
  html.modal-open, body.modal-open{ overflow:hidden; overscroll-behavior:none; }
  .modal, .ads-sheet{ overscroll-behavior:contain; }
  /* Lock the page to the screen while the big table is up: nothing can scroll or widen it. */
  body.xl-active{ position:fixed; inset:0; width:100%; height:100%; }

  /* ZOOM FIX. game.js flies cards on an overlay that is `position:absolute` with
     overflow:visible. When a player on the RIGHT drops 2+ cards, the fanned cards
     stick out past the right edge, the page becomes wider than the screen
     (measured: 390px -> 446px) and the phone browser rescales the whole page for
     the duration of the flight, then snaps back. Clipping the overlay to the screen
     means nothing can ever widen the page. (Overlay coordinates are unchanged:
     the body no longer scrolls, so fixed == absolute here.) */
  body.xl-active > div[data-flight-overlay]{
    position:fixed !important; left:0 !important; top:0 !important;
    width:100% !important; height:100% !important;
    overflow:hidden !important; contain:paint;
  }
  body.xl-active > canvas{ max-width:100%; }

  .xl-root{
    position:fixed; inset:0; z-index:5;
    display:flex; flex-direction:column;
    padding-top:var(--sat);
    padding-left:var(--sal);
    padding-right:var(--sar);
    /* Option 3 (vignette): a transparent-centre radial darkening is layered ABOVE the felt gradient, so the
       edges/corners (where the avatars sit) go darker while the centre stays bright. It is a background layer,
       not a pseudo-element, so it can never sit over the table, avatars or buttons or block taps. */
    background:
      radial-gradient(ellipse at 50% 45%, rgba(0,0,0,0) 48%, rgba(0,0,0,.30) 78%, rgba(0,0,0,.60) 100%),
      radial-gradient(ellipse at 50% 45%, var(--felt-2), var(--felt-1) 60%, var(--felt-edge) 100%);
    touch-action:manipulation;
  }

  /* ---- full-screen toggle button ---- */
  .xl-fs-btn{ display:inline-flex; align-items:center; justify-content:center; padding:4px 9px; line-height:0; }
  .xl-fs-btn svg{ width:15px; height:15px; display:block; }
  @media (max-width:420px){
    .xl-topbar{ gap:5px; padding-left:8px; padding-right:8px; }
    .xl-topbar .rules-btn{ padding-left:8px; padding-right:8px; }
  }

  /* ---- slim top bar ---- */
  .xl-topbar{
    flex-shrink:0; display:flex; align-items:center; gap:8px;
    padding:6px 10px 2px; min-height:34px; font-size:12px; color:var(--muted);
  }
  .xl-topbar .xl-sp{ flex:1; }
  .xl-topbar .rules-btn{ padding:4px 10px; font-size:12px; }
  .xl-topbar .ping-badge{ font-size:11px; }
  /* FIXED-size top bar: the room/round label is always exactly two lines (never re-wraps), and the ping badge keeps one
     constant width whatever the value ('8ms' .. '1234ms'), so a changing ping can no longer resize the table. */
  .xl-topbar .xl-room{ display:flex; flex-direction:column; flex-shrink:0; white-space:nowrap; line-height:1.15; }
  .xl-topbar .ping-badge{ display:inline-block; flex-shrink:0; white-space:nowrap; }
  .ping-val{ display:inline-block; min-width:6ch; text-align:right; } /* mono font: 6ch fits '1234ms' */
  @media (max-width:480px){
    .xl-topbar .ping-lbl{ display:none; } /* narrow phones: just the value, saves ~5 characters of width */
    .xl-topbar .ping-val{ min-width:5ch; }
  }
  .xl-root .settings-strip{
    flex-shrink:0; flex-wrap:nowrap; overflow-x:auto; white-space:nowrap;
    padding:0 10px 4px; font-size:10.5px; scrollbar-width:none;
  }
  .xl-root .settings-strip::-webkit-scrollbar{ display:none; }

  /* ---- arena: everything between the top bar and the hand ---- */
  .xl-arena{ position:relative; flex:1 1 auto; min-height:0; }

  /* deck + discard in the middle, shrunk (via --xl-ps) when space is tight */
  .xl-arena .center-piles{
    flex-direction:row; gap:18px; align-items:flex-start;
    transform:translate(-50%,-50%) scale(var(--xl-ps,1));
    transform-origin:center center;
  }
  .xl-arena .pile{ width:96px; }
  /* phones (portrait): deck above discard, like the original portrait table */
  @media (orientation: portrait){
    .xl-arena .center-piles{ flex-direction:column; align-items:center; gap:8px; }
  }

  /* pill table artwork (portrait only) — sized/placed by JS so the pill sits
     inside the ring of avatars; avatars + piles are drawn above it */
  .xl-table{ position:absolute; z-index:0; pointer-events:none; user-select:none; display:none; max-width:none;
             filter:drop-shadow(0 6px 12px rgba(0,0,0,.45)); }
  .xl-table.on{ display:block; }
  .xl-table-css{                      /* fallback if table-portrait.jpg is missing */
    border-radius:999px; background:radial-gradient(ellipse at 50% 40%, #14583f, #0d3b2c 70%);
    border:6px solid #07120e; box-shadow:0 0 0 3px rgba(201,162,39,.35), inset 0 0 30px rgba(0,0,0,.45);
  }
  .xl-arena{ overflow:hidden; }

  /* ---- minimal avatar (keeps .player-box so deal / move animations still find it) ---- */
  .player-box.xl-seat{
    width:var(--av,48px); height:var(--av,48px);
    padding:0; background:none; border:0; border-radius:50%;
    pointer-events:none; z-index:2; transition:none;
  }
  .player-box.xl-seat.is-you.thinking{ background:none; }
  .xl-av{
    width:100%; height:100%; border-radius:50%;
    display:flex; align-items:center; justify-content:center;
    font-family:'Fraunces', serif; font-weight:700; color:#fff8e6;
    font-size:calc(var(--av,48px) * .4); letter-spacing:.3px;
    background:radial-gradient(circle at 30% 25%, hsl(var(--h) 55% 46%), hsl(var(--h) 55% 22%));
    border:2px solid rgba(236,227,206,.55);
    box-shadow:0 2px 6px rgba(0,0,0,.5);
  }
  .xl-seat.is-you .xl-av{ border-color:var(--gold-soft); }
  .xl-seat.is-out .xl-av,
  .xl-seat.has-left .xl-av{ filter:grayscale(1); font-size:calc(var(--av,48px) * .26); }
  .xl-seat.has-left .xl-av{ letter-spacing:.4px; }
  .xl-name{
    position:absolute; top:calc(100% + 2px); left:50%; transform:translateX(-50%);
    max-width:var(--lw,64px); padding:0 5px; border-radius:6px;
    background:rgba(0,0,0,.5); color:var(--cream);
    font-size:10px; font-weight:600; line-height:14px; height:14px;
    white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
  }
  /* bot seat: icon + name form ONE pill; the name part ellipsizes, the icon never shrinks */
  .xl-name.has-bot{ display:flex; align-items:center; gap:3px; }
  .xl-name.has-bot .icon{ flex:0 0 auto; color:var(--bot-icon); }
  .xl-name .xl-name-t{ min-width:0; overflow:hidden; text-overflow:ellipsis; }
  .xl-seat.is-you .xl-name{ color:var(--gold-soft); }
  .xl-seat.thinking .xl-name{ color:var(--gold-soft); background:rgba(0,0,0,.75); }
  .xl-pts, .xl-cnt, .xl-tag{
    position:absolute; display:flex; align-items:center; gap:3px;
    padding:0 4px; height:15px; border-radius:8px;
    background:rgba(0,0,0,.8); border:1px solid var(--panel-line);
    font-size:9.5px; font-weight:700; line-height:1; white-space:nowrap;
  }
  .xl-pts{ top:-6px; left:-8px; color:var(--gold-soft); font-family:'IBM Plex Mono', monospace; }
  .xl-cnt{ top:-6px; right:-8px; color:var(--cream); }
  .xl-cnt::before{
    content:''; width:7px; height:10px; border-radius:1.5px;
    background:var(--card-face); border:1px solid rgba(0,0,0,.4);
  }
  .xl-tag{ bottom:-4px; right:-8px; padding:0 3px; font-size:10px; height:19px; border-radius:10px; }
  .xl-tag.off{ border-color:rgba(192,90,79,.7); }
  .xl-tag .icon-alert{ color:var(--danger); }   /* only the warning icon is red - a muted-speaker icon next to it keeps its own colour */

  /* ---- one-line ticker + hand ---- */
  .xl-log{
    flex-shrink:0; height:34px; padding:2px 12px 2px; text-align:center;
    display:flex; flex-direction:column; justify-content:center;
    font-size:11.5px; line-height:1.3; color:var(--cream); overflow:hidden;
  }
  /* The log text is ONE wrapping block (.xl-log-text) holding one <span> per entry, so a drop + the draw that follows
     flow together and wrap onto a second line instead of being cut off with '...'. Clamped to 2 lines. */
  .xl-log-text{ display:-webkit-box; -webkit-box-orient:vertical; -webkit-line-clamp:2; overflow:hidden; white-space:normal; overflow-wrap:anywhere; line-height:1.2; }
  .xl-log-text > span + span{ color:var(--gold-soft); margin-left:.4em; }
  .xl-log{ position:relative; cursor:pointer; -webkit-tap-highlight-color:transparent; }
  /* The line looks like a tappable bar (soft box) and carries a clear 'Moves' pill with a chevron: tapping it
     opens the list of recent moves. Side padding is kept to what the clock icon (left) and the pill (right) really
     need, so the text gets as much width as possible. */
  .xl-log{ margin:0 8px; padding-top:1px; padding-bottom:1px; padding-left:28px; padding-right:68px; background:rgba(0,0,0,.22); border:1px solid rgba(214,176,58,.22); border-radius:10px; }
  .xl-log-more{
    position:absolute; right:8px; top:50%; transform:translateY(-50%); display:inline-flex; align-items:center; gap:2px;
    padding:3px 8px; border-radius:999px; font-size:10px; font-weight:700; letter-spacing:.3px; line-height:1; white-space:nowrap;
    color:var(--gold-soft); background:rgba(214,176,58,.16); border:1px solid rgba(214,176,58,.55);
  }
  .xl-log-clock{
    position:absolute; left:10px; top:50%; transform:translateY(-50%);
    font-size:13px; line-height:1; color:var(--gold-soft); opacity:.8; display:flex;
  }
  .xl-log-more .icon{ vertical-align:0; }
  .xl-root .hand-dock{
    position:relative; left:auto; right:auto; bottom:auto; flex-shrink:0; z-index:3;
    padding:6px 10px calc(8px + var(--sab));
    /* Option 1 (dark fade at the bottom): transparent at the top of the dock -> ~70% black at the bottom.
       The dock is only as tall as the hand area, so the fade shrinks automatically in landscape. */
    background:linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.28) 40%, rgba(0,0,0,.70) 100%);
    pointer-events:auto;
  }
  /* FIXED-height action bar + header: hint text wrapping, 'Call Show' button,
     'Dealing…' hint etc. can no longer change the dock height, so the arena
     (and therefore avatar size / positions) stays put during dealing & turns */
  .xl-root .action-bar{ height:40px; min-height:40px; max-height:40px; overflow:hidden; align-content:center; flex-wrap:nowrap; }
  /* the Show button must fit inside the fixed-height bar (the global .ghost-btn is 44px tall) */
  /* SHOW button: centred at the bottom of the screen and made as large as the fixed bar allows. The bar's
     height is unchanged (40px / 30px on short phones), so the hand dock and the table do not shrink. */
  .xl-root .action-bar{ justify-content:center; }
  .xl-root .action-bar .ghost-btn{
    height:38px; min-height:0; min-width:150px; padding:0 34px;
    font-size:16px; font-weight:700; letter-spacing:.3px; line-height:1; white-space:nowrap; flex-shrink:0;
    border-color:var(--gold); color:var(--gold-soft); background:rgba(201,162,39,.12);
  }
  .xl-root .action-bar .hint{ flex:1 1 0; min-width:0; }
  .xl-root .hint{ font-size:11.5px; line-height:1.2; }
  .xl-root .hand-header{ height:18px; align-items:center; }
  .xl-root .hand-row{ position:relative; }
  .xl-root .deal-hint{ position:absolute; left:0; right:0; top:50%; transform:translateY(-50%); text-align:center; }
  .xl-root .hand-dock-inner{ max-width:560px; }
  .xl-root .hand-dock-spacer{ display:none; }

  /* HAND CARD SIZE is owned by this layout (JS sets --xl-hs on .xl-root from the screen height and the
     number of cards). !important makes it win over game.js's own inline --card-scale, so it no longer
     matters how / whether game.js sizes the hand: cards stay in proportion with the deck & discard pile. */
  .xl-root .hand-row{ --card-scale: var(--xl-hs, 1) !important; }
  /* The row always reserves the height of the LARGEST cards the screen allows (--xl-hs-max, set by tableLayout.js),
     so the dock keeps one height whatever the card count; smaller cards are centred in the reserved space.
     --xl-hpad = the row's padding-top (min-height is border-box). */
  .xl-root .hand-row{
    --xl-hpad:12px; align-items:center;
    min-height:calc(84px * var(--xl-hs-max, var(--xl-hs, 1)) + var(--xl-hpad));
  }

  /* shorter portrait phones (e.g. 360x640): tighten the dock so the table gets the room */
  @media (max-height:700px) and (orientation:portrait){
    .xl-topbar{ min-height:30px; padding-top:4px; }
    .xl-log{ height:30px; padding-top:0; padding-bottom:0; font-size:11px; }
    .xl-log-text{ line-height:1.15; }
    .xl-root .hand-dock{ padding-top:3px; padding-bottom:calc(5px + var(--sab)); }
    .xl-root .hand-header{ height:16px; }
    .xl-root .hand-header .label, .xl-root .hand-header .total{ font-size:12px; }
    .xl-root .action-bar{ height:30px; min-height:30px; max-height:30px; }
    .xl-root .action-bar .ghost-btn{ height:28px; min-width:120px; padding:0 26px; font-size:14px; }
    .xl-root .hint{ font-size:10.5px; line-height:1.15; }
    .xl-root .hand-row{ padding-top:8px; margin-bottom:2px; --xl-hpad:8px; }
  }

  /* ---- short LANDSCAPE phones (viewport height < 520, e.g. 796x306 with the address bar showing) ----
     Every pixel of height counts, so the dock is squeezed into: [Your hand/Total | cards | log text] and
     one slim hint row; the settings strip (wild rank) moves up into the top bar; and the JS seats YOU
     beside your hand (see 'compact' in xlGeometry) so the table keeps the rest of the arena. */
  @media (orientation:landscape) and (max-height:520px){
    .xl-topbar{ min-height:28px; padding:3px 10px 0; }
    .xl-topbar .rules-btn{ padding:3px 9px; font-size:11px; }
    .xl-root .settings-strip{
      position:absolute; top:5px; left:50%; transform:translateX(-50%); width:auto;
      padding:0; overflow:visible; z-index:8; pointer-events:none;
    }
    .xl-root .settings-strip > span:not(.wild-strip-tag){ display:none; }
    .xl-root .settings-strip:not(:has(.wild-strip-tag)){ display:none; }
    .xl-log{
      position:absolute; right:12px; bottom:calc(6px + var(--sab));
      width:var(--xl-side, 170px); height:auto; min-height:0; padding:0; text-align:right;
      display:block; font-size:10.5px; line-height:1.2; max-height:52px; overflow:hidden;
      z-index:6; pointer-events:auto;
    }
    /* side column: no room for the left icon / wide padding - the 'Moves' pill drops under the text instead */
    .xl-log{ margin:0; background:none; border:0; border-radius:0; padding-left:0; padding-right:0; }
    .xl-log-clock{ display:none; }
    .xl-log-more{ position:static; transform:none; display:flex; width:max-content; margin:3px 0 0 auto; }
    .xl-log-text{ text-align:right; }
    .xl-root .hand-dock{ padding-top:2px; padding-bottom:calc(3px + var(--sab)); }
    .xl-root .hand-section{ margin-top:0; }
    /* left column: one line 'Your hand · Total', then the hint / Call Show button under it */
    .xl-root .hand-header{
      position:absolute; left:12px; top:6px; height:auto; margin:0; width:var(--xl-side, 170px);
      display:flex; flex-direction:row; align-items:baseline; justify-content:flex-start; gap:8px; z-index:5;
    }
    .xl-root .hand-header .label, .xl-root .hand-header .total{ font-size:10.5px; }
    .xl-root .hand-row{ margin-bottom:0; padding-top:8px; --xl-hpad:8px; }
    .xl-root .action-bar{
      position:absolute; left:12px; top:26px; width:var(--xl-side, 170px);
      height:auto; min-height:0; max-height:40px; overflow:hidden;
      display:flex; flex-direction:column; align-items:flex-start; justify-content:flex-start; gap:3px; z-index:5;
    }
    .xl-root .action-bar{ justify-content:flex-start; }
    .xl-root .action-bar .ghost-btn{ height:auto; min-width:0; padding:3px 14px; font-size:12.5px; }
    .xl-root .hint{
      font-size:10px; line-height:1.2; text-align:left; white-space:normal; max-width:100%;
      display:-webkit-box; -webkit-line-clamp:3; -webkit-box-orient:vertical; overflow:hidden;
    }
  }

  /* ---- landscape phones with a little more height (>= 340px, e.g. full screen): bring back the whole
     settings strip (bots · cards · show · wrong show · out at · wild) as a slim line under the top bar.
     Below 340px (address bar showing) only the wild rank is kept, up in the top bar, to save height. ---- */
  @media (orientation:landscape) and (min-height:340px) and (max-height:520px){
    .xl-root .settings-strip,
    .xl-root .settings-strip:not(:has(.wild-strip-tag)){
      position:static; transform:none; width:auto; display:flex; justify-content:center; align-items:center;
      gap:6px; padding:0 10px 2px; font-size:10px; line-height:1.3; white-space:nowrap;
      overflow-x:auto; overflow-y:hidden; pointer-events:auto; z-index:auto;
    }
    .xl-root .settings-strip > span:not(.wild-strip-tag){ display:inline; }
    .xl-root .settings-strip .wild-strip-tag{ font-size:10.5px; }
  }


  /* =====================================================================
     DEAL FADE-IN, SEAT INFO CARD, LEAVE CONFIRM, LANDSCAPE PILE SPACING
     ===================================================================== */
  /* a dealt card of yours appears face-down in its hand slot, then flips face-up.
     The card turns edge-on (rotateY 90deg) at 50%; the .flip-back card-back layer is
     visible until then and hidden after, so it reads as one card turning over. */
  @keyframes dealFlip{
    0%{ transform:perspective(500px) translateY(8px) rotateY(0deg) scale(.92); }
    50%{ transform:perspective(500px) translateY(-4px) rotateY(90deg) scale(1.04); }
    100%{ transform:perspective(500px) none; }
  }
  @keyframes dealFlipBack{
    0%,49.9%{ opacity:1; }
    50%,100%{ opacity:0; }
  }
  .hand-card.deal-fade{ animation:dealFlip .42s ease-in-out var(--dd,0ms) backwards; } /* keep in sync with DEAL_FADE_MS in game.js */
  .hand-card .flip-back{ position:absolute; inset:0; border-radius:inherit; --cb-scale:var(--card-scale,1); pointer-events:none; animation:dealFlipBack .42s linear var(--dd,0ms) both; }

  /* tap an avatar -> info card (name / points / cards) */
  .xl-av{ pointer-events:auto; cursor:pointer; -webkit-tap-highlight-color:transparent; }
  .player-box.xl-seat.xl-open{ z-index:40; }
  .xl-bubble{
    display:none; position:absolute; z-index:41; min-width:120px; max-width:210px; padding:7px 11px;
    border-radius:10px; background:rgba(7,26,19,.97); border:1px solid var(--gold-soft); color:var(--cream);
    font-size:12px; line-height:1.4; text-align:left; white-space:normal; pointer-events:none;
    box-shadow:0 6px 18px rgba(0,0,0,.6);
  }
  .xl-seat.xl-open .xl-bubble{ display:block; }
  .xl-bubble b{ display:block; font-size:13px; color:var(--gold-soft); margin-bottom:2px; word-break:break-word; }
  .xl-bubble span{ display:block; }
  .xl-bubble span i{ font-style:normal; color:var(--muted); }
  .xl-bubble em{ display:block; font-style:normal; color:#7fd1ae; font-size:11.5px; margin:0 0 3px; }
  .xl-bubble em.end{ color:#e8907f; margin:4px 0 0; font-weight:600; }
  .xl-bubble.below{ top:calc(100% + 22px); }
  .xl-bubble.above{ bottom:calc(100% + 10px); }
  .xl-bubble.al-c{ left:50%; transform:translateX(-50%); }
  .xl-bubble.al-l{ left:-6px; }
  .xl-bubble.al-r{ right:-6px; }

  /* "Leave the table?" confirmation */
  .modal.leave-confirm{ max-width:340px; padding:22px 22px 18px; text-align:center; }
  .modal.leave-confirm h2{ margin:0 0 8px; }
  .leave-confirm-text{ margin:0 0 16px; color:var(--muted); font-size:14px; }
  .leave-confirm-actions{ display:flex; gap:10px; justify-content:center; }
  .leave-confirm-actions > button{ flex:1; }

  /* landscape: the deck and discard hint pills ("Drop + draw blind", "Drop - no draw needed!") are wider
     than the 96px pile column and were touching each other, so give each pile column more room */
  @media (orientation:landscape){
    .xl-arena .center-piles{ gap:16px; }
    .xl-arena .pile{ width:136px; }
  }

  /* expandable play history: tap the log line */
  .xl-history{
    display:none; position:fixed; z-index:150; box-sizing:border-box; /* above the card-flight overlay (z-index 100+) */
    max-height:min(200px, 45vh); overflow-y:auto; -webkit-overflow-scrolling:touch;
    padding:8px 11px 6px; border-radius:10px; background:rgba(7,26,19,.97); border:1px solid var(--gold-soft);
    box-shadow:0 6px 18px rgba(0,0,0,.6); text-align:left; font-size:11.5px; line-height:1.35; color:var(--muted);
  }
  .xl-history.open{ display:block; }
  .xl-hist-title{ font-size:10px; letter-spacing:1.2px; text-transform:uppercase; color:var(--gold-soft); margin-bottom:3px; }
  .xl-history > div:not(.xl-hist-title){ padding:1.5px 0; border-top:1px solid rgba(255,255,255,.05); }
  .xl-history > div:last-child{ color:var(--cream); font-weight:600; }

  /* Eliminated / left seats are dimmed per element (avatar, name, badges), NOT with opacity on the whole
     seat, because opacity on the seat would also fade its info bubble and make it hard to read. */
  .player-box.xl-seat.is-out, .player-box.xl-seat.has-left{ opacity:1; }
  .xl-seat.is-out > :not(.xl-bubble), .xl-seat.has-left > :not(.xl-bubble){ opacity:.45; }

  /* home screen: saved player name is locked, with a pencil button to edit it (tick to save) */
  .name-row{ display:flex; align-items:stretch; gap:8px; margin-bottom:18px; }
  .name-row .text-input{ flex:1; min-width:0; margin-bottom:0; }
  .text-input:disabled{ opacity:.75; cursor:default; color:var(--cream); -webkit-text-fill-color:var(--cream); }
  /* same height / shape / colours as the input beside it, so the two line up exactly */
  .name-btn{
    flex:0 0 46px; display:inline-flex; align-items:center; justify-content:center; padding:0;
    background:var(--felt-1); border:1.5px solid var(--panel-line); border-radius:10px;
    color:var(--gold-soft); cursor:pointer; -webkit-tap-highlight-color:transparent;
    transition:border-color .15s, color .15s, background .15s;
  }
  .name-btn:hover, .name-btn:active{ border-color:var(--gold-soft); color:var(--gold); }
  .name-btn.save{ background:var(--gold); border-color:var(--gold); color:var(--felt-1); }
  .name-btn.save:hover, .name-btn.save:active{ filter:brightness(1.08); color:var(--felt-1); }

  /* ---- reshuffle: empty deck slot + chip ---- */
  .deckpile.deck-empty{ background:none; }
  .reshuffle-chip{
    position:absolute; left:50%; top:-6px; transform:translate(-50%,-100%); z-index:6;
    display:inline-flex; align-items:center; gap:6px; white-space:nowrap;
    padding:5px 12px; border-radius:999px; font-size:12px; font-weight:600; letter-spacing:.3px;
    color:var(--gold-soft); background:rgba(7,26,19,.92); border:1px solid var(--gold-soft);
    box-shadow:0 4px 12px rgba(0,0,0,.5);
    /* the piles are re-rendered on every swept card; --t (ms since the reshuffle began) is applied as a
       negative delay so the chip's animations CONTINUE instead of restarting (which looked like blinking) */
    animation:chipIn .22s ease-out backwards; animation-delay:calc(-1 * var(--t, 0ms));
  }
  .reshuffle-chip svg{ animation:chipSpin 1.1s ease-in-out infinite; animation-delay:calc(-1 * var(--t, 0ms)); }
  @keyframes chipIn{ from{ opacity:0; scale:.85; } to{ opacity:1; scale:1; } }
  @keyframes chipSpin{ 0%,100%{ transform:rotate(0); } 50%{ transform:rotate(12deg); } }

  /* whose turn it is: besides the glow, the whole seat (avatar, name, badges) is enlarged so the active
     player stands out at a glance. Uses the independent `scale` property, so it never fights with the
     seat's positioning transform; no animation, because the seats are re-created on every render and an
     animation would restart (flicker) each time. */
  .player-box.xl-seat.thinking{ scale:1.1; z-index:8; }
  .player-box.xl-seat.thinking.xl-open{ z-index:40; }
  /* keep the info bubble at its normal size inside the enlarged seat (1 / 1.1) */
  .xl-seat.thinking .xl-bubble{ scale:.91; }

  /* ===== Quick chat (preset messages) - "table talk" ===== */
  /* Bubble over an avatar: a cream playing-card with a gold frame and a speech tail. It fades in fast,
     holds, then fades out (3.5 s total = CHAT_BUBBLE_MS in game.js). width:max-content is essential: the
     bubble lives inside the ~50px avatar box, and without it the text would wrap at every space.
     Everything is sized in em from the avatar size (--av), so on small phones / big tables it shrinks. */
  @keyframes chatBubble{
    0%{ opacity:0; transform:translateY(5px) scale(.8); }
    7%{ opacity:1; transform:scale(1.04); }
    12%{ transform:none; }
    88%{ opacity:1; transform:none; }
    100%{ opacity:0; transform:none; }
  }
  .player-box.xl-seat.xl-chatting{ z-index:39; }
  .xl-chat{
    --tail-x:50%;
    position:absolute; z-index:45; width:max-content; max-width:min(15em, 62vw);
    font-size:clamp(10.5px, calc(var(--av, 48px) * .3), 14px);
    padding:.42em .85em .5em; border-radius:.95em;
    background:linear-gradient(180deg,#fffaf0 0%,#f3e9cc 100%); color:#14352a;
    border:1.5px solid var(--gold-soft);
    box-shadow:inset 0 0 0 1px rgba(214,176,58,.35), 0 5px 14px rgba(0,0,0,.55);
    font-family:'Fraunces', Georgia, serif; font-weight:700; line-height:1.2; text-align:center;
    white-space:normal; pointer-events:none;
    animation:chatBubble 3.5s linear var(--cd,0ms) both;
  }
  /* speech tail pointing at the avatar */
  .xl-chat::after{
    content:''; position:absolute; left:var(--tail-x); width:9px; height:9px; margin-left:-5px;
    background:#f3e9cc; border:1.5px solid var(--gold-soft); transform:rotate(45deg);
  }
  .xl-chat.above{ bottom:calc(100% + 9px); }
  .xl-chat.above::after{ bottom:-6px; border-top:0; border-left:0; }
  .xl-chat.below{ top:calc(100% + 22px); }
  .xl-chat.below::after{ top:-6px; border-bottom:0; border-right:0; background:#fffaf0; }
  .xl-chat.al-c{ left:50%; translate:-50% 0; }
  .xl-chat.al-l{ left:-6px; --tail-x:26px; }
  .xl-chat.al-r{ right:-6px; --tail-x:calc(100% - 26px); }
  .xl-chat:not(.above):not(.below){ bottom:calc(100% + 9px); left:50%; translate:-50% 0; }

  /* chat button: fixed, bottom-left. z-index 154/155: above the flying cards (100+) and the play-history
     panel (150), below modals (160), confetti (200) and the tour (300+). */
  .chat-fab{
    position:fixed; z-index:154; left:calc(10px + var(--sal, 0px)); bottom:calc(6px + var(--sab, 0px));
    width:40px; height:40px; padding:0; border-radius:50%; font-size:18px; line-height:1;
    display:flex; align-items:center; justify-content:center; cursor:pointer;
    background:radial-gradient(circle at 35% 30%, #1d5a42, #0b2a1e); border:1.5px solid var(--gold-soft); color:var(--cream);
    box-shadow:0 2px 8px rgba(0,0,0,.5); -webkit-tap-highlight-color:transparent;
  }
  .chat-fab.open{ background:var(--gold-soft); }
  @keyframes chatPanelIn{ from{ opacity:0; transform:translateY(8px) scale(.94); } to{ opacity:1; transform:none; } }
  /* Panel: all inner sizes are em, and the font size follows the screen HEIGHT (11px on a 640px-tall phone,
     up to 13px on tall screens), so the panel never swallows a small phone. Never taller than ~58% of the screen. */
  .chat-panel{
    position:fixed; z-index:155; left:calc(8px + var(--sal, 0px)); bottom:calc(54px + var(--sab, 0px));
    font-size:clamp(11px, 1.85vh, 13px);
    width:min(300px, calc(100vw - 16px - var(--sal, 0px) - var(--sar, 0px)));
    max-height:min(58%, calc(100% - 70px - var(--sab, 0px) - var(--sat, 0px))); overflow-y:auto;
    padding:.7em .85em .85em; transform-origin:bottom left;
    background:radial-gradient(120% 90% at 50% 0%, #17503a 0%, #0c2c20 70%);
    border:2px solid var(--gold-soft); border-radius:1.2em;
    box-shadow:inset 0 0 0 3px rgba(7,26,19,.9), inset 0 0 0 4px rgba(214,176,58,.35), 0 10px 28px rgba(0,0,0,.65);
  }
  .chat-panel.chat-anim{ animation:chatPanelIn .16s ease-out; }  /* only on the render right after opening */
  .chat-head{
    display:flex; align-items:center; justify-content:space-between; margin-bottom:.5em; padding:0 .15em .45em;
    border-bottom:1px solid rgba(214,176,58,.35);
  }
  .chat-title{ font-family:'Fraunces', Georgia, serif; font-weight:700; font-size:1.3em; color:var(--gold-soft); letter-spacing:.3px; }
  .chat-suits{ font-size:1.15em; letter-spacing:.2em; color:var(--cream); }
  .chat-suits i{ font-style:normal; color:#e0584a; }
  .chat-group + .chat-group{ margin-top:.6em; }
  .chat-label{ font-size:.8em; font-weight:700; letter-spacing:.14em; text-transform:uppercase; color:var(--muted); margin:0 .15em .35em; }
  .chat-chips{ display:flex; flex-wrap:wrap; gap:.4em; }
  .chat-item{
    padding:.38em .8em; border-radius:999px; font-size:1em; font-weight:600; line-height:1.15; white-space:nowrap; cursor:pointer;
    color:var(--cream); background:rgba(255,255,255,.07); border:1px solid rgba(214,176,58,.45);
    -webkit-tap-highlight-color:transparent; transition:background .1s, color .1s;
  }
  .chat-item:active{ background:var(--gold-soft); color:#14352a; border-color:var(--gold-soft); }
  /* small / short phones: drop the group captions (the three groups stay separated by spacing + a thin rule) */
  @media (max-height:700px){
    .chat-label{ display:none; }
    .chat-group + .chat-group{ margin-top:.55em; padding-top:.55em; border-top:1px solid rgba(214,176,58,.2); }
  }
  /* short landscape phones: wider panel so the chips sit in fewer rows */
  @media (max-height:520px){
    .chat-panel{ width:min(560px, calc(100vw - 16px - var(--sal, 0px) - var(--sar, 0px))); max-height:calc(100% - 56px - var(--sab, 0px)); }
  }

  /* ----- Phase 2: emoji reactions, per-player mute ----- */
  /* one row that scrolls sideways: ~6.5 emoji visible so the half-cut one hints there are more */
  .chat-reactions{ display:flex; gap:.3em; overflow-x:auto; overflow-y:hidden; -webkit-overflow-scrolling:touch; touch-action:pan-x; scrollbar-width:none; }
  .chat-reactions::-webkit-scrollbar{ display:none; }
  .chat-emoji{
    flex:0 0 calc((100% - 6 * .3em) / 6.5); min-width:0; padding:.12em 0; font-size:1.75em; line-height:1.25; text-align:center; cursor:pointer;
    background:rgba(255,255,255,.07); border:1px solid rgba(214,176,58,.4); border-radius:.6em;
    -webkit-tap-highlight-color:transparent;
  }
  .chat-emoji:active{ background:var(--gold-soft); border-color:var(--gold-soft); }
  .chat-reactions-group{ margin-bottom:.1em; }
  .chat-reactions-group + .chat-group{ margin-top:.6em; }

  /* a reaction floats over the avatar as a big bare emoji (no card frame): pops in, drifts up, fades */
  /* The emoji sits centred ON the avatar and is pushed out along the seat's spoke: the unit vector (--ux, --uy)
     from the avatar's centre to the table's centre, set per seat by applyOpenSeat() in tableLayout.js.
     --d0 = distance from the avatar centre at which it starts (avatar radius + about half the emoji + a little;
     extra room below the avatar for seats whose spoke points down, where the name label hangs). The keyframes then
     add 'distance along the spoke'. Default (0,-1) = straight up. */
  @keyframes chatEmoji{
    0%{ opacity:0; transform:translate(calc(var(--ux,0) * (var(--d0) - 6px)), calc(var(--uy,-1) * (var(--d0) - 6px))) scale(.3); }
    10%{ opacity:1; transform:translate(calc(var(--ux,0) * (var(--d0) + 4px)), calc(var(--uy,-1) * (var(--d0) + 4px))) scale(1.35); }
    20%{ transform:translate(calc(var(--ux,0) * (var(--d0) + 8px)), calc(var(--uy,-1) * (var(--d0) + 8px))) scale(1); }
    80%{ opacity:1; transform:translate(calc(var(--ux,0) * (var(--d0) + 22px)), calc(var(--uy,-1) * (var(--d0) + 22px))) scale(1); }
    100%{ opacity:0; transform:translate(calc(var(--ux,0) * (var(--d0) + 30px)), calc(var(--uy,-1) * (var(--d0) + 30px))) scale(1); }
  }
  .xl-chat{ animation-duration:var(--dur, 3500ms); }
  .xl-chat.emoji{
    animation-name:chatEmoji;
    padding:0; background:none; border:0; box-shadow:none; border-radius:0;
    --es:clamp(22px, calc(var(--av, 48px) * .8), 36px);
    --d0:calc(var(--av, 48px) / 2 + var(--es) * .55 + 2px + max(0, var(--uy, -1)) * 16px);
    font-size:var(--es); line-height:1; font-family:inherit;
    filter:drop-shadow(0 3px 5px rgba(0,0,0,.55));
  }
  .xl-chat.emoji::after{ display:none; }
  /* anchor point = the avatar's centre (the seat box IS the avatar box); beats the generic text-bubble placement */
  .xl-chat.emoji:not(.above):not(.below){ top:50%; bottom:auto; left:50%; right:auto; translate:-50% -50%; }

  /* Mute button inside the avatar info card (the card itself ignores taps, the button does not) */
  .xl-mute{
    pointer-events:auto; display:block; width:100%; margin-top:6px; padding:4px 6px; cursor:pointer;
    font-size:11px; font-weight:600; color:var(--cream); text-align:center; white-space:nowrap;
    background:rgba(255,255,255,.08); border:1px solid var(--gold-soft); border-radius:8px;
    -webkit-tap-highlight-color:transparent;
  }
  .xl-mute.on{ background:var(--gold-soft); color:#14352a; }

  /* 'Mute all' toggle in the panel header + a badge on the chat button while it is on */
  .chat-muteall{
    padding:.28em .75em; border-radius:999px; font-size:.95em; font-weight:600; white-space:nowrap; cursor:pointer;
    color:var(--cream); background:rgba(255,255,255,.07); border:1px solid rgba(214,176,58,.45);
    -webkit-tap-highlight-color:transparent;
  }
  .chat-muteall.on{ background:var(--gold-soft); color:#14352a; border-color:var(--gold-soft); }
  .chat-fab{ position:fixed; }
  .chat-fab-badge{
    position:absolute; right:-3px; top:-3px; width:17px; height:17px; border-radius:50%; font-size:10px; line-height:17px; text-align:center;
    background:#0b2a1e; border:1px solid var(--gold-soft);
  }


  /* =====================================================================
     CARD PREVIEW (home screen, Game tab)  -  see cardPreview.js
     Showroom panel for the picked card back: the card grows out of the tapped swatch, then spins slowly
     (back weighted, the Ace of Spades flashes by) and fades away. Everything is sized from --cp-s (the
     card scale), so it can shrink on short screens. Each animated transform has its own element so the
     animations never overwrite each other: emerge > float > tilt > spin.
     ===================================================================== */
  .cp-panel{
    --cp-s:1.3;
    position:fixed; z-index:120; pointer-events:none; visibility:hidden;
    width:calc(60px * var(--cp-s) + 44px); padding:14px 0 12px; text-align:center;
  }
  .cp-panel.cp-go{ visibility:visible; }
  /* locked (Pro) preview: wider, stays until dismissed, and is tappable */
  .cp-panel.cp-locked{ width:calc(60px * var(--cp-s) + 84px); pointer-events:auto; }
  @media (max-height:560px){ .cp-panel{ --cp-s:1.1; } }

  .cp-glass{
    position:absolute; inset:0; border-radius:22px; opacity:0;
    background:rgba(7,26,19,.72); border:1px solid rgba(236,227,206,.16);
    box-shadow:0 12px 36px rgba(0,0,0,.55);
    -webkit-backdrop-filter:blur(8px); backdrop-filter:blur(8px);
  }
  .cp-title, .cp-sub, .cp-proRow, .cp-actions{ position:relative; opacity:0; }
  .cp-title{ font-family:'Fraunces', serif; font-size:18px; font-weight:600; color:var(--gold-soft); line-height:1.2; }
  .cp-sub{ font-size:11.5px; color:var(--muted); letter-spacing:.3px; margin-top:2px; }
  .cp-go .cp-glass{ animation:cpFadeIn .25s ease-out .1s forwards; }
  .cp-go .cp-title, .cp-go .cp-sub, .cp-go .cp-proRow, .cp-go .cp-actions{ animation:cpFadeIn .25s ease-out .15s forwards; }
  @keyframes cpFadeIn{ to{ opacity:1; } }

  .cp-stage{ position:relative; height:calc(84px * var(--cp-s) + 40px); margin-top:8px; }
  .cp-emerge{
    position:absolute; left:50%; top:50%; translate:-50% -50%;
    width:calc(60px * var(--cp-s)); height:calc(84px * var(--cp-s));
    --fx:0px; --fy:0px; --fs:.3;
  }
  .cp-go .cp-emerge{ animation:cpEmerge .45s cubic-bezier(.22,.68,0,1.15) both; }
  @keyframes cpEmerge{
    from{ transform:translate(var(--fx), var(--fy)) scale(var(--fs)); }
    to{ transform:none; }
  }
  .cp-float{ width:100%; height:100%; }
  .cp-go .cp-float{ animation:cpFloat 3s ease-in-out infinite alternate; }
  @keyframes cpFloat{ from{ transform:translateY(3px); } to{ transform:translateY(-6px); } }
  .cp-tilt{ width:100%; height:100%; perspective:900px; -webkit-perspective:900px; transform:rotateX(-12deg); }
  .cp-spin{ position:relative; width:100%; height:100%; transform-style:preserve-3d; -webkit-transform-style:preserve-3d; }
  /* 3.6 s per turn (keep SHOW_MS in cardPreview.js equal to this, so the fade starts with the back facing): the back lingers (0-35%), then the card whips round (35-65%, the Ace flashes by) and settles back */
  .cp-go .cp-spin{ animation:cpSpin 3.6s linear infinite; will-change:transform; }
  @keyframes cpSpin{
    0%{   transform:rotateY(0deg);   animation-timing-function:ease-in-out; }
    35%{  transform:rotateY(35deg);  animation-timing-function:ease-in; }
    65%{  transform:rotateY(325deg); animation-timing-function:ease-out; }
    100%{ transform:rotateY(360deg); }
  }
  .cp-face{
    position:absolute; inset:0; border-radius:calc(7px * var(--cp-s));
    -webkit-backface-visibility:hidden; backface-visibility:hidden;
  }
  .cp-back{ overflow:hidden; }
  .cp-back .card-back, .cp-back .cp-dice{
    position:absolute; left:0; top:0; width:60px; height:84px; transform:scale(var(--cp-s)); transform-origin:0 0;
  }
  .cp-front{ transform:rotateY(180deg); --card-scale:var(--cp-s); }
  /* the Random swatch's dice tile, drawn as a card back */
  .cp-dice{
    display:flex; align-items:center; justify-content:center; font-size:26px; line-height:1;
    background:var(--felt-1); border:2px solid var(--gold); border-radius:7px;
    box-shadow:inset 0 0 0 2px var(--felt-edge), 0 3px 8px rgba(0,0,0,.35);
  }
  .cp-shadow{
    position:absolute; left:50%; bottom:6px; translate:-50% 0;
    width:calc(60px * var(--cp-s) * .8); height:14px; border-radius:50%;
    background:radial-gradient(ellipse at center, rgba(0,0,0,.55), transparent 70%);
  }
  .cp-go .cp-shadow{ animation:cpShadow 3s ease-in-out infinite alternate; }
  @keyframes cpShadow{ from{ transform:scale(1.05); opacity:.9; } to{ transform:scale(.85); opacity:.6; } }

  .cp-panel.cp-go.cp-fading{ animation:cpOut .3s ease-in forwards; }   /* keep .3s in sync with FADE_MS in cardPreview.js */
  @keyframes cpOut{ to{ opacity:0; } }

  /* reduced motion: styles.css shrinks every animation duration, which would make the infinite loops flicker,
     so here the card just sits still (back facing you) and the panel fades */
  .cp-static .cp-emerge, .cp-static .cp-float, .cp-static .cp-spin, .cp-static .cp-shadow{ animation:none !important; }

/* ---------- Pro / locked card backs ---------- */
.backswatch-btn{ position:relative; }
.backswatch-btn.locked .backswatch{ filter:saturate(.75) brightness(.85); }
.backswatch-btn .lock-badge{
  position:absolute; right:0; bottom:0; width:15px; height:15px; padding:2px; box-sizing:border-box;
  border-radius:50%; background:rgba(8,24,18,.88); color:var(--gold-soft);
  border:1px solid var(--gold); pointer-events:none;
}
.cp-proRow{ margin-top:5px; }
.cp-pro{
  display:inline-block; padding:1px 8px; border-radius:999px;
  font-family:inherit; font-size:10px; font-weight:700; letter-spacing:.6px; line-height:1.4;
  color:#1a1405; background:linear-gradient(135deg,var(--gold-soft),var(--gold));
}
.cp-actions{ margin:6px 14px 0; }
.cp-unlock{
  width:100%; padding:9px 10px; border:none; border-radius:999px; cursor:pointer; white-space:nowrap;
  font-family:inherit; font-size:13px; font-weight:700; letter-spacing:.2px; color:#1a1405;
  background:linear-gradient(135deg,var(--gold-soft),var(--gold)); box-shadow:0 3px 10px rgba(0,0,0,.35);
}
.cp-unlock:active{ transform:scale(.97); }
.cp-x{
  position:absolute; top:6px; right:8px; width:26px; height:26px; padding:0; border:none; border-radius:50%;
  background:transparent; color:var(--muted); font-size:14px; line-height:26px; cursor:pointer; z-index:2;
}

/* Unlock button: dark chip with a gold open padlock, so the icon reads clearly on the gold button */
.cp-unlock{ display:flex; align-items:center; justify-content:center; gap:8px; }
.cp-lock{ flex:0 0 auto; width:24px; height:24px; border-radius:50%; background:#1a1405; color:var(--gold-soft); display:inline-flex; align-items:center; justify-content:center; }
.cp-lock svg{ display:block; }


  /* =====================================================================
     ICONS  -  SVG icons from icons.js (outline, colour = currentColor)
     ===================================================================== */
  .icon{ width:1.15em; height:1.15em; display:inline-block; vertical-align:-.2em; flex-shrink:0; pointer-events:none; }
  .icon-lead{ margin-right:.45em; }
  /* buttons with a leading icon: centre icon and label on the same line */
  .replay-tour-btn, .ghost-btn:has(.icon-lead), .chat-muteall{ display:inline-flex; align-items:center; justify-content:center; }
  /* bot marker colour - change this one value to restyle every bot icon */
  :root{ --bot-icon:#7fd4c6; }
  .rs-bot, .xl-tag .icon-bot, .xl-bubble .icon-bot{ color:var(--bot-icon); }
  .xl-mute{ display:flex; align-items:center; justify-content:center; }
  .xl-mute .icon, .chat-muteall .icon{ vertical-align:0; }
  .server-dropdown-chevron{ display:inline-flex; align-items:center; }
  .rs-bot .icon{ vertical-align:-.15em; }
  .xl-tag .icon{ vertical-align:0; }
  .rules-btn.mute-btn, .rules-btn.vibrate-btn, .chat-fab, .xl-topbar .rules-btn[data-action="openRules"]{ display:inline-flex; align-items:center; justify-content:center; }
  .chat-fab-badge{ display:flex; align-items:center; justify-content:center; line-height:1; }
  .chat-fab-badge .icon{ vertical-align:0; }
  .champion-trophy .icon, .standing-medal .icon{ vertical-align:0; display:block; margin:0 auto; }
  .backswatch-random .icon{ vertical-align:0; color:var(--cream); }
  .ob-tap-note .icon{ vertical-align:-.2em; }
  /* results table: icon and name stay side by side (the name wraps inside its own box, never pushes the icon onto its own line) */
  .rs-who{ display:inline-flex; align-items:center; gap:4px; max-width:100%; vertical-align:top; }
  .rs-who .rs-bot{ flex:0 0 auto; margin:0; display:inline-flex; }
  .rs-who .rs-bot .icon{ vertical-align:0; }
  .rs-nm{ min-width:0; overflow-wrap:anywhere; }

/* Pro design badge: slim gold "PRO" pill on the bottom edge of an owned premium swatch (Android Pro only) */
.backswatch-btn .pro-pill{
  position:absolute; left:50%; bottom:-5px; transform:translateX(-50%);
  padding:1px 6px 0; border-radius:999px; pointer-events:none;
  font-size:8px; font-weight:800; letter-spacing:.7px; line-height:12px; color:#1a1405;
  background:linear-gradient(135deg,var(--gold-soft),var(--gold));
  border:1px solid rgba(8,24,18,.85); box-shadow:0 1px 3px rgba(0,0,0,.45);
}
/* Owned premium preview: PRO pill kept, 'OWNED' tag instead of the Unlock button; fades like a free preview */
.cp-panel.cp-owned-panel{ pointer-events:none; }
.cp-owned{
  display:flex; align-items:center; justify-content:center; gap:6px; padding:7px 10px; border-radius:999px;
  font-size:12px; font-weight:700; letter-spacing:.8px; color:var(--gold-soft);
  background:rgba(8,24,18,.55); border:1px solid var(--gold);
}

/* ---------- Round summary: frosted-glass dialog ----------
   The table stays visible (blurred) behind a translucent, lightly tinted panel. Legibility is protected three ways:
   the panel keeps a fairly dark tint, text is full-strength cream with a faint shadow, and the highlighted rows
   (caller / verdict banner) keep their own solid-ish colours. Browsers without backdrop-filter get a solid panel. */
.modal-backdrop.glass-bd{
  background:radial-gradient(120% 90% at 50% 0%, rgba(40,110,85,.28), rgba(3,14,10,.62));
  -webkit-backdrop-filter:blur(7px) saturate(115%); backdrop-filter:blur(7px) saturate(115%);
}
.modal.glass{
  position:relative;
  background:
    linear-gradient(160deg, rgba(255,255,255,.20) 0%, rgba(255,255,255,.05) 38%, rgba(255,255,255,0) 60%),
    rgba(12,42,32,.74);
  border:1px solid rgba(255,255,255,.26);
  box-shadow:0 18px 50px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.35), inset 0 0 0 1px rgba(255,255,255,.04);
  -webkit-backdrop-filter:blur(22px) saturate(150%); backdrop-filter:blur(22px) saturate(150%);
  color:#fbf6e8;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .modal.glass{ background:var(--panel); }
}
.modal.glass h2{ text-shadow:0 1px 6px rgba(0,0,0,.45); }
.modal.glass table.results th{ color:rgba(251,246,232,.78); border-bottom-color:rgba(255,255,255,.22); }
.modal.glass table.results td{ color:#fbf6e8; border-bottom-color:rgba(255,255,255,.14); text-shadow:0 1px 2px rgba(0,0,0,.35); }
.modal.glass table.results tr.rs-win td{ background:rgba(230,199,102,.26); }
.modal.glass table.results tr.rs-lose td{ background:rgba(150,38,50,.70); }
.modal.glass .verdict{ background:rgba(0,0,0,.22); }
.modal.glass .verdict.lose{ background:linear-gradient(180deg, rgba(120,32,42,.86), rgba(78,20,28,.86)); }
.modal.glass .ready-status, .modal.glass .hint{ color:rgba(251,246,232,.82); }

/* ---------- Round summary: black, semi-transparent glass variant (add .glass-black / .glass-bd-black; remove them for the green glass) ---------- */
.modal-backdrop.glass-bd.glass-bd-black{
  background:radial-gradient(120% 90% at 50% 0%, rgba(255,255,255,.06), rgba(0,0,0,.6));
  -webkit-backdrop-filter:blur(8px) saturate(110%); backdrop-filter:blur(8px) saturate(110%);
}
.modal.glass.glass-black{
  background:
    linear-gradient(155deg, rgba(255,255,255,.16) 0%, rgba(255,255,255,.04) 30%, rgba(255,255,255,0) 55%),
    rgba(8,10,10,0.87);
  border:1px solid rgba(255,255,255,.22);
  box-shadow:0 20px 56px rgba(0,0,0,.65), inset 0 1px 0 rgba(255,255,255,.30), inset 0 0 40px rgba(255,255,255,.015);
  -webkit-backdrop-filter:blur(10px) saturate(120%); backdrop-filter:blur(10px) saturate(120%);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .modal.glass.glass-black{ background:#0c0f0f; }
}
.modal.glass.glass-black table.results th{ color:rgba(255,255,255,.72); border-bottom-color:rgba(255,255,255,.20); }
.modal.glass.glass-black table.results td{ color:#fff; border-bottom-color:rgba(255,255,255,.12); }
.modal.glass.glass-black table.results tr.rs-win td{ background:rgba(230,199,102,.22); }
.modal.glass.glass-black table.results tr.rs-lose td{ background:rgba(170,40,52,.62); }
.modal.glass.glass-black .verdict{ background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.12); }
.modal.glass.glass-black .verdict.lose{ background:linear-gradient(180deg, rgba(140,36,46,.78), rgba(86,22,30,.78)); border-color:rgba(232,115,90,.6); }
.modal.glass.glass-black .ready-status, .modal.glass.glass-black .hint{ color:rgba(255,255,255,.78); }

/* ---------- Round summary banner / row colours (black glass) ----------
   opponent's wrong Show  -> quiet transparent banner (same as an opponent's right Show)
   your right Show        -> green banner (mirror of your red wrong-Show banner)
   wrong-Show row         -> softer maroon tint */
.modal.glass.glass-black .verdict.lose:not(.mine){
  background:rgba(255,255,255,.07); border:1px solid rgba(255,255,255,.12);
}
.modal.glass.glass-black .verdict.win.mine{
  background:linear-gradient(180deg, rgba(34,120,76,.86), rgba(20,74,48,.86));
  border:1px solid rgba(110,205,145,.65);
}
.modal.glass.glass-black .verdict.win.mine .vd-head{ color:#b8f2cd; }
.modal.glass.glass-black .verdict.win.mine .vd-emoji{
  background:rgba(120,220,160,.16); box-shadow:0 0 0 2px #6fcf97, 0 0 24px rgba(111,207,151,.40);
}
.modal.glass.glass-black table.results tr.rs-lose td{ background:rgba(150,38,50,.30); }
.modal.glass table.results tr.rs-lose td{ background:rgba(150,38,50,.34); }

/* ---------- Black glass: opponent wrong-Show headline/icon in maroon; right-Show row in light green ---------- */
.modal.glass.glass-black .verdict.lose:not(.mine) .vd-head,
.modal.glass.glass-black .verdict.lose:not(.mine) .vd-emoji{ color:#b3303f; }
.modal.glass.glass-black table.results tr.rs-win td{ background:rgba(120,210,150,.22); }
.modal.glass.glass-black table.results tr.rs-win td:first-child{ box-shadow:inset 4px 0 0 #6fcf97; }

/* ---------- Round summary pop-in: the dialog scales up from a little smaller while fading in ----------
   .fx is only present for the first ~2 s (game.js), and --fx-age is the time already spent, so the negative delay
   lets the animation CONTINUE (not restart) when the countdown re-renders the dialog. Only transform + opacity
   are animated (cheap on weak GPUs); the end state equals the normal state, so removing .fx changes nothing. */
.modal.glass.fx{
  animation:rsPop .36s cubic-bezier(.2,.85,.3,1.15) both;
  animation-delay:calc(-1 * var(--fx-age, 0ms));
  transform-origin:50% 50%;
}
@keyframes rsPop{ from{ transform:scale(.82); opacity:0; } to{ transform:scale(1); opacity:1; } }

/* ---------- Round summary tweaks: brighter wrong-show headline/icon, more compact banner ---------- */
.modal.glass.glass-black .verdict.lose:not(.mine) .vd-head,
.modal.glass.glass-black .verdict.lose:not(.mine) .vd-emoji{ color:#e2505f; }
.modal.glass .verdict{ padding:8px 12px 8px; margin:8px 0 10px; }
.modal.glass .verdict .vd-head{ margin-top:2px; }
.modal.glass .verdict .vd-sub{ margin-top:2px; }

/* ---------- Black glass polish: clearer banner, tighter bottom, LOWEST pill ---------- */
.modal.glass{ padding-bottom:18px; }
.modal.glass .ready-status{ margin-top:10px; }
.modal.glass table.results td.rs-name .rs-lowest{
  display:block; width:fit-content; margin-top:3px; padding:1px 8px; border-radius:999px;
  font-size:9.5px; font-weight:700; letter-spacing:.08em; line-height:1.5; text-transform:uppercase;
  color:#a8efc1; background:rgba(111,207,151,.16); border:1px solid rgba(111,207,151,.55);
}
