:root {
    --p1: #ff3b3b;
    --p2: #3b8eff;
    --gold: #f5c842;
    --panel: rgba(18, 14, 38, 0.97);
    --border: rgba(255, 255, 255, 0.22);
    --hud-h: 78px;
    /* Three- and four-player seats. Numbers mirror PLAYER_SLOTS in
       src/config/GameConfig.js — the JS wants them as ints, CSS wants strings,
       and the two are kept in step by hand in exactly these two places. */
    --p3: #22c55e;
    --p4: #f5c842;
    --rival-h: 46px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body, html {
    width: 100%; height: 100%; overflow: hidden;
    font-family: 'Nunito', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    touch-action: none; user-select: none; -webkit-user-select: none;
    -webkit-touch-callout: none;
    overscroll-behavior: none;
}

/* THE TWO BACKGROUND LAYERS, AND WHY THEY WERE INVISIBLE.
   #bg-gradient (z -2) and #bg-canvas (z -1) sit behind the board: the biome's
   sky, and a starfield. Neither has ever been seen.
   `body, html` carried an opaque `background: #1a0b2e`, and within a stacking
   context CSS paints negative-z children (step 2) BEFORE the backgrounds of
   in-flow block descendants (step 3) — so <body>'s flat purple went straight
   over both of them. Five hand-tuned district skies and sixty twinkling stars
   rendered every frame into something nothing could show.
   The fallback belongs on <html>, whose background paints at step 1, under
   everything. <body> paints nothing. */
html { background: #1a0b2e; }
body { background: transparent; }

/* Core Layers */
#bg-gradient {
    position: fixed; inset: 0; z-index: -2;
    background: linear-gradient(to bottom, #1a0b2e, #5a0b4d);
    transition: background 1.5s ease;
}
#bg-canvas { position: fixed; inset: 0; z-index: -1; pointer-events: none; }

#game-container { position: absolute; inset: 0; z-index: 1; display: none; }
#game-container canvas { display: block; width: 100vw !important; height: 100vh !important; outline: none; }

#ui-layer { position: absolute; inset: 0; z-index: 10; pointer-events: none; display: none; }
#minigame-layer { position: fixed; inset: 0; z-index: 100; background: rgba(20, 8, 38, 0.98); display: none; flex-direction: column; padding-bottom: env(safe-area-inset-bottom); padding-top: env(safe-area-inset-top); }

/* Splash & Char Select */
#splash, #char-select {
    position: fixed; inset: 0; z-index: 1000;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: linear-gradient(to bottom, #1e0c36, #4a093f);
    padding: 15px; overflow-y: auto; touch-action: pan-y;
}
#char-select { display: none; }
/* Tabletop: Player 2 picks from the far side of the phone, so their step is turned to face them. */
#char-select.cs-flipped { transform: rotate(180deg); }

.screen-content {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    width: 100%; max-width: 360px; margin: auto;
}

#modal-overlay {
    position: fixed; inset: 0; z-index: 200;
    background: rgba(0, 0, 0, .85);
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none; transition: opacity .3s ease;
}
#modal-overlay.act { opacity: 1; pointer-events: auto; }
/* The toast rail. This used to sit at top:50%/left:50% — dead centre, exactly
   where the token, the dice and the tile being moved toward all are — and it
   could stack five deep. It now hugs the ACTIVE player's own edge and leaves
   the middle of the screen alone, which is the part of the screen the game is
   actually being played on. */
#toast-box {
    position: fixed; left: 50%; bottom: calc(var(--hud-h) + 10px); top: auto;
    transform: translateX(-50%);
    z-index: 300; display: flex; flex-direction: column-reverse; align-items: center;
    gap: 6px; pointer-events: none;
    max-width: min(92vw, 360px);
}

.bfont { font-family: 'Bebas Neue', 'Arial Narrow', 'Oswald', Impact, system-ui, sans-serif; }
.stitle { font-size: calc(60px * var(--ts, 1)); line-height: 1; color: #fff; text-align: center; margin-bottom: 12px; text-shadow: 0 0 30px rgba(245,200,66,.8); }
.stitle span { color: var(--gold); }

.sbtn {
    background: rgba(0,0,0,0.4); border: 2px solid rgba(255,255,255,0.5); color: #fff;
    padding: 12px 16px; border-radius: 12px; cursor: pointer; margin-bottom: 10px; font-size: calc(20px * var(--ts, 1));
    transition: all .2s; width: 100%; box-shadow: 0 4px 10px rgba(0,0,0,0.3);
}
.sbtn:hover, .sbtn.sel { border-color: var(--gold); background: rgba(245,200,66,.25); color: var(--gold); }

.btn-primary {
    background: linear-gradient(135deg, #f59e0b, #fbbf24); color: #000; border: none;
    font-size: calc(24px * var(--ts, 1)); padding: 14px 40px; border-radius: 50px; margin-top: 16px; cursor: pointer;
    box-shadow: 0 8px 30px rgba(245,200,66,.6); width: 100%; max-width: 300px; transition: transform 0.1s;
}
.btn-primary:active { transform: scale(0.95); }
.btn-primary:disabled { filter: grayscale(1); opacity: 0.6; cursor: not-allowed; transform: none; }
.mg-arcade-btn { background: transparent; border: 2px solid rgba(245,200,66,0.45); color: rgba(245,200,66,0.85); padding: 10px 24px; border-radius: 50px; margin-top: 10px; cursor: pointer; font-size: calc(17px * var(--ts, 1)); letter-spacing: 1px; width: 100%; max-width: 300px; transition: all .2s; }
.mg-arcade-btn:active { background: rgba(245,200,66,0.12); border-color: var(--gold); color: var(--gold); }

.diff-chips { display: flex; gap: 8px; width: 100%; }
.diff-chip {
    flex: 1; background: rgba(0,0,0,0.4); border: 2px solid rgba(255,255,255,0.35); color: #fff;
    padding: 9px 4px; border-radius: 10px; font-size: calc(16px * var(--ts, 1)); letter-spacing: 1px; cursor: pointer; transition: all .2s;
}
.diff-chip.sel, .diff-chip:hover { border-color: var(--gold); background: rgba(245,200,66,.22); color: var(--gold); }

.char-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; margin-top: 16px; width: 100%; }
.char-card { background: rgba(0,0,0,0.4); border: 2px solid rgba(255,255,255,0.4); border-radius: 16px; padding: 12px 8px; text-align: center; cursor: pointer; transition: transform .2s, border-color .2s; width: 100%; }
.char-card.sel { border-color: var(--gold); background: rgba(245,200,66,.2); box-shadow: 0 0 20px rgba(245,200,66,0.4); }

.char-icon { font-size: calc(40px * var(--ts, 1)); margin-bottom: 6px; display: block; }
.char-name { color: #fff; font-weight: bold; font-size: calc(15px * var(--ts, 1)); }

/* Map */
#map-ui { position: fixed; inset: 0; z-index: 50; display: none; flex-direction: column; justify-content: flex-end; align-items: center; padding-bottom: 32px; pointer-events: none; }
.map-panel { width: 92%; max-width: 580px; background: var(--panel); padding: 18px 20px; border-radius: 20px; border: 1px solid var(--border); pointer-events: auto; display: flex; flex-direction: column; align-items: center; gap: 12px; box-shadow: 0 10px 30px rgba(0,0,0,.8); }
.map-header { display: flex; justify-content: space-between; align-items: center; width: 100%; }
.map-title { color: var(--gold); font-size: calc(26px * var(--ts, 1)); letter-spacing: 2px; }
.map-counter { font-size: calc(20px * var(--ts, 1)); color: #fff; background: rgba(255,255,255,.1); padding: 4px 14px; border-radius: 10px; border: 1px solid rgba(255,255,255,.2); }
#map-slider { width: 100%; appearance: none; height: 8px; background: rgba(255,255,255,.18); border-radius: 4px; outline: none; }
#map-slider::-webkit-slider-thumb { appearance: none; width: 28px; height: 28px; border-radius: 50%; background: var(--gold); cursor: pointer; box-shadow: 0 0 15px var(--gold); }
.map-labels { display: flex; justify-content: space-between; width: 100%; font-size: calc(12px * var(--ts, 1)); font-weight: bold; color: #666; }
#map-tooltip { position: fixed; background: rgba(0,0,0,.95); border: 2px solid var(--gold); color: #fff; padding: 10px 18px; border-radius: 12px; font-family: 'Bebas Neue'; font-size: calc(20px * var(--ts, 1)); pointer-events: none; display: none; transform: translate(-50%, -110%); text-align: center; z-index: 60; box-shadow: 0 10px 20px rgba(0,0,0,.5); white-space: nowrap; }
.map-dist { font-family: 'Nunito'; font-size: calc(13px * var(--ts, 1)); color: #ccc; font-weight: bold; }

/* HUD */
.hud-bar { pointer-events: auto; position: absolute; left: 0; right: 0; height: var(--hud-h); background: var(--panel); display: flex; justify-content: space-between; align-items: center; padding: 0 16px; border: 1px solid var(--border); }
/* `--seat` is set per bar by UIManager: in the solo layout the bottom bar
   changes owner every turn, so its accent has to follow the seat rather than
   be baked in. The fallbacks keep two-player matches exactly as they were. */
#hud-p2 { top: 0; border-bottom: 4px solid var(--seat, var(--p2)); }
#hud-p1 { bottom: 0; border-top: 4px solid var(--seat, var(--p1)); }
.tabletop-mode #hud-p2 { transform: rotate(180deg); }

/* ---- Rival strip: the 3-and-4-player HUD ------------------------------------
   Four full bars leave no board, so above two seats every player except the one
   holding this device collapses into one scrollable row of chips along the top.
   The strip is laid out to be READ, not operated: nothing in it is tappable, so
   it can be dense in a way an action bar cannot. */
#hud-rivals {
    pointer-events: auto; position: absolute; top: 0; left: 0; right: 0;
    display: none; gap: 6px; padding: 6px 8px;
    min-height: var(--rival-h);
    background: var(--panel); border-bottom: 1px solid var(--border);
    overflow-x: auto; overflow-y: hidden; scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
#hud-rivals::-webkit-scrollbar { display: none; }
body[data-hud-mode="solo"] #hud-rivals { display: flex; }

.rival-chip {
    flex: 0 0 auto; display: flex; align-items: center; gap: 7px;
    padding: 5px 10px; border-radius: 10px;
    background: rgba(255,255,255,.05);
    border: 1px solid rgba(255,255,255,.12);
    border-left: 4px solid var(--rc);
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 700;
    color: rgba(255,255,255,.82); white-space: nowrap;
    transition: background .18s ease, border-color .18s ease;
}
/* Whose go it is. The only state in the strip worth a colour. */
.rival-chip.rival-up {
    background: color-mix(in srgb, var(--rc) 22%, transparent);
    border-color: var(--rc);
    box-shadow: 0 0 12px color-mix(in srgb, var(--rc) 45%, transparent);
}
.rival-chip .rc-name  { color: var(--rc); font-weight: 900; letter-spacing: .2px; }
.rival-chip .rc-coins { font-variant-numeric: tabular-nums; }
.rival-chip .rc-pos   { opacity: .55; font-weight: 600; max-width: 96px; overflow: hidden; text-overflow: ellipsis; }
.rival-chip .rc-bag,
.rival-chip .rc-bud   { opacity: .8; }
.rival-chip .bot-badge { margin-left: 4px; }

/* The solo bar is the seat's own colour, set inline from PLAYER_SLOTS, and the
   board has to start below the strip rather than under the (absent) top bar. */
body[data-hud-mode="solo"] #round-counter { top: calc(var(--rival-h) + 8px); }
body[data-hud-mode="solo"] #contracts-strip { top: calc(var(--rival-h) + 40px); }

/* Tabletop mode: flip center UI and the 3D canvas when P2 is the active player */
.tabletop-p2-turn #game-container canvas { transform: rotate(180deg); }
/* Tabletop, Player 2's turn: the rail moves to THEIR edge and faces them. */
.tabletop-p2-turn #toast-box {
    bottom: auto; top: calc(var(--hud-h) + 10px);
    transform: translateX(-50%) rotate(180deg);
    flex-direction: column-reverse;
}
/* .modal-box, #mg-page-info and #mg-page-hold are rotated through the
   --card-rot custom property instead (see the DUAL READ block at the end of
   this file), so the turn-flip and the manual ⟳ flip can compose. */
.tabletop-p2-turn #space-info-card { bottom: auto; top: calc(var(--hud-h) + 90px); transform: translateX(-50%) rotate(180deg); }
.tabletop-p2-turn #swipe-zone { transform: rotate(180deg); }
.tabletop-p2-turn #gate-overlay > * { transform: rotate(180deg); }
/* Every full-screen scene that shows text has to face the active player too.
   Previously only the HUD, toasts, modals and the gate's children were covered,
   so branch choices, realm banners, the story card, the minigame intro and the
   map panel all stayed locked to Player 1's side of the table. */
.tabletop-p2-turn #branch-choice-overlay > *,
.tabletop-p2-turn #realm-banner > *,
.tabletop-p2-turn #hbd-story-overlay > *,
.tabletop-p2-turn #mg-intro-overlay #mg-intro-steps,
.tabletop-p2-turn .map-panel,
.tabletop-p2-turn #round-counter,
.tabletop-p2-turn #contracts-strip { transform: rotate(180deg); }
.tabletop-p2-turn #round-counter { transform-origin: center; }
/* The map's control panel is pinned to the bottom of the screen, which is
   Player 1's edge. Turning it round in place still left P2 reaching across the
   whole phone for the slider, so it moves to their end as well. */
.tabletop-p2-turn #map-ui { justify-content: flex-start; padding-bottom: 0; padding-top: 32px; }
/* The tooltip carries its own offset — it hangs ABOVE the tile you tapped, via
   translate(-50%,-110%). The blanket rotate above replaced that transform
   outright, so for P2 the card lost its offset and landed on top of the tile.
   Rotate AND offset, to the side that is "above" from where P2 is sitting. */
.tabletop-p2-turn #map-tooltip { transform: translate(-50%, 10%) rotate(180deg); }
.hud-info { display: flex; flex-direction: column; gap: 2px; }
.hud-name { font-size: calc(18px * var(--ts, 1)); letter-spacing: 2px; }
.p1c { color: var(--p1); text-shadow: 0 0 10px var(--p1); }
.p2c { color: var(--p2); text-shadow: 0 0 10px var(--p2); }
.bot-badge { background: #9333ea; color: white; font-size: calc(12px * var(--ts, 1)); padding: 2px 6px; border-radius: 4px; vertical-align: middle; margin-left: 6px; font-family: 'Nunito'; }
.hud-coins { font-size: calc(26px * var(--ts, 1)); color: #4ade80; display: flex; align-items: center; gap: 6px; text-shadow: 0 0 10px #4ade80; }
.hud-inv { display: flex; gap: 4px; flex-wrap: wrap; max-width: 90px; }
.inv-slot { width: 24px; height: 24px; background: rgba(255,255,255,.15); border-radius: 5px; display: flex; align-items: center; justify-content: center; font-size: calc(13px * var(--ts, 1)); border: 1px solid rgba(255,255,255,.2); }
.hud-badges { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.hud-turn-badge { display: none; font-size: calc(18px * var(--ts, 1)); color: var(--gold); padding: 3px 10px; background: rgba(245,200,66,.2); border-radius: 14px; border: 2px solid var(--gold); animation: pulse 1.5s infinite; }
.active-turn .hud-turn-badge { display: block; }
.hud-pos-badge { font-size: calc(14px * var(--ts, 1)); color: rgba(255,255,255,.5); padding: 2px 8px; background: rgba(255,255,255,.06); border-radius: 8px; }
@keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: .55 } }

/* Actions */
/* The turn controls are a column of SQUARES down the acting player's right
   side, vertically centred rather than parked in the bottom corner. Square
   because a full-width row of pills ate half the board while saying no more
   than an icon and a word does; centred because the middle of the right edge is
   the easiest place on a phone to reach without shifting your grip.

   `column-reverse` puts ROLL nearest the player's own edge in every hold: P1
   reads bottom-up from the bottom edge, and P2's stack is rotated a half turn
   in tabletop so it reads the same way from theirs. */
.action-row {
    position: absolute; right: 12px; left: auto;
    top: 50%; transform: translateY(-50%);
    display: none; flex-direction: column-reverse; gap: 10px;
    width: 86px;
    pointer-events: auto; z-index: 20;
}
/* Tabletop: P2 faces the other way, so their right hand is the screen's left. */
.tabletop-mode #p2-actions {
    left: 12px; right: auto;
    transform: translateY(-50%) rotate(180deg);
}
.btn-action {
    background: rgba(0,0,0,.6); border: 2px solid rgba(255,255,255,.4); color: #fff;
    width: 86px; height: 86px; padding: 0; border-radius: 22px;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    letter-spacing: .5px; cursor: pointer; white-space: nowrap; text-align: center;
}
.btn-action .ba-ic { font-size: calc(27px * var(--ts, 1)); line-height: 1; }
.btn-action .ba-tx { font-size: calc(14px * var(--ts, 1)); line-height: 1; }
.btn-action:active { transform: scale(.96); }
.btn-action.roll-btn { background: linear-gradient(135deg, #d4960e, var(--gold)); color: #000; font-weight: bold; border: none; box-shadow: 0 4px 15px rgba(245,200,66,.6); }
.btn-action.map-btn { background: linear-gradient(135deg, #1e3a8a, #3b82f6); color: #fff; border: none; box-shadow: 0 4px 15px rgba(59,130,246,.5); }

#swipe-zone { position: absolute; inset: var(--hud-h) 0 var(--hud-h) 0; pointer-events: auto; display: none; flex-direction: column; align-items: center; justify-content: center; z-index: 15; }
#swipe-zone.act { display: flex; }
@keyframes swipeAnim { 0% { transform: translateY(20px); opacity: 0 } 50% { opacity: 1 } 100% { transform: translateY(-40px); opacity: 0 } }

/* Space Info Card */
#space-info-card { position: fixed; bottom: calc(var(--hud-h) + 90px); left: 50%; transform: translateX(-50%); z-index: 155; background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 10px 20px; display: none; flex-direction: column; align-items: center; gap: 4px; pointer-events: none; text-align: center; box-shadow: 0 8px 24px rgba(0,0,0,.8); }
.sic-type { font-size: calc(22px * var(--ts, 1)); letter-spacing: 1px; color:#fff; }
.sic-desc { font-size: calc(13px * var(--ts, 1)); color: rgba(255,255,255,.7); font-family: 'Nunito'; }

/* Modal Boxes */
.modal-box { background: var(--panel); border: 1px solid var(--border); border-radius: 20px; padding: 26px; width: 92%; max-width: 420px; text-align: center; box-shadow: 0 20px 50px rgba(0,0,0,.8); max-height: 90vh; overflow-y: auto; touch-action: pan-y; }
.m-title { font-size: calc(32px * var(--ts, 1)); color: #fff; margin-bottom: 7px; letter-spacing: 2px; }
/* The character screen's second line: whose go it is stays big, the standing
   instruction under it does not. */
.cs-sub { display: block; font-size: calc(15px * var(--ts, 1)); letter-spacing: 2px; opacity: .72; margin-top: 2px; }
/* The "let a bot play this seat" offer sits under CONFIRM and must not end
   up flush against the bottom of a short screen. */
#btn-seat-bot { margin-top: 8px; margin-bottom: 4px; }
.m-desc { font-size: calc(16px * var(--ts, 1)); color: rgba(255,255,255,.85); margin-bottom: 20px; line-height: 1.4; }
.m-row { display: flex; justify-content: space-between; align-items: center; background: rgba(255,255,255,.09); padding: 10px 14px; border-radius: 12px; margin-bottom: 8px; }
.m-row-info { text-align: left; color: #fff; flex: 1; padding-right: 10px; }
.m-row-info b { display: block; font-size: calc(15px * var(--ts, 1)); font-family:'Nunito'; }
.m-row-info small { font-size: calc(12px * var(--ts, 1)); color: #999; line-height: 1.3; display: block; margin-top: 2px; font-family:'Nunito'; }
.btn-buy { background: linear-gradient(135deg, #16a34a, #22c55e); color: #fff; border: none; padding: 8px 14px; border-radius: 8px; font-weight: bold; cursor: pointer; font-size: calc(13px * var(--ts, 1)); white-space: nowrap; font-family:'Nunito';}
.btn-buy:disabled { filter: grayscale(1); opacity: .45; cursor: not-allowed; }
.btn-close { background: rgba(255,255,255,.16); color: #fff; border: none; padding: 12px 26px; border-radius: 10px; font-weight: bold; cursor: pointer; margin-top: 10px; width: 100%; font-size: calc(16px * var(--ts, 1)); font-family:'Bebas Neue'; letter-spacing: 1px;}
.btn-close.primary { background: linear-gradient(135deg, #3b82f6, #60a5fa); color: #fff; box-shadow: 0 4px 15px rgba(59,130,246,.4); }
.drop-item-btn { background: rgba(0,0,0,.5); border: 2px solid rgba(255,255,255,.5); color: #fff; font-family: 'Nunito'; font-size: calc(14px * var(--ts, 1)); font-weight: 700; padding: 10px 16px; border-radius: 12px; cursor: pointer; display: flex; align-items: center; gap: 7px; margin:4px; display:inline-flex;}
/* ── Bag-full discard picker ──────────────────────────────────────────────
   Four cards: the three you carry plus the one you just picked up. Two
   columns, because four full-width rows plus a title, a line of copy and two
   buttons does not fit above the fold on a phone. */
.drop-grid {
    display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
    margin: 12px 0 14px;
}
.drop-card {
    position: relative; text-align: left; cursor: pointer;
    background: rgba(0,0,0,.5); border: 2px solid rgba(255,255,255,.28);
    border-radius: 14px; padding: 10px 11px 11px;
    color: #fff; font-family: 'Nunito', system-ui, sans-serif;
    transition: border-color .16s, background .16s, transform .16s;
}
.drop-card .dc-ic   { font-size: calc(26px * var(--ts, 1)); line-height: 1; margin-bottom: 5px; }
.drop-card .dc-name { font-size: calc(13px * var(--ts, 1)); font-weight: 800; letter-spacing: .2px; margin-bottom: 3px; }
.drop-card .dc-desc { font-size: calc(12px * var(--ts, 1)); line-height: 1.35; color: rgba(255,255,255,.6); }
/* The incoming item is the odd one out and has to read that way at a glance. */
.drop-card.incoming { border-color: rgba(74,222,128,.55); background: rgba(74,222,128,.08); }
.drop-card .dc-tag {
    position: absolute; top: -8px; right: 8px;
    font-size: calc(12px * var(--ts, 1)); font-weight: 900; letter-spacing: .8px;
    padding: 2px 7px; border-radius: 999px;
    background: #4ade80; color: #06240f;
}
/* Selected = the one about to be thrown away, so it goes red, not gold. */
.drop-card.sel {
    border-color: #f97316; background: rgba(249,115,22,.16);
    transform: translateY(-2px);
}
.drop-card.sel .dc-toss {
    display: block; position: absolute; top: -8px; left: 8px;
    font-size: calc(12px * var(--ts, 1)); font-weight: 900; letter-spacing: .8px;
    padding: 2px 7px; border-radius: 999px;
    background: #f97316; color: #2a1002;
}
.drop-card .dc-toss { display: none; }
#btn-confirm-drop:disabled { opacity: .4; cursor: default; }

.inv-full-note { background: rgba(245,158,11,0.18); border: 1px solid rgba(245,158,11,0.5); color: #fbbf24; border-radius: 10px; padding: 8px 14px; font-size: calc(13px * var(--ts, 1)); font-family: 'Nunito'; font-weight: 700; margin-bottom: 8px; text-align: center; }

/* Gate.
   This was `background: rgba(0,0,0,.95)` across the whole screen — you rolled
   to break through a gate you could not see, and the one permanent change to
   the board in a match happened behind a black panel. It is now a transparent
   layer with a compact card on the ACTIVE player's own edge, so the gate itself
   (which the camera is pointed at) stays on screen the whole time.
   `#ui-layer` stays hidden throughout, which is what keeps items and the ordinary
   roll unavailable at the gate — the only control here is the gate's own. */
#gate-overlay {
    position: fixed; inset: 0; z-index: 180; background: transparent;
    display: none; flex-direction: column; align-items: center;
    justify-content: flex-end; gap: 8px; color: #fff; text-align: center;
    padding: 18px 16px calc(var(--hud-h) + 16px);
    pointer-events: none;
}
/* Everything inside the card takes input; the layer itself does not, so the
   board behind it is never swallowed. */
#gate-overlay > * { pointer-events: auto; }
.tabletop-p2-turn #gate-overlay {
    justify-content: flex-start;
    padding: calc(var(--hud-h) + 16px) 16px 18px;
}
.gate-card {
    width: 100%; max-width: 400px;
    display: flex; flex-direction: column; align-items: center; gap: 7px;
    background: rgba(8, 6, 18, .93);
    border: 1.5px solid rgba(245, 158, 11, .55);
    border-radius: 20px; padding: 14px 16px 16px;
    box-shadow: 0 14px 44px rgba(0,0,0,.7), 0 0 30px -10px rgba(245,158,11,.55);
    backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
}
.gate-icon { font-size: calc(30px * var(--ts, 1)); line-height: 1; }
.gate-title { font-size: calc(34px * var(--ts, 1)); color: #f59e0b; letter-spacing: 3px; text-shadow: 0 0 22px rgba(245,158,11,.7); line-height: 1; }
.gate-sub { font-size: calc(12.5px * var(--ts, 1)); color: rgba(255,255,255,.75); line-height: 1.35; font-family: 'Nunito'; }
.gate-noitems {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); letter-spacing: .3px;
    color: rgba(255,255,255,.45);
}
.gate-roll-btn { font-size: calc(22px * var(--ts, 1)); padding: 11px 34px; border-radius: 14px; border: none; background: linear-gradient(135deg, #b45309, #f59e0b); color: #000; cursor: pointer; box-shadow: 0 5px 18px rgba(245,158,11,.5); width: 100%; }
.gate-roll-btn:disabled { filter: grayscale(1); opacity: .45; cursor: not-allowed; }
.gate-result { font-size: calc(26px * var(--ts, 1)); min-height: 0; color: var(--gold); line-height: 1.1; }
.gate-sum { font-size: calc(15px * var(--ts, 1)); color: rgba(255,255,255,.55); }
.gate-open-banner { font-size: calc(20px * var(--ts, 1)); color: #4ade80; text-shadow: 0 0 16px #4ade80; display: none; line-height: 1.2; }
#gate-overlay .btn-close { width: 100%; }

/* Minigame Intro & Orientation */
#mg-intro-overlay { position: fixed; inset: 0; z-index: 250; background: rgba(0,0,0,.97); display: none; flex-direction: column; align-items: center; justify-content: center; color: #fff; text-align: center; padding: 20px; }
.mg-intro-title { font-size: calc(58px * var(--ts, 1)); color: var(--gold); letter-spacing: 3px; margin-bottom: 14px; text-shadow: 0 0 20px rgba(245,200,66,.8); }
#mg-intro-steps { display: flex; gap: 0; width: 100%; max-width: 420px; margin-bottom: 18px; }
.mg-step { flex: 1; height: 4px; background: rgba(255,255,255,.2); border-radius: 2px; margin: 0 3px; transition: background .4s; }
.mg-step.done { background: var(--gold); }
#mg-intro-content { width: 100%; max-width: 420px; }

.orient-name { font-size: calc(42px * var(--ts, 1)); color: var(--gold); letter-spacing: 3px; margin-bottom: 2px; text-shadow: 0 0 20px rgba(245,200,66,.6); }
.orient-subtitle { font-size: calc(14px * var(--ts, 1)); font-family: 'Nunito'; color: rgba(255,255,255,.6); margin-bottom: 18px; letter-spacing: 1px; text-transform: uppercase; }
.orient-instructions { font-size: calc(13px * var(--ts, 1)); font-family: 'Nunito'; color: rgba(255,255,255,.75); max-width: 300px; margin: 14px auto 18px; line-height: 1.55; text-align: center; }

.phone-diagram-wrap { display: flex; align-items: center; justify-content: center; width: 100%; margin: 60px auto 60px; }
.phone-diagram { position: relative; width: 110px; height: 210px; }
.ph-body { position: absolute; inset: 0; background: #1a1a2e; border: 3px solid rgba(255,255,255,.25); border-radius: 18px; box-shadow: 0 0 30px rgba(0,0,0,.6), inset 0 0 0 2px rgba(255,255,255,.06); }
.ph-screen { position: absolute; inset: 12px 8px; background: #0d0d1f; border-radius: 10px; overflow: hidden; }
.ph-screen-p1 { position: absolute; bottom: 0; left: 0; right: 0; height: 50%; display: flex; align-items: center; justify-content: center; }
.ph-screen-p2 { position: absolute; top: 0; left: 0; right: 0; height: 50%; display: flex; align-items: center; justify-content: center; transform: rotate(180deg); }
.ph-divider { position: absolute; top: 50%; left: 0; right: 0; height: 2px; background: rgba(255,255,255,.15); transform: translateY(-50%); }
.ph-home { position: absolute; bottom: 4px; left: 50%; transform: translateX(-50%); width: 28px; height: 4px; background: rgba(255,255,255,.2); border-radius: 3px; }
.ph-camera { position: absolute; top: 6px; left: 50%; transform: translateX(-50%); width: 8px; height: 8px; background: rgba(255,255,255,.15); border-radius: 50%; }

.ph-grip { position: absolute; left: -3px; right: -3px; height: 38px; border-radius: 18px; display: flex; align-items: center; justify-content: center; gap: 6px; font-family: 'Bebas Neue'; font-size: calc(13px * var(--ts, 1)); letter-spacing: 1px; }
.ph-grip.p1 { bottom: -3px; background: rgba(255,59,59,.85); border: 2px solid #ff3b3b; color: #fff; border-bottom-left-radius: 18px; border-bottom-right-radius: 18px; border-top-left-radius: 4px; border-top-right-radius: 4px; box-shadow: 0 4px 16px rgba(255,59,59,.4); }
.ph-grip.p2 { top: -3px; background: rgba(59,142,255,.85); border: 2px solid #3b8eff; color: #fff; border-top-left-radius: 18px; border-top-right-radius: 18px; border-bottom-left-radius: 4px; border-bottom-right-radius: 4px; box-shadow: 0 -4px 16px rgba(59,142,255,.4); }

.ph-thumb { position: absolute; width: 22px; height: 22px; border-radius: 50%; border: 3px solid; display: flex; align-items: center; justify-content: center; font-size: calc(11px * var(--ts, 1)); font-family: 'Bebas Neue'; }
.ph-thumb.p1 { background: rgba(255,59,59,.3); border-color: #ff3b3b; color: #ff3b3b; bottom: 50px; }
.ph-thumb.p2 { background: rgba(59,142,255,.3); border-color: #3b8eff; color: #3b8eff; top: 50px; transform: rotate(180deg); }

@keyframes thumbPulse { 0%,100% { transform: scale(1); opacity: .8; } 50% { transform: scale(1.25); opacity: 1; } }
@keyframes thumbPulseRot { 0%,100% { transform: rotate(180deg) scale(1); opacity: .8; } 50% { transform: rotate(180deg) scale(1.25); opacity: 1; } }
@keyframes gripGlow1 { 0%,100% { box-shadow: 0 4px 16px rgba(255,59,59,.4); } 50% { box-shadow: 0 4px 28px rgba(255,59,59,.9); } }
@keyframes gripGlow2 { 0%,100% { box-shadow: 0 -4px 16px rgba(59,142,255,.4); } 50% { box-shadow: 0 -4px 28px rgba(59,142,255,.9); } }

.ph-grip.p1 { animation: gripGlow1 2s ease-in-out infinite; }
.ph-grip.p2 { animation: gripGlow2 2s ease-in-out infinite; }

/* Minigame zone styles */
.mg-zone { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; position: relative; overflow: hidden; pointer-events: auto; width:100%; transition: background 0.3s; }
#mg-p2 { background: rgba(59,142,255,.12); border-bottom: 3px solid var(--p2); transform: rotate(180deg); }
#mg-p1 { background: rgba(255,59,59,.12); border-top: 3px solid var(--p1); }
/* The status strip used to be a 76px opaque band in the flex column between the
   two zones. Every minigame overlay is `position:absolute; inset:0`, so every
   one of them painted straight over it — the clock, the score and the prompt
   that fifteen games write here have been invisible the whole time. It is now a
   compact floating pill above the game instead, in two copies (see
   MinigameManager._syncNeutralMirror) so both players can read it, one pinned to
   each player's own outer edge — the centre line is contested space in a
   face-off game (ClearOut's gap, Sumo's arena) and a bar across it hides the
   thing you are playing with. Taking it out of the flex flow also gives the two
   halves an honest 50/50 split, which is what the overlays already assumed. */
#mg-neutral {
    position: absolute; left: 50%; bottom: 10px; z-index: 60;
    transform: translateX(-50%);
    max-width: 92%; padding: 5px 16px; border-radius: 12px;
    background: rgba(8, 6, 18, 0.74); border: 1px solid rgba(255,255,255,.13);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    font-size: calc(17px * var(--ts, 1)); line-height: 1.1; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
    color: var(--gold); letter-spacing: 2px; pointer-events: none;
    text-shadow: 0 0 10px rgba(245,200,66,.55); transition: color 0.2s;
}
/* Played alone there is no second half to keep the strip out of, and no
   mirrored copy whose height has to match — so a long line wraps instead of
   being cut off mid-word. "DODGE THE METEORS! DRAG TO MOV" was the whole
   instruction on a 412px phone. */
#minigame-layer.is-solo #mg-neutral {
    white-space: normal; text-align: center; font-size: calc(15px * var(--ts, 1));
    max-width: 88%; line-height: 1.25; padding: 7px 14px;
}

/* The far player's copy, back to back with the first across the centre line. */
#mg-neutral-mirror {
    position: absolute; left: 50%; top: 10px; z-index: 60;
    transform: translateX(-50%) rotate(180deg);
    max-width: 92%; padding: 5px 16px; border-radius: 12px;
    background: rgba(8, 6, 18, 0.74); border: 1px solid rgba(255,255,255,.13);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center;
    font-size: calc(17px * var(--ts, 1)); line-height: 1.1; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
    color: var(--gold); letter-spacing: 2px; pointer-events: none;
    text-shadow: 0 0 10px rgba(245,200,66,.55);
}
.mg-ready-btn { font-size: calc(30px * var(--ts, 1)); padding: 16px 48px; border-radius: 16px; border: 3px solid rgba(255,255,255,.4); cursor: pointer; background: rgba(255,255,255,.12); color: #fff; letter-spacing: 2px; transition: background .15s, transform .1s, border-color .15s; }
.mg-ready-btn:active { transform: scale(.94); }
.mg-ready-btn.ready { background: rgba(74,222,128,.35); border-color: #4ade80; color: #4ade80; box-shadow: 0 0 20px rgba(74,222,128,.4); }
.mg-victory { animation: mgVictoryPulse .8s ease both; }
@keyframes mgVictoryPulse {
    0%   { background-color: transparent; }
    20%  { background-color: rgba(245,200,66,.32); box-shadow: inset 0 0 80px rgba(245,200,66,.5); filter: brightness(1.5); }
    60%  { background-color: rgba(245,200,66,.18); box-shadow: inset 0 0 50px rgba(245,200,66,.3); filter: brightness(1.2); }
    100% { background-color: transparent; box-shadow: none; filter: brightness(1); }
}
.mg-defeat { animation: mgDefeatFlash .6s ease both; }
@keyframes mgDefeatFlash {
    0%   { background-color: transparent; }
    25%  { background-color: rgba(239,68,68,.28); box-shadow: inset 0 0 60px rgba(239,68,68,.4); filter: brightness(0.7) saturate(0.5); }
    100% { background-color: transparent; box-shadow: none; filter: brightness(1) saturate(1); }
}

/* Win Screen */
#win-screen { position: fixed; inset: 0; z-index: 500; background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); display: none; flex-direction: column; align-items: center; justify-content: center; overflow: hidden; }
.win-confetti { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.confetti-piece { position: absolute; width: 10px; height: 10px; border-radius: 2px; animation: confettiFall linear forwards; }
@keyframes confettiFall { 0% { transform: translateY(-20px) rotate(0deg); opacity: 1; } 100% { transform: translateY(110vh) rotate(720deg); opacity: 0; } }
.win-crown { font-size: calc(80px * var(--ts, 1)); animation: crownBounce .6s ease backwards; margin-bottom: 4px; }
@keyframes crownBounce { 0% { transform: scale(0) rotate(-20deg); } 70% { transform: scale(1.15) rotate(5deg); } 100% { transform: scale(1) rotate(0deg); } }
.win-name { font-size: calc(52px * var(--ts, 1)); color: var(--gold); font-family: 'Bebas Neue'; letter-spacing: 3px; text-shadow: 0 0 30px rgba(245,200,66,.8); animation: fadeSlideUp .5s .3s backwards ease; }
.win-subtitle { font-size: calc(20px * var(--ts, 1)); color: rgba(255,255,255,.7); font-family: 'Nunito'; margin-bottom: 20px; animation: fadeSlideUp .5s .5s backwards ease; }
@keyframes fadeSlideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
.win-cards { display: flex; gap: 12px; flex-wrap: wrap; justify-content: center; width: 100%; max-width: 500px; padding: 0 16px; animation: fadeSlideUp .5s .7s backwards ease; }
.win-card { background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.18); border-radius: 20px; padding: 16px 20px; flex: 1; min-width: 140px; text-align: center; }
.win-card.winner-card { border-color: var(--gold); background: rgba(245,200,66,.1); }
.win-card-name { font-size: calc(18px * var(--ts, 1)); font-family: 'Bebas Neue'; letter-spacing: 1px; color: #fff; margin-bottom: 8px; }
.win-card-score { font-size: calc(36px * var(--ts, 1)); font-family: 'Bebas Neue'; color: var(--gold); margin-bottom: 8px; }
.win-card-stat { display: flex; justify-content: space-between; font-family: 'Nunito'; font-size: calc(13px * var(--ts, 1)); color: rgba(255,255,255,.7); margin-bottom: 4px; }
.win-btn { margin-top: 22px; animation: fadeSlideUp .5s 1.2s backwards ease; }

.toast { font-size: calc(22px * var(--ts, 1)); background: rgba(0,0,0,.88); padding: 12px 26px; border-radius: 40px; border: 2px solid; animation: toastAnim 2.5s forwards; white-space: nowrap; z-index: 9999; }
@keyframes toastAnim { 0% { opacity: 0; transform: translateY(20px) scale(.9) } 15% { opacity: 1; transform: none } 85% { opacity: 1 } 100% { opacity: 0; transform: translateY(-20px) } }

.coin-changed { animation: coinPop .4s ease; }
@keyframes coinPop { 0% { transform: scale(1); } 50% { transform: scale(1.4); color: #fbbf24; } 100% { transform: scale(1); } }

.dice-pick-btn { width: 72px; height: 72px; border-radius: 16px; border: 3px solid rgba(255,255,255,.5); background: rgba(0,0,0,.5); color: #fff; font-size: calc(36px * var(--ts, 1)); cursor: pointer; transition: background .15s, transform .1s; }
.dice-pick-btn:active { background: var(--gold); transform: scale(.92); }

/* Minigame element styles */
.math-question { font-size: calc(40px * var(--ts, 1)); color: #fff; letter-spacing: 2px; margin-bottom: 16px; text-align: center; display: none; }
.math-answers { display: none; grid-template-columns: 1fr 1fr; gap: 10px; width: 90%; max-width: 320px; }
.math-ans-btn { background: rgba(255,255,255,.12); border: 2px solid rgba(255,255,255,.3); color: #fff; font-size: calc(28px * var(--ts, 1)); padding: 14px; border-radius: 14px; cursor: pointer; width: 100%; transition: background .15s; }
.math-ans-btn:active { background: rgba(245,200,66,.3); }
.math-ans-btn.correct-ans { background: rgba(74,222,128,.3); border-color: #4ade80; }
.math-ans-btn.wrong-ans { background: rgba(239,68,68,.3); border-color: #ef4444; }

.trivia-q { font-size: calc(18px * var(--ts, 1)); color: #fff; font-family: 'Nunito'; text-align: center; padding: 10px 16px; max-width: 340px; line-height: 1.4; display: none; margin-bottom: 10px; }
.trivia-grid { display: none; grid-template-columns: 1fr 1fr; gap: 8px; width: 90%; max-width: 320px; }
.trivia-btn { background: rgba(255,255,255,.12); border: 2px solid rgba(255,255,255,.3); color: #fff; font-size: calc(14px * var(--ts, 1)); font-family: 'Nunito'; padding: 10px 8px; border-radius: 12px; cursor: pointer; text-align: center; line-height: 1.3; transition: background .15s; }
.trivia-btn:active, .trivia-btn.cm-tapped { transform: scale(.96); filter: brightness(1.25); }
.trivia-btn.correct { background: rgba(74,222,128,.3); border-color: #4ade80; }
.trivia-btn.wrong { background: rgba(239,68,68,.3); border-color: #ef4444; }

.trace-container { position: relative; width: 90%; max-width: 320px; height: 200px; display: none; }
.trace-node { position: absolute; width: 38px; height: 38px; border-radius: 50%; background: rgba(255,255,255,.15); border: 3px solid rgba(255,255,255,.4); color: #fff; display: flex; align-items: center; justify-content: center; font-size: calc(16px * var(--ts, 1)); transform: translate(-50%,-50%); cursor: pointer; transition: background .2s; z-index: 2; }
.trace-node.next { background: rgba(245,200,66,.4); border-color: var(--gold); box-shadow: 0 0 12px var(--gold); animation: pulse 1s infinite; }
.trace-node.done { background: rgba(74,222,128,.4); border-color: #4ade80; }
.trace-node.wrong-shape { background: rgba(239,68,68,.45); border-color: #ef4444; box-shadow: 0 0 16px rgba(239,68,68,.7); }
.trace-line { position: absolute; height: 3px; background: rgba(255,255,255,.2); transform-origin: left center; border-radius: 2px; pointer-events: none; }

.reaction-zone { width: 90%; max-width: 320px; height: 160px; border-radius: 20px; display: none; align-items: center; justify-content: center; font-size: calc(36px * var(--ts, 1)); letter-spacing: 3px; cursor: pointer; border: 4px solid rgba(255,255,255,.2); background: rgba(255,255,255,.08); }
.reaction-zone.waiting { border-color: rgba(255,255,255,.2); background: rgba(255,255,255,.05); color: rgba(255,255,255,.5); }
.reaction-zone.go { border-color: #4ade80; background: rgba(74,222,128,.2); color: #4ade80; animation: pulse .3s infinite; }
.reaction-zone.won { border-color: var(--gold); background: rgba(245,200,66,.2); color: var(--gold); }
.reaction-zone.too-early { border-color: #ef4444; background: rgba(239,68,68,.2); color: #ef4444; }

.color-round-label { font-size: calc(16px * var(--ts, 1)); color: rgba(255,255,255,.6); font-family: 'Nunito'; display: none; }
.color-word { font-size: calc(42px * var(--ts, 1)); font-family: 'Bebas Neue'; letter-spacing: 3px; display: none; margin: 8px 0; }
.color-btns { display: none; gap: 10px; flex-wrap: wrap; justify-content: center; width: 90%; max-width: 320px; }
.color-btn { width: 64px; height: 64px; border-radius: 50%; border: 4px solid; cursor: pointer; transition: transform .1s, filter .1s; color:#050505; font-family:'Bebas Neue'; font-size: calc(18px * var(--ts, 1)); letter-spacing:1px; text-shadow:0 1px 0 rgba(255,255,255,.45); position:relative; overflow:hidden; }
.color-btn:active { transform: scale(.88); }
.color-btn::after { content: attr(data-symbol); position:absolute; inset:0; display:flex; align-items:center; justify-content:center; color:rgba(0,0,0,.62); font-size: calc(22px * var(--ts, 1)); }
.color-btn.cm-tapped { filter: brightness(1.45); transform: scale(.9); }
.color-btn.cm-wrong { animation: badTap .24s ease 2; box-shadow:0 0 22px rgba(239,68,68,.75); }

.memory-grid { display: none; grid-template-columns: repeat(3, 1fr); gap: 8px; width: 90%; max-width: 260px; touch-action:none; }
.mem-card { background: rgba(255,255,255,.1); border: 2px solid rgba(255,255,255,.25); border-radius: 12px; aspect-ratio: 1; display: flex; align-items: center; justify-content: center; font-size: calc(28px * var(--ts, 1)); cursor: pointer; transition: background .2s; }
.mem-card:active { transform: scale(.94); filter: brightness(1.35); }
.mem-card.flipped { background: rgba(245,200,66,.2); border-color: var(--gold); }
.mem-card.matched { background: rgba(74,222,128,.2); border-color: #4ade80; cursor: default; }
.mem-score { display: none; font-size: calc(18px * var(--ts, 1)); color: rgba(255,255,255,.7); font-family: 'Nunito'; margin-top: 8px; }

.seq-display { display: none; gap: 8px; margin-bottom: 16px; }
.seq-light { width: 44px; height: 44px; border-radius: 12px; border: 3px solid; opacity: 0.4; transition: opacity .15s; }
.seq-light.lit { opacity: 1; box-shadow: 0 0 14px currentColor; }
.seq-btns { display: none; gap: 10px; }
.seq-btn { width: 68px; height: 68px; border-radius: 16px; border: 3px solid; cursor: pointer; transition: opacity .1s, transform .1s, filter .1s; position:relative; font-size: calc(22px * var(--ts, 1)); }
.seq-btn:active { opacity: .6; transform: scale(.9); }
.seq-btn::after { content: attr(data-symbol); position:absolute; right:6px; bottom:2px; font-size: calc(15px * var(--ts, 1)); color:rgba(0,0,0,.65); }

.cd-timer { font-size: calc(48px * var(--ts, 1)); color: var(--gold); text-align: center; display: none; }
.cd-tap-zone { width: 90%; max-width: 300px; padding: 28px; border-radius: 20px; border: 4px solid rgba(255,255,255,.3); background: rgba(255,255,255,.08); color: #fff; font-size: calc(24px * var(--ts, 1)); text-align: center; cursor: pointer; display: none; letter-spacing: 2px; transition: background .1s; }
.cd-tap-zone.stopped { border-color: #4ade80; background: rgba(74,222,128,.15); color: #4ade80; }
.cd-result { font-size: calc(18px * var(--ts, 1)); color: rgba(255,255,255,.8); font-family: 'Nunito'; margin-top: 8px; display: none; }

.shape-target-label { font-size: calc(22px * var(--ts, 1)); color: var(--gold); letter-spacing: 2px; margin-bottom: 8px; display: none; }
.shape-grid { display: none; grid-template-columns: repeat(3, 1fr); gap: 10px; width: 90%; max-width: 300px; }
.shape-cell { background: rgba(255,255,255,.08); border: 2px solid rgba(255,255,255,.15); border-radius: 12px; padding: 10px; display: flex; align-items: center; justify-content: center; cursor: pointer; aspect-ratio: 1; transition: background .15s, transform .1s, filter .1s; }
.shape-cell:active { background: rgba(255,255,255,.2); }
.shape-cell.correct-shape { background: rgba(74,222,128,.2); border-color: #4ade80; }
.shape-cell.wrong-shape { background: rgba(239,68,68,.2); border-color: #ef4444; }
.shape-cell.tap-pop { transform: scale(.9); filter: brightness(1.45); }
.shape-cell.wrong-shape, .cr-tap-zone.cr-wrong, .ooo-btn.ooo-wrong { animation: badTap .24s ease 2; }
@keyframes badTap {
    0%,100% { transform: translateX(0); }
    25% { transform: translateX(-5px); }
    75% { transform: translateX(5px); }
}
.sh-circle { width: 36px; height: 36px; border-radius: 50%; border: 4px solid; }
.sh-square { width: 36px; height: 36px; border: 4px solid; border-radius: 4px; }
.sh-triangle { width: 0; height: 0; border-left: 20px solid transparent; border-right: 20px solid transparent; border-bottom: 34px solid; }
.sh-diamond { width: 30px; height: 30px; border: 4px solid; transform: rotate(45deg); border-radius: 2px; }
.sh-pentagon { width: 36px; height: 36px; clip-path: polygon(50% 0%, 100% 38%, 82% 100%, 18% 100%, 0% 38%); border: 0; }
.sh-star { width: 36px; height: 36px; clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); border: 0; }
.sh-hexagon { width: 36px; height: 36px; clip-path: polygon(25% 0%, 75% 0%, 100% 50%, 75% 100%, 25% 100%, 0% 50%); border: 0; }
.sh-cross { width: 36px; height: 36px; clip-path: polygon(35% 0%, 65% 0%, 65% 35%, 100% 35%, 100% 65%, 65% 65%, 65% 100%, 35% 100%, 35% 65%, 0% 65%, 0% 35%, 35% 35%); border: 0; }

.ooo-label { font-size: calc(18px * var(--ts, 1)); color: rgba(255,255,255,.7); font-family: 'Nunito'; margin-bottom: 10px; display: none; }
.ooo-grid { display: none; grid-template-columns: 1fr 1fr; gap: 10px; width: 90%; max-width: 300px; }
.ooo-btn { background: rgba(255,255,255,.12); border: 2px solid rgba(255,255,255,.25); color: #fff; font-size: calc(18px * var(--ts, 1)); font-family: 'Nunito'; font-weight: 700; padding: 14px 10px; border-radius: 14px; cursor: pointer; transition: background .15s; }
.ooo-btn.ooo-correct { background: rgba(74,222,128,.3); border-color: #4ade80; }
.ooo-btn.ooo-wrong { background: rgba(239,68,68,.3); border-color: #ef4444; }

.hl-secret { font-size: calc(32px * var(--ts, 1)); font-family: 'Bebas Neue'; color: #fff; letter-spacing: 2px; display: none; text-align: center; margin-bottom: 4px; }
.hl-feedback { font-size: calc(22px * var(--ts, 1)); font-family: 'Bebas Neue'; letter-spacing: 1px; display: none; min-height: 30px; text-align: center; }
.hl-guesses-left { font-size: calc(14px * var(--ts, 1)); color: rgba(255,255,255,.6); font-family: 'Nunito'; display: none; margin-bottom: 8px; text-align: center; }
.hl-input-row { display: none; flex-direction: column; align-items: center; gap: 8px; width: 90%; max-width: 300px; }
.hl-guess-display { font-size: calc(42px * var(--ts, 1)); font-family: 'Bebas Neue'; color: var(--gold); min-width: 80px; text-align: center; }
.hl-num-btn { width: 44px; height: 44px; background: rgba(255,255,255,.12); border: 2px solid rgba(255,255,255,.3); color: #fff; font-size: calc(20px * var(--ts, 1)); font-family: 'Bebas Neue'; border-radius: 10px; cursor: pointer; transition: background .1s; }
.hl-num-btn:active { background: rgba(245,200,66,.3); }
.hl-submit-btn { background: linear-gradient(135deg, #3b82f6, #60a5fa); color: #fff; border: none; padding: 10px 24px; border-radius: 12px; font-size: calc(18px * var(--ts, 1)); font-family: 'Bebas Neue'; letter-spacing: 1px; cursor: pointer; width: 100%; }

@keyframes countPop {
    0%   { transform: scale(0.2); opacity: 0; }
    50%  { transform: scale(1.4); opacity: 1; }
    75%  { transform: scale(0.95); }
    100% { transform: scale(1); opacity: 1; }
}
#mg-countdown { position: absolute; font-size: calc(160px * var(--ts, 1)); color: var(--gold); text-shadow: 0 0 60px rgba(245,200,66,1), 0 0 20px rgba(245,200,66,.8); pointer-events: none; z-index: 10; display: none; }

.btn-close-map { background: rgba(255,255,255,.16); color: #fff; border: none; padding: 10px 24px; border-radius: 10px; font-weight: bold; cursor: pointer; width: 100%; font-family: 'Bebas Neue'; font-size: calc(18px * var(--ts, 1)); letter-spacing: 1px; }

/* ============================================================
   NEW MINIGAME STYLES
   ============================================================ */

/* --- Pulse Hold --- */
.pulse-zone { width: 90%; max-width: 200px; height: 180px; display: none; align-items: center; justify-content: center; cursor: pointer; border-radius: 50%; }
.pulse-circle { width: 110px; height: 110px; border-radius: 50%; border: 5px solid #fff; background: rgba(255,255,255,0.08); transition: border-color 0.06s; pointer-events: none; }
.pulse-score { font-size: calc(20px * var(--ts, 1)); font-family: 'Bebas Neue'; letter-spacing: 1px; display: none; text-align: center; min-height: 26px; }

/* --- Grid Recall --- */
.grid-recall { display: none; grid-template-columns: repeat(4, 1fr); gap: 4px; width: 88%; max-width: 240px; }
.grid-recall-score { font-size: calc(15px * var(--ts, 1)); color: rgba(255,255,255,.7); display: none; }
.grid-cell { aspect-ratio: 1; background: rgba(255,255,255,0.07); border: 2px solid rgba(255,255,255,0.18); border-radius: 6px; cursor: pointer; transition: background .12s, border-color .12s; }
.grid-cell.highlighted { background: rgba(245,200,66,0.55); border-color: var(--gold); }
.grid-cell.player-selected { background: rgba(59,130,246,0.45); border-color: #3b82f6; }

/* --- Word Scramble --- */
.ws-scramble { font-size: calc(34px * var(--ts, 1)); letter-spacing: 6px; color: var(--gold); display: none; text-align: center; margin-bottom: 6px; }
.ws-options { display: none; grid-template-columns: 1fr 1fr; gap: 8px; width: 90%; max-width: 280px; }
.ws-option-btn { background: rgba(255,255,255,0.1); border: 2px solid rgba(255,255,255,0.25); color: #fff; font-size: calc(15px * var(--ts, 1)); font-family: 'Nunito'; font-weight: 700; padding: 12px 6px; border-radius: 12px; cursor: pointer; transition: background .12s; }
.ws-option-btn:active { background: rgba(245,200,66,0.25); border-color: var(--gold); }
.ws-score { font-size: calc(15px * var(--ts, 1)); color: rgba(255,255,255,.7); display: none; }

/* --- Color Rush --- */
.cr-word { font-size: calc(46px * var(--ts, 1)); letter-spacing: 3px; display: none; text-align: center; text-shadow: 0 2px 12px rgba(0,0,0,0.5); }
.cr-tap-zone { width: 88%; max-width: 260px; height: 70px; display: none; align-items: center; justify-content: center; border: 3px solid rgba(255,255,255,0.25); border-radius: 18px; font-size: calc(22px * var(--ts, 1)); font-family: 'Bebas Neue'; color: #fff; cursor: pointer; transition: background .1s, border-color .1s; letter-spacing: 2px; }
.cr-tap-zone.cr-correct { background: rgba(74,222,128,0.35); border-color: #4ade80; }
.cr-tap-zone.cr-wrong   { background: rgba(239,68,68,0.35);  border-color: #ef4444; }
.cr-score { font-size: calc(36px * var(--ts, 1)); color: var(--gold); display: none; text-align: center; }

/* --- Rhythm Tap --- */
.rhythm-zone { width: 88%; max-width: 260px; height: 110px; display: none; align-items: center; justify-content: center; font-size: calc(20px * var(--ts, 1)); letter-spacing: 2px; border: 3px solid rgba(255,255,255,0.2); border-radius: 20px; cursor: pointer; transition: background .08s, border-color .08s; }
.rhythm-zone.rhythm-tapped { background: rgba(74,222,128,0.3); border-color: #4ade80; }
.rhythm-zone.rhythm-flash  { background: rgba(245,200,66,0.35); border-color: var(--gold); }
.rhythm-score { font-size: calc(18px * var(--ts, 1)); display: none; color: rgba(255,255,255,.75); }

/* --- Speed Sort --- */
.ss-grid { display: none; grid-template-columns: repeat(3, 1fr); gap: 7px; width: 88%; max-width: 240px; }
.ss-num-btn { aspect-ratio: 1; background: rgba(255,255,255,0.12); border: 2px solid rgba(255,255,255,0.28); color: #fff; font-size: calc(26px * var(--ts, 1)); font-family: 'Bebas Neue'; border-radius: 12px; cursor: pointer; transition: background .1s; }
.ss-num-btn.ss-done  { background: rgba(74,222,128,0.3); border-color: #4ade80; color: #4ade80; }
.ss-num-btn.ss-wrong { background: rgba(239,68,68,0.35); border-color: #ef4444; }
.ss-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

/* --- Flick Shot --- */
.fs-arena { width: 88%; max-width: 240px; height: 170px; display: none; position: relative; border: 2px solid rgba(255,255,255,0.2); border-radius: 14px; overflow: hidden; background: rgba(0,0,0,0.2); cursor: crosshair; }
.fs-target { position: absolute; width: 36px; height: 36px; border-radius: 50%; border: 3px solid #ef4444; transform: translate(-50%,-50%); pointer-events: none; }
.fs-target::after { content: ''; position: absolute; inset: 8px; border-radius: 50%; background: rgba(239,68,68,0.5); }
.fs-shot { position: absolute; width: 10px; height: 10px; border-radius: 50%; background: var(--gold); border: 2px solid #fff; transform: translate(-50%,-50%); pointer-events: none; }
.fs-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

/* --- Code Breaker --- */
.cb-history { width: 90%; max-width: 280px; max-height: 120px; overflow-y: auto; display: none; flex-direction: column; gap: 3px; font-family: 'Nunito'; font-size: calc(14px * var(--ts, 1)); color: #fff; }
.cb-hint { color: rgba(255,255,255,0.5); font-size: calc(12px * var(--ts, 1)); }
.cb-guess-row { font-family: 'Bebas Neue'; letter-spacing: 3px; font-size: calc(18px * var(--ts, 1)); color: #fff; }
.cb-current-input { font-size: calc(28px * var(--ts, 1)); letter-spacing: 8px; color: var(--gold); font-family: 'Bebas Neue'; text-align: center; }
.cb-solved { color: #4ade80; font-weight: 700; font-size: calc(14px * var(--ts, 1)); }
.cb-failed { color: #ef4444; font-size: calc(13px * var(--ts, 1)); }
.cb-btns { width: 90%; max-width: 280px; display: none; flex-wrap: wrap; gap: 6px; justify-content: center; }
.cb-digit-btn { width: 44px; height: 44px; background: rgba(255,255,255,0.12); border: 2px solid rgba(255,255,255,0.3); color: #fff; font-size: calc(20px * var(--ts, 1)); font-family: 'Bebas Neue'; border-radius: 10px; cursor: pointer; transition: background .1s; }
.cb-digit-btn:active { background: rgba(245,200,66,0.3); }
.cb-del { background: rgba(239,68,68,0.2); border-color: #ef4444; }
.cb-sub { background: rgba(74,222,128,0.2); border-color: #4ade80; }
.cb-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

/* --- Sound Match --- */
.sm-options { display: none; grid-template-columns: 1fr 1fr; gap: 8px; width: 90%; max-width: 280px; }
.sm-opt-btn { background: rgba(255,255,255,0.1); border: 2px solid rgba(255,255,255,0.25); color: #fff; padding: 10px 6px; border-radius: 12px; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 4px; transition: background .12s; }
.sm-opt-btn:active { background: rgba(245,200,66,0.25); border-color: var(--gold); }
.sm-note { font-family: 'Bebas Neue'; font-size: calc(18px * var(--ts, 1)); letter-spacing: 1px; width: 22px; height: 22px; border-radius: 4px; display: inline-flex; align-items: center; justify-content: center; }
.sm-note-h { background: rgba(251,191,36,0.35); color: #fbbf24; }
.sm-note-l { background: rgba(99,102,241,0.35); color: #818cf8; }
.sm-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

/* --- Minigame Arcade Selector --- */
#mg-select-overlay { position: fixed; inset: 0; background: #0d0d1a; z-index: 1100; display: flex; flex-direction: column; overflow: hidden; }
.mg-sel-header { display: flex; align-items: center; justify-content: space-between; padding: 16px 16px 10px; flex-shrink: 0; border-bottom: 1px solid rgba(255,255,255,0.08); }
.mg-sel-title { font-family: 'Bebas Neue'; font-size: calc(24px * var(--ts, 1)); color: #fff; letter-spacing: 2px; text-shadow: 0 0 20px rgba(245,200,66,.5); }
.mg-sel-back { background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.18); color: #fff; padding: 8px 14px; border-radius: 20px; cursor: pointer; font-size: calc(14px * var(--ts, 1)); letter-spacing: 1px; }
.mg-sel-back:active { background: rgba(255,255,255,0.18); }
.mg-sel-grid { flex: 1; overflow-y: auto; padding: 10px; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; -webkit-overflow-scrolling: touch; overscroll-behavior: contain; }
.mg-sel-card { background: rgba(255,255,255,0.05); border: 2px solid rgba(255,255,255,0.1); border-radius: 14px; padding: 14px 10px 10px; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 5px; transition: border-color .15s, background .15s, box-shadow .15s; touch-action: manipulation; user-select: none; -webkit-user-select: none; }
.mg-sel-card:active { background: rgba(255,255,255,0.1); }
.mg-sel-card.sel { border-color: #f5c842; background: rgba(245,200,66,0.1); box-shadow: 0 0 14px rgba(245,200,66,0.3); }
.mg-sel-icon { font-size: calc(34px * var(--ts, 1)); line-height: 1; }
.mg-sel-name { font-family: 'Bebas Neue'; font-size: calc(13px * var(--ts, 1)); letter-spacing: 1px; color: #fff; text-align: center; line-height: 1.2; }
.mg-sel-desc { font-family: 'Nunito'; font-size: calc(11px * var(--ts, 1)); color: rgba(255,255,255,0.45); text-align: center; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.mg-sel-footer { padding: 12px 16px 16px; flex-shrink: 0; border-top: 1px solid rgba(255,255,255,0.08); background: rgba(13,13,26,0.97); }
.mg-sel-play { width: 100%; padding: 16px; background: linear-gradient(135deg,#f59e0b,#fbbf24); color: #000; border: none; border-radius: 14px; font-size: calc(24px * var(--ts, 1)); letter-spacing: 2px; cursor: pointer; transition: opacity .15s, transform .1s; box-shadow: 0 6px 24px rgba(245,200,66,.4); }
.mg-sel-play:disabled { opacity: 0.3; cursor: not-allowed; box-shadow: none; }
.mg-sel-play:not(:disabled):active { opacity: 0.85; transform: scale(0.97); }

/* --- Tether Tap --- */
.tt-zone { display: none; position: relative; width: 90%; max-width: 320px; height: 44px; background: rgba(255,255,255,0.06); border: 2px solid rgba(255,255,255,0.18); border-radius: 22px; overflow: hidden; cursor: pointer; touch-action: none; }
.tt-ring { position: absolute; top: 50%; left: 5%; width: 32px; height: 32px; border-radius: 50%; border: 3px solid #fbbf24; transform: translateY(-50%); transition: border-color .1s; }
.tt-ring.tt-held { border-color: #4ade80; background: rgba(74,222,128,0.15); }
.tt-ring.tt-missed { border-color: #ef4444; background: rgba(239,68,68,0.15); }
.tt-target { position: absolute; top: 0; bottom: 0; width: 3px; background: rgba(251,191,36,0.7); border-radius: 2px; transform: translateX(-50%); }
.tt-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

/* --- Mirror Match --- */
.mm-source { display: none; grid-template-columns: repeat(3,1fr); gap: 5px; width: 110px; }
.mm-options { display: none; flex-direction: row; gap: 8px; flex-wrap: wrap; justify-content: center; width: 90%; }
.mm-opt-grid { display: grid; grid-template-columns: repeat(3,1fr); gap: 3px; padding: 6px; background: rgba(255,255,255,0.07); border: 2px solid rgba(255,255,255,0.2); border-radius: 10px; cursor: pointer; touch-action: none; }
.mm-opt-grid:active { border-color: var(--gold); background: rgba(245,200,66,0.12); }
.mm-cell { width: 26px; height: 26px; border-radius: 4px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.12); }
.mm-cell.mm-lit { background: #fbbf24; border-color: #f59e0b; }
.mm-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

/* --- Blind Build --- */
.bb-canvas { display: none; width: 90%; max-width: 260px; height: 160px; border-radius: 12px; border: 2px solid rgba(255,255,255,0.15); touch-action: none; cursor: crosshair; }
.bb-prompt { display: none; font-size: calc(13px * var(--ts, 1)); color: rgba(255,255,255,.7); letter-spacing: 1px; text-align: center; }
.bb-score { font-size: calc(15px * var(--ts, 1)); display: none; color: rgba(255,255,255,.7); }

.trace-container { touch-action:none; }
.trace-node { min-width: 46px; min-height: 46px; display:flex; align-items:center; justify-content:center; touch-action:none; }
.trace-node::before { content:''; position:absolute; inset:-12px; border-radius:50%; }

/* --- ColorMatch ink hint --- */
.cm-ink-hint { display: block; font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1)); font-weight: 800; letter-spacing: 1px; margin-top: 4px; }

/* --- Sequence Repeat extras --- */
.seq-double { outline: 3px solid #fff; outline-offset: 3px; animation: seqDoublePulse .35s ease infinite alternate; }
@keyframes seqDoublePulse { from { outline-color: #fff; } to { outline-color: #fbbf24; } }
.seq-pressed { filter: brightness(1.6) !important; transform: scale(0.9); }

/* --- Shape Tap icon --- */
.shape-icon { font-size: calc(30px * var(--ts, 1)); line-height: 1; display: block; pointer-events: none; }
.shape-cell { display: flex; align-items: center; justify-content: center; min-height: 62px; border-radius: 10px; background: rgba(255,255,255,0.07); border: 2px solid rgba(255,255,255,0.12); cursor: pointer; transition: background .12s, transform .1s, filter .1s; touch-action: none; }
.shape-cell:active { background: rgba(255,255,255,0.18); }
.shape-cell.correct-shape { background: rgba(74,222,128,0.25); border-color: #4ade80; }
.shape-cell.wrong-shape   { background: rgba(239,68,68,0.25);  border-color: #ef4444; }

/* --- Grid Recall cell feedback --- */
.grid-cell { border-radius: 6px; background: rgba(255,255,255,0.07); border: 2px solid rgba(255,255,255,0.1); min-height: 36px; cursor: pointer; transition: background .1s; }
.grid-cell.highlighted      { background: #fbbf24; border-color: #f59e0b; box-shadow: 0 0 8px rgba(251,191,36,0.5); }
.grid-cell.player-selected  { background: rgba(59,130,246,0.45); border-color: #3b82f6; }
.grid-cell.gr-correct       { background: rgba(74,222,128,0.45); border-color: #4ade80; }
.grid-cell.gr-wrong         { background: rgba(239,68,68,0.45);  border-color: #ef4444; }
.grid-cell.gr-missed        { background: rgba(251,191,36,0.2);  border-color: #fbbf24; border-style: dashed; }

/* iOS rubber-banding prevention */
body { overscroll-behavior: none; -webkit-overflow-scrolling: auto; }

/* Game layer interaction guards */
#ui-layer, #minigame-layer, #modal-overlay { touch-action: none; }
#minigame-layer * { user-select: none; -webkit-user-select: none; }

/* Small screen (phones < 380px wide) */
@media (max-width: 380px) {
    :root { --hud-h: 64px; }
    .hud-player { padding: 6px 8px; }
    .hud-name { font-size: calc(12px * var(--ts, 1)); }
    .hud-coins { font-size: calc(18px * var(--ts, 1)); }
    .action-btn { font-size: calc(13px * var(--ts, 1)); padding: 6px 10px; }
    .mg-intro-overlay, .minigame-layer { font-size: calc(14px * var(--ts, 1)); }
}

/* ============================================================
   CITY CIRCUIT — NEW ELEMENTS
   ============================================================ */

/* --- Char grid 3-column for 9 characters --- */
.char-grid-3col { grid-template-columns: repeat(3, 1fr) !important; }

/* --- Round Counter --- */
#round-counter {
    position: absolute; top: calc(var(--hud-h) + 6px); left: 50%; transform: translateX(-50%);
    z-index: 18; background: rgba(0,0,0,.7); border: 1px solid rgba(255,255,255,.2);
    color: var(--gold); font-size: calc(14px * var(--ts, 1)); letter-spacing: 2px; padding: 4px 14px;
    border-radius: 20px; pointer-events: none; white-space: nowrap;
    text-shadow: 0 0 10px rgba(245,200,66,.5);
}

/* --- Contracts Strip --- */
#contracts-strip {
    position: absolute; top: calc(var(--hud-h) + 36px); left: 50%; transform: translateX(-50%);
    z-index: 17; display: flex; gap: 6px; pointer-events: none;
    max-width: calc(100vw - 24px); overflow-x: auto; padding: 2px 0;
    scrollbar-width: none;
}
#contracts-strip::-webkit-scrollbar { display: none; }

/* --- WHERE THE STAR IS (Star Territory) ---------------------------------
   Rule 1 of the Star system is that the live Star's location is never hidden,
   so it is a permanent line rather than a toast. It sits directly under the
   round counter and pushes the bounty pills down, because "where is the Star"
   is read more often than "what are my bounties" on this board. */
#star-strip {
    position: absolute; top: calc(var(--hud-h) + 34px); left: 50%; transform: translateX(-50%);
    z-index: 18; pointer-events: none; white-space: nowrap;
    display: flex; align-items: center; gap: 8px;
    background: rgba(28,18,2,.86); border: 1px solid rgba(251,191,36,.55);
    border-radius: 20px; padding: 4px 12px;
    font-family: 'Nunito'; font-size: calc(12.5px * var(--ts, 1)); color: #fde68a;
    box-shadow: 0 0 14px rgba(251,191,36,.18);
    max-width: calc(100vw - 24px); overflow: hidden; text-overflow: ellipsis;
}
#star-strip b { color: #fbbf24; font-weight: 800; }
#star-strip .ss-dist { color: rgba(253,230,138,.65); }
body[data-star="1"] #contracts-strip { top: calc(var(--hud-h) + 68px); }
body[data-hud-mode="solo"][data-star="1"] #contracts-strip { top: calc(var(--rival-h) + 72px); }
body[data-hud-mode="solo"] #star-strip { top: calc(var(--rival-h) + 38px); }
.tabletop-p2-turn #star-strip { transform: translateX(-50%) rotate(180deg); }

/* The per-seat count on the HUD bar. Stars are the score on this board, so they
   are drawn at coin size and in the Star's own gold; the Shards beside them are
   deliberately smaller, because they are the secondary lane. */
.hud-stars { font-size: calc(20px * var(--ts, 1)); color: var(--gold); display: flex; align-items: center; gap: 5px;
             text-shadow: 0 0 10px rgba(251,191,36,.7); }
.hud-stars .hs-shards { font-size: calc(12px * var(--ts, 1)); color: #c084fc; text-shadow: none; opacity: .9; }
.contract-pill {
    display: flex; align-items: center; gap: 5px;
    background: rgba(0,0,0,.8); border: 1px solid rgba(255,255,255,.2);
    border-radius: 20px; padding: 4px 10px; white-space: nowrap;
    font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.85);
    flex-shrink: 0;
}
.contract-pill.done { opacity: 0.4; text-decoration: line-through; }
.contract-icon { font-size: calc(14px * var(--ts, 1)); }
.contract-text { color: rgba(255,255,255,.7); max-width: 140px; overflow: hidden; text-overflow: ellipsis; }
.contract-reward { color: #4ade80; font-weight: 800; font-size: calc(12px * var(--ts, 1)); }

/* --- Ally HUD Slots --- */
.hud-ally-slots { display: flex; gap: 3px; }
.ally-slot-badge {
    position: relative; width: 22px; height: 22px; background: rgba(74,222,128,.2);
    border: 1px solid #4ade80; border-radius: 6px; display: flex; align-items: center;
    justify-content: center; font-size: calc(12px * var(--ts, 1)); cursor: default;
}
.ally-turns {
    position: absolute; bottom: -4px; right: -4px; background: #4ade80; color: #000;
    font-size: calc(12px * var(--ts, 1)); font-weight: 900; width: 12px; height: 12px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center; font-family: 'Nunito';
}
.ally-slot-empty {
    width: 22px; height: 22px; background: rgba(255,255,255,.06);
    border: 1px dashed rgba(255,255,255,.18); border-radius: 6px;
}

/* --- Cabbie button in action row --- */
.cabbie-btn { background: linear-gradient(135deg, #78350f, #f59e0b) !important; color: #000 !important; border: none !important; font-size: calc(18px * var(--ts, 1)) !important; }

/* --- Branch Choice Overlay --- */
#branch-choice-overlay {
    position: fixed; inset: 0; z-index: 190;
    background: rgba(0,0,0,.92);
    display: none; flex-direction: column; align-items: center; justify-content: center;
    gap: 16px; padding: 24px;
}
.branch-header { text-align: center; }
.branch-title { font-size: calc(36px * var(--ts, 1)); color: var(--gold); letter-spacing: 3px; text-shadow: 0 0 20px rgba(245,200,66,.7); }
.branch-subtitle { font-family: 'Nunito'; font-size: calc(14px * var(--ts, 1)); color: rgba(255,255,255,.5); letter-spacing: 1px; text-transform: uppercase; margin-top: 4px; }
.branch-cards { display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 440px; }
.branch-card {
    display: flex; flex-direction: row; align-items: center; gap: 16px;
    width: 100%; padding: 16px 18px; cursor: pointer; color: #fff;
    background: rgba(255,255,255,.09);
    border: 2px solid rgba(255,255,255,.15);
    border-left: 5px solid var(--bc, #94a3b8);
    border-radius: 16px;
    transition: background .15s, transform .1s;
    text-align: left;
}
.branch-card:active { transform: scale(.97); background: rgba(255,255,255,.18); }
/* district accent colors */
.branch-ring  { --bc: #94a3b8; }
.branch-fin   { --bc: #3b82f6; }
.branch-ba    { --bc: #ef4444; }
.branch-shop  { --bc: #ec4899; }
.branch-ind   { --bc: #eab308; }
.bc-icon    { font-size: calc(44px * var(--ts, 1)); flex-shrink: 0; line-height: 1; }
.bc-body    { display: flex; flex-direction: column; gap: 5px; flex: 1; }
.bc-name    { font-size: calc(21px * var(--ts, 1)); letter-spacing: 1px; color: var(--bc, #fff); line-height: 1.1; }
.bc-details { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bc-spaces  { font-family: 'Nunito'; font-size: calc(13px * var(--ts, 1)); font-weight: 700; color: rgba(255,255,255,.9); background: rgba(255,255,255,.14); padding: 2px 9px; border-radius: 8px; white-space: nowrap; }
.bc-desc    { font-family: 'Nunito'; font-size: calc(13px * var(--ts, 1)); color: rgba(255,255,255,.62); }
.bc-chev    { font-size: calc(28px * var(--ts, 1)); color: var(--bc, #fff); opacity: .65; flex-shrink: 0; line-height: 1; }

/* --- Duel Modal --- */
.duel-bet-options { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; margin-bottom: 8px; }
.duel-bet-btn {
    width: 64px; height: 64px; border-radius: 16px; border: 2px solid rgba(255,107,53,.5);
    background: rgba(255,107,53,.15); color: #fff; font-size: calc(24px * var(--ts, 1)); cursor: pointer;
    transition: background .15s, transform .1s, border-color .15s;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px;
}
.duel-bet-btn:not(:disabled):active { background: rgba(255,107,53,.4); border-color: #ff6b35; transform: scale(.92); }
.duel-bet-btn:disabled { opacity: .35; cursor: not-allowed; }
/* The Shard stake is not a bigger coin bet, so it does not look like one. */
.duel-bet-shard { border-color: rgba(192,132,252,.7) !important; color: #e9d5ff; }
.duel-bet-shard:not(:disabled):active { background: rgba(192,132,252,.35) !important; border-color: #c084fc !important; }

/* --- Ally Steal List --- */
.ally-steal-list { display: flex; flex-direction: column; gap: 8px; width: 100%; margin-bottom: 4px; }
.ally-steal-btn {
    display: flex; align-items: center; gap: 12px;
    background: rgba(255,255,255,.08); border: 2px solid rgba(255,255,255,.25);
    color: #fff; border-radius: 14px; padding: 12px 14px; cursor: pointer;
    transition: background .15s, border-color .15s; text-align: left; width: 100%;
}
.ally-steal-btn:active { background: rgba(249,115,22,.2); border-color: #f97316; }
.ally-steal-icon { font-size: calc(28px * var(--ts, 1)); flex-shrink: 0; }
.ally-steal-info { display: flex; flex-direction: column; }
.ally-steal-info b { font-family: 'Bebas Neue'; font-size: calc(18px * var(--ts, 1)); letter-spacing: 1px; color: #fff; }
.ally-steal-info small { font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.55); line-height: 1.4; }

/* --- Shop discount badge --- */
.shop-discount-banner {
    background: rgba(74,222,128,.15); border: 1px solid rgba(74,222,128,.4);
    color: #4ade80; border-radius: 10px; padding: 6px 12px; font-size: calc(13px * var(--ts, 1));
    font-family: 'Nunito'; font-weight: 800; margin-bottom: 8px; text-align: center;
}
.shop-discount { color: #4ade80; font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1)); font-weight: 800; }

/* --- HQ tile glow animation --- */
@keyframes hqGlow {
    0%, 100% { box-shadow: 0 0 12px rgba(251,191,36,.4); }
    50%       { box-shadow: 0 0 28px rgba(251,191,36,.9), 0 0 50px rgba(251,191,36,.4); }
}

/* --- District color CSS variables --- */
:root {
    --dist-ring:  #cbd5e1;
    --dist-fin:   #60a5fa;
    --dist-ba:    #f87171;
    --dist-shop:  #f472b6;
    --dist-ind:   #fbbf24;
}

/* ============================================================
   MAP SELECT SCREEN
   ============================================================ */

#map-select {
    position: fixed; inset: 0; z-index: 1000;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: linear-gradient(to bottom, #1e0c36, #4a093f);
    padding: 20px; overflow-y: auto; touch-action: pan-y;
}

.map-select-content {
    display: flex; flex-direction: column; align-items: center;
    width: 100%; max-width: 480px; gap: 14px;
}

.map-select-layout {
    display: flex; flex-direction: column; gap: 12px; width: 100%;
}

/* Grid of map cards (stacks for <2 maps, becomes 2-col when more added) */
.map-select-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 10px; width: 100%;
}

.map-card {
    background: rgba(0,0,0,0.4);
    border: 2px solid rgba(255,255,255,0.25);
    border-radius: 18px; padding: 16px 12px;
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    cursor: pointer; text-align: center;
    transition: border-color .2s, background .2s, transform .1s, box-shadow .2s;
}
.map-card:active { transform: scale(0.97); }
.map-card.sel {
    border-color: var(--map-color, #60a5fa);
    background: rgba(96,165,250,0.12);
    box-shadow: 0 0 24px rgba(96,165,250,0.35);
}
/* Override tint per-card using the CSS variable set inline */
.map-card.sel { box-shadow: 0 0 24px color-mix(in srgb, var(--map-color, #60a5fa) 60%, transparent); }

.map-card-locked { opacity: 0.45; cursor: default; }
.map-card-icon   { font-size: calc(44px * var(--ts, 1)); line-height: 1; }
.map-card-name   { font-size: calc(18px * var(--ts, 1)); letter-spacing: 1px; color: #fff; line-height: 1.1; }
.map-card-desc   { font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.55); line-height: 1.35; }
.map-card-soon   { background: rgba(245,158,11,.2); border: 1px solid rgba(245,158,11,.5); color: #fbbf24;
                   font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1)); font-weight: 800; padding: 2px 8px;
                   border-radius: 10px; letter-spacing: 1px; }

/* Preview panel shown below cards */
.map-preview-panel {
    background: rgba(0,0,0,0.4); border: 1px solid rgba(255,255,255,0.15);
    border-radius: 16px; padding: 18px 20px; width: 100%;
    display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center;
    min-height: 120px;
}
.map-preview-icon  { font-size: calc(36px * var(--ts, 1)); line-height: 1; }
.map-preview-name  { font-size: calc(26px * var(--ts, 1)); color: var(--gold); letter-spacing: 2px; }
.map-preview-desc  { font-family: 'Nunito'; font-size: calc(13px * var(--ts, 1)); color: rgba(255,255,255,.7);
                     line-height: 1.5; max-width: 380px; }
.map-preview-tags  { display: flex; gap: 6px; flex-wrap: wrap; justify-content: center; margin-top: 4px; }
.map-tag           { background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2);
                     color: rgba(255,255,255,.75); font-family: 'Nunito'; font-size: calc(12px * var(--ts, 1));
                     font-weight: 700; padding: 3px 10px; border-radius: 20px; }

/* ============================================================
   VISUAL POLISH — ALL PHASES
   ============================================================ */

/* --- Enhanced design tokens --- */
:root {
    --glass:          rgba(255,255,255,0.06);
    --glass-border:   rgba(255,255,255,0.13);
    --glow-gold:      0 0 24px rgba(245,200,66,0.55), 0 0 50px rgba(245,200,66,0.2);
    --shadow-deep:    0 24px 64px rgba(0,0,0,0.75);
    --transition-sm:  all 0.18s cubic-bezier(0.4, 0, 0.2, 1);
    --transition-md:  all 0.28s cubic-bezier(0.4, 0, 0.2, 1);
}

/* --- Logo shimmer animation --- */
@keyframes titleShimmer {
    0%,100% { text-shadow: 0 0 24px rgba(245,200,66,0.55), 0 0 48px rgba(245,200,66,0.12); }
    50%     { text-shadow: 0 0 48px rgba(245,200,66,1.0),  0 0 96px rgba(245,200,66,0.45), 0 0 140px rgba(245,200,66,0.18); }
}
.stitle {
    animation: titleShimmer 3.2s ease-in-out infinite;
    letter-spacing: 3px;
}

/* --- Character card unique gradient backgrounds --- */
.char-card { transition: var(--transition-sm); }
.char-card[data-char="slime"]     { background: linear-gradient(145deg, #0c1e40, #0e4a80); border-color: rgba(59,130,246,0.5); }
.char-card[data-char="ghost"]     { background: linear-gradient(145deg, #1a0a2e, #3a1555); border-color: rgba(168,85,247,0.5); }
.char-card[data-char="boxy"]      { background: linear-gradient(145deg, #062232, #0d4a5f); border-color: rgba(6,182,212,0.5); }
.char-card[data-char="bunny"]     { background: linear-gradient(145deg, #2e0a1e, #5f1540); border-color: rgba(244,114,182,0.5); }
.char-card[data-char="cabbie"]    { background: linear-gradient(145deg, #2a1e00, #4a3800); border-color: rgba(251,191,36,0.5); }
.char-card[data-char="vendor"]    { background: linear-gradient(145deg, #2e1100, #5c2500); border-color: rgba(249,115,22,0.5); }
.char-card[data-char="banker"]    { background: linear-gradient(145deg, #0a1428, #162445); border-color: rgba(96,165,250,0.5); }
.char-card[data-char="bodyguard"] { background: linear-gradient(145deg, #0a1612, #152b20); border-color: rgba(74,222,128,0.5); }
.char-card[data-char="investor"]  { background: linear-gradient(145deg, #052216, #0a4028); border-color: rgba(34,197,94,0.5); }

.char-card:active:not(.sel) { transform: scale(0.97); }
.char-card.sel { transform: scale(1.04); }
.char-icon {
    font-size: calc(42px * var(--ts, 1));
    filter: drop-shadow(0 4px 8px rgba(0,0,0,0.6));
    transition: transform 0.2s ease;
    display: block;
    margin-bottom: 6px;
}
.char-card.sel .char-icon { transform: scale(1.12); }

/* --- HUD glassmorphism --- */
.hud-bar {
    background: rgba(10, 6, 22, 0.9) !important;
    backdrop-filter: blur(16px) saturate(1.5);
    -webkit-backdrop-filter: blur(16px) saturate(1.5);
    transition: box-shadow 0.35s ease;
}
#hud-p1.active-turn {
    box-shadow: 0 -5px 32px rgba(255,59,59,0.38), 0 -1px 0 rgba(255,59,59,0.7), inset 0 1px 0 rgba(255,59,59,0.12) !important;
    border-top-color: rgba(255,59,59,0.9) !important;
}
#hud-p2.active-turn {
    box-shadow: 0 5px 32px rgba(59,142,255,0.38), 0 1px 0 rgba(59,142,255,0.7), inset 0 -1px 0 rgba(59,142,255,0.12) !important;
    border-bottom-color: rgba(59,142,255,0.9) !important;
}

/* --- Coin display polish --- */
.hud-coins {
    filter: drop-shadow(0 0 8px rgba(74,222,128,0.55));
    letter-spacing: 1px;
}

/* --- Action buttons upgrade --- */
.btn-action {
    background: rgba(10,7,22,0.75) !important;
    border: 1.5px solid rgba(255,255,255,0.28) !important;
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    letter-spacing: 0.5px;
    transition: var(--transition-sm) !important;
    box-shadow: 0 4px 14px rgba(0,0,0,0.45), inset 0 1px 0 rgba(255,255,255,0.07) !important;
}
.btn-action:active { transform: scale(0.93) !important; }
.btn-action.roll-btn {
    background: linear-gradient(150deg, #b37c0c, #f5c842, #ffe066) !important;
    border: none !important;
    color: #1a0a00 !important;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
    box-shadow: 0 6px 26px rgba(245,200,66,0.58), 0 0 0 1px rgba(245,200,66,0.28), inset 0 1px 0 rgba(255,255,255,0.32) !important;
    letter-spacing: 1px;
}
/* The one you press every turn, so it stays the loudest square in the stack. */
.btn-action.roll-btn .ba-ic { font-size: calc(33px * var(--ts, 1)); }
.btn-action.roll-btn .ba-tx { font-size: calc(17px * var(--ts, 1)); }
/* The bag is the button players forget exists. A live count on it is the
   reminder — and it greys out honestly when there is nothing to use. */
.btn-action.items-btn { background: linear-gradient(145deg, #3b1d63, #6d3fae) !important; border: none !important; }
.btn-action.items-btn.empty { opacity: .5; }
.btn-action.roll-btn:active {
    box-shadow: 0 2px 12px rgba(245,200,66,0.45) !important;
}
.btn-action.map-btn {
    background: linear-gradient(145deg, #122060, #1e3fa0) !important;
    border: none !important;
    box-shadow: 0 4px 18px rgba(59,130,246,0.42), inset 0 1px 0 rgba(255,255,255,0.1) !important;
}

/* --- Modal backdrop polish --- */
#modal-overlay {
    background: radial-gradient(ellipse at 50% 38%, rgba(28,8,58,0.93), rgba(0,0,0,0.96)) !important;
    backdrop-filter: blur(5px);
    -webkit-backdrop-filter: blur(5px);
}
.modal-box {
    background: rgba(12, 8, 26, 0.97) !important;
    border: 1px solid rgba(255,255,255,0.13) !important;
    box-shadow: 0 32px 80px rgba(0,0,0,0.88), 0 0 0 1px rgba(255,255,255,0.04), inset 0 1px 0 rgba(255,255,255,0.07) !important;
}

/* --- Mode select button polish --- */
.sbtn {
    background: rgba(12,8,26,0.65) !important;
    border: 1.5px solid rgba(255,255,255,0.2) !important;
    backdrop-filter: blur(10px);
    transition: var(--transition-sm) !important;
    box-shadow: 0 4px 18px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.05) !important;
}
.sbtn:active { transform: scale(0.97); }
.sbtn.sel, .sbtn:hover {
    border-color: var(--gold) !important;
    background: rgba(245,200,66,0.13) !important;
    box-shadow: 0 6px 26px rgba(245,200,66,0.28), inset 0 1px 0 rgba(255,255,255,0.07) !important;
}

/* --- Primary button upgrade --- */
.btn-primary {
    background: linear-gradient(150deg, #c47c0a, #f5c842, #ffe066) !important;
    box-shadow: 0 8px 34px rgba(245,200,66,0.58), 0 0 0 1px rgba(245,200,66,0.22), inset 0 1px 0 rgba(255,255,255,0.3) !important;
    letter-spacing: 1.5px;
    transition: transform 0.12s ease, box-shadow 0.12s ease !important;
}
.btn-primary:active {
    transform: scale(0.95) !important;
    box-shadow: 0 3px 16px rgba(245,200,66,0.38) !important;
}

/* --- Toast upgrade --- */
.toast {
    background: rgba(8, 5, 18, 0.96) !important;
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    box-shadow: 0 10px 36px rgba(0,0,0,0.65), 0 0 24px rgba(0,0,0,0.3) !important;
    font-size: calc(15px * var(--ts, 1)) !important;
    padding: 7px 16px !important;
    max-width: 100%;
    white-space: normal !important;
    text-align: center; line-height: 1.25;
    letter-spacing: 0.5px;
    border-width: 1.5px !important;
}

/* --- Map panel glassmorphism --- */
.map-panel {
    background: rgba(8, 5, 20, 0.93) !important;
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    border: 1px solid rgba(255,255,255,0.11) !important;
    box-shadow: 0 22px 64px rgba(0,0,0,0.88), inset 0 1px 0 rgba(255,255,255,0.07) !important;
}

/* --- Round counter + contracts upgrade --- */
#round-counter {
    background: rgba(6,3,16,0.9) !important;
    backdrop-filter: blur(10px);
    border: 1px solid rgba(245,200,66,0.24) !important;
    box-shadow: 0 4px 18px rgba(0,0,0,0.55) !important;
}

/* --- Space info card upgrade --- */
#space-info-card {
    background: rgba(8,4,20,0.93) !important;
    backdrop-filter: blur(14px);
    border: 1px solid rgba(255,255,255,0.15) !important;
    box-shadow: 0 14px 44px rgba(0,0,0,0.75) !important;
}

/* --- Branch choice — backdrop blur --- */
.branch-card { backdrop-filter: blur(8px); }

/* --- Gate overlay upgrade --- */
.gate-title {
    filter: drop-shadow(0 0 24px rgba(245,158,11,0.95));
}

/* --- Win screen polish --- */
.win-crown {
    font-size: calc(92px * var(--ts, 1)) !important;
    filter: drop-shadow(0 0 32px rgba(245,200,66,0.95)) drop-shadow(0 8px 18px rgba(0,0,0,0.65));
    animation: crownBounce .65s ease backwards !important;
}
.win-name {
    font-size: calc(58px * var(--ts, 1)) !important;
    filter: drop-shadow(0 0 22px rgba(245,200,66,0.75));
}
.win-card {
    background: rgba(255,255,255,0.06) !important;
    border: 1px solid rgba(255,255,255,0.13) !important;
    backdrop-filter: blur(10px);
}
.win-card.winner-card {
    border-color: var(--gold) !important;
    background: rgba(245,200,66,0.11) !important;
    box-shadow: 0 0 34px rgba(245,200,66,0.22), inset 0 1px 0 rgba(255,255,255,0.1) !important;
}
.win-card-score {
    filter: drop-shadow(0 0 12px rgba(245,200,66,0.52));
}

/* --- Map card upgrade --- */
.map-card {
    background: rgba(12,8,26,0.62) !important;
    backdrop-filter: blur(10px);
    transition: var(--transition-sm) !important;
    box-shadow: 0 4px 18px rgba(0,0,0,0.42) !important;
}
.map-card:active:not(.map-card-locked) { transform: scale(0.97) !important; }

/* --- Coin particle animation --- */
@keyframes coinParticleFly {
    0%   { transform: translate(0,0) scale(1.25); opacity: 1; }
    100% { transform: translate(var(--tx,0px), var(--ty,-55px)) scale(0.35); opacity: 0; }
}
.coin-particle {
    position: fixed;
    pointer-events: none;
    z-index: 9999;
    font-size: calc(17px * var(--ts, 1));
    animation: coinParticleFly 0.72s cubic-bezier(0.22, 1, 0.36, 1) forwards;
    will-change: transform, opacity;
}

/* --- Splash title size boost on tall screens --- */
@media (min-height: 700px) {
    .stitle { font-size: calc(68px * var(--ts, 1)) !important; }
}

/* --- Inv slot polish --- */
.inv-slot {
    background: rgba(255,255,255,0.11) !important;
    border: 1px solid rgba(255,255,255,0.18) !important;
    box-shadow: inset 0 1px 0 rgba(255,255,255,0.08);
}

/* --- Ally slot badge polish --- */
.ally-slot-badge {
    box-shadow: 0 0 10px rgba(74,222,128,0.25);
}

/* --- Hud name glow fix --- */
.p1c { text-shadow: 0 0 14px rgba(255,59,59,0.7), 0 0 30px rgba(255,59,59,0.25) !important; }
.p2c { text-shadow: 0 0 14px rgba(59,142,255,0.7), 0 0 30px rgba(59,142,255,0.25) !important; }

/* ============================================================
   ONBOARDING, RULES, SETTINGS  (Phase 1)
   ============================================================ */

/* Splash secondary links + stats */
.splash-secondary { display: flex; gap: 10px; width: 100%; max-width: 300px; margin-top: 10px; }
.splash-link {
    flex: 1; background: transparent; border: 1px solid rgba(255,255,255,0.28); color: rgba(255,255,255,0.8);
    padding: 9px 6px; border-radius: 50px; font-size: calc(14px * var(--ts, 1)); letter-spacing: 1px; cursor: pointer; transition: all .2s;
}
.splash-link:active, .splash-link:hover { border-color: var(--gold); color: var(--gold); background: rgba(245,200,66,0.1); }
.splash-stats {
    margin-top: 12px; color: rgba(255,255,255,0.7); font-size: calc(13px * var(--ts, 1)); font-weight: 700;
    background: rgba(0,0,0,0.3); padding: 6px 16px; border-radius: 50px; border: 1px solid rgba(255,255,255,0.15);
}
.splash-stats b { color: var(--gold); }

/* In-game rules button */
#btn-rules {
    /* Above #swipe-zone (15): below it, the swipe zone swallowed every tap on
       your own turn and the rules could not be opened (RELEASE_AUDIT G-01).
       44 px is the minimum touch target on both stores' guidelines. */
    position: absolute; top: calc(var(--hud-h) + 10px); right: 10px; z-index: 16;
    width: 44px; height: 44px; border-radius: 50%; pointer-events: auto; cursor: pointer;
    background: rgba(18,14,38,0.92); border: 1px solid var(--border); color: rgba(255,255,255,0.85);
    font-size: calc(18px * var(--ts, 1)); box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}
#btn-rules:active { background: rgba(245,200,66,0.2); border-color: var(--gold); }
.tabletop-p2-turn #btn-rules { transform: rotate(180deg); top: auto; bottom: calc(var(--hud-h) + 10px); }

/* Full-screen overlays (shared) */
.fs-overlay {
    position: fixed; inset: 0; z-index: 1500;
    background: rgba(8, 4, 20, 0.92); backdrop-filter: blur(4px);
    display: flex; align-items: center; justify-content: center; padding: 18px;
}
.ob-panel {
    width: 100%; max-width: 380px; max-height: 86vh; display: flex; flex-direction: column;
    background: var(--panel); border: 1px solid var(--border); border-radius: 22px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.7); overflow: hidden;
}
.ob-panel-wide { max-width: 440px; }

/* How-to-play slides */
.ob-slide { padding: 30px 26px 14px; text-align: center; min-height: 240px; display: flex; flex-direction: column; justify-content: center; }
.ob-icon { font-size: calc(64px * var(--ts, 1)); margin-bottom: 12px; }
.ob-title { font-size: calc(32px * var(--ts, 1)); letter-spacing: 2px; color: var(--gold); margin-bottom: 12px; }
.ob-body { font-size: calc(16px * var(--ts, 1)); line-height: 1.55; color: rgba(255,255,255,0.88); }
.ob-body b { color: var(--gold); }
.ob-dots { display: flex; justify-content: center; gap: 8px; padding: 6px 0 14px; }
.ob-dot { width: 8px; height: 8px; border-radius: 50%; background: rgba(255,255,255,0.25); transition: all .2s; }
.ob-dot.on { background: var(--gold); transform: scale(1.3); }
.ob-nav { display: flex; gap: 8px; padding: 14px 18px 18px; border-top: 1px solid rgba(255,255,255,0.1); }
.ob-btn {
    flex: 1; padding: 12px 8px; border-radius: 50px; font-family: 'Bebas Neue', sans-serif; font-size: calc(18px * var(--ts, 1));
    letter-spacing: 1px; cursor: pointer; border: 1px solid rgba(255,255,255,0.3); background: rgba(0,0,0,0.3); color: #fff; transition: transform .1s;
}
.ob-btn:active { transform: scale(0.95); }
.ob-btn-primary { background: linear-gradient(135deg, #f59e0b, #fbbf24); color: #000; border: none; }
.ob-btn-ghost { background: transparent; color: rgba(255,255,255,0.65); }

/* Rules + settings header & scroll */
.ob-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px 12px; border-bottom: 1px solid rgba(255,255,255,0.1); }
.ob-head-title { font-size: calc(24px * var(--ts, 1)); letter-spacing: 1px; color: var(--gold); }
.ob-x { width: 34px; height: 34px; border-radius: 50%; border: 1px solid var(--border); background: rgba(0,0,0,0.3); color: #fff; font-size: calc(16px * var(--ts, 1)); cursor: pointer; }
.ob-x:active { background: rgba(255,255,255,0.15); }
.ob-scroll { overflow-y: auto; padding: 8px 16px 18px; -webkit-overflow-scrolling: touch; }
.ob-section { font-size: calc(19px * var(--ts, 1)); letter-spacing: 1px; color: #fff; margin: 16px 0 8px; padding-bottom: 5px; border-bottom: 1px solid rgba(255,255,255,0.12); }
.ob-section:first-child { margin-top: 4px; }
.ob-ref-row { display: flex; gap: 12px; align-items: flex-start; padding: 8px 4px; }
.ob-ref-icon { font-size: calc(22px * var(--ts, 1)); width: 30px; text-align: center; flex-shrink: 0; }
.ob-ref-text { display: flex; flex-direction: column; }
.ob-ref-text b { color: #fff; font-size: calc(14px * var(--ts, 1)); }
.ob-ref-text small { color: rgba(255,255,255,0.6); font-size: calc(13px * var(--ts, 1)); line-height: 1.4; }

/* Settings rows */
.set-row { display: flex; align-items: center; justify-content: space-between; padding: 13px 4px; border-bottom: 1px solid rgba(255,255,255,0.08); color: #fff; font-size: calc(15px * var(--ts, 1)); font-weight: 700; cursor: pointer; }
.set-toggle { width: 26px; height: 26px; accent-color: #fbbf24; cursor: pointer; }
.set-range { width: 150px; accent-color: #fbbf24; cursor: pointer; }
.set-wide { width: 100%; margin-top: 14px; }

/* Win screen rematch row */
.win-stats { color: rgba(255,255,255,0.8); font-size: calc(14px * var(--ts, 1)); font-weight: 700; margin: 4px 0 14px; min-height: 18px; }
.win-stats b { color: var(--gold); }
.win-btn-row { display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%; max-width: 300px; }
.win-menu-btn { margin-top: 0; }

/* ============================================================
   REDUCE MOTION  — kill CSS animation/transition for vestibular safety
   ============================================================ */
body.reduce-motion *, body.reduce-motion *::before, body.reduce-motion *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
}
body.reduce-motion .confetti-piece,
body.reduce-motion .coin-particle { display: none !important; }

/* ============================================================
   HUNDRED BLOCK DASH — story intro + realm-entry banner
   ============================================================ */
#hbd-story-overlay {
    position: fixed; inset: 0; z-index: 4000; display: none;
    flex-direction: column; align-items: center; justify-content: center;
    padding: 28px 20px; text-align: center;
    background: radial-gradient(circle at 50% 30%, #241246 0%, #0b0b1a 70%);
    overflow-y: auto;
}
.hbd-story-crown { font-size: calc(64px * var(--ts, 1)); filter: drop-shadow(0 0 18px rgba(245,200,66,.7)); animation: floatY 3s ease-in-out infinite; }
.hbd-story-title { font-size: calc(40px * var(--ts, 1)); color: var(--gold); letter-spacing: 2px; line-height: .95; margin: 6px 0 4px; text-shadow: 0 0 26px rgba(245,200,66,.6); }
.hbd-story-sub   { font-family: 'Nunito', sans-serif; font-size: calc(15px * var(--ts, 1)); color: rgba(255,255,255,.82); max-width: 360px; line-height: 1.5; margin: 8px auto 6px; }
.hbd-story-bonus {
    font-family: 'Nunito', sans-serif; font-weight: 800; font-size: calc(14px * var(--ts, 1)); color: #ffe08a;
    background: rgba(245,200,66,.12); border: 1px solid rgba(245,200,66,.45);
    border-radius: 12px; padding: 10px 16px; max-width: 360px; margin: 10px auto 16px; line-height: 1.45;
}
.hbd-story-realms { display: flex; flex-direction: column; gap: 8px; width: 100%; max-width: 360px; margin-bottom: 18px; }
.hbd-story-realm {
    display: flex; align-items: center; gap: 12px; text-align: left;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
    border-left-width: 4px; border-radius: 12px; padding: 10px 14px;
    animation: fadeSlideUp .5s backwards ease;
}
.hbd-story-realm .r-ic { font-size: calc(28px * var(--ts, 1)); }
.hbd-story-realm .r-name { font-family: 'Bebas Neue', sans-serif; font-size: calc(18px * var(--ts, 1)); letter-spacing: 1px; color: #fff; }
.hbd-story-realm .r-tag  { font-family: 'Nunito', sans-serif; font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.6); }
#btn-hbd-story-begin {
    background: linear-gradient(135deg, #f59e0b, #f5c842); color: #1a1a2e; border: none;
    font-size: calc(20px * var(--ts, 1)); letter-spacing: 1px; padding: 14px 40px; border-radius: 14px;
    cursor: pointer; box-shadow: 0 8px 22px rgba(245,158,11,.4);
}

#realm-banner {
    position: fixed; top: 38%; left: 0; right: 0; z-index: 1500;
    display: none; flex-direction: column; align-items: center; pointer-events: none;
    animation: realmBannerIn .5s ease;
}
#realm-banner .rb-ic   { font-size: calc(60px * var(--ts, 1)); filter: drop-shadow(0 0 16px rgba(0,0,0,.6)); }
#realm-banner .rb-name { font-family: 'Bebas Neue', sans-serif; font-size: calc(44px * var(--ts, 1)); letter-spacing: 3px; color: #fff; text-shadow: 0 2px 14px rgba(0,0,0,.8); }
#realm-banner .rb-tag  { font-family: 'Nunito', sans-serif; font-size: calc(15px * var(--ts, 1)); color: rgba(255,255,255,.85); text-shadow: 0 2px 8px rgba(0,0,0,.8); }
@keyframes realmBannerIn { from { opacity: 0; transform: translateY(20px) scale(.92); } to { opacity: 1; transform: none; } }
@keyframes floatY { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-8px); } }
body.reduce-motion .hbd-story-crown, body.reduce-motion #realm-banner { animation: none !important; }

/* ── Practice mode ───────────────────────────────────────────────────────────
   Practice is deliberately styled as a *lower-stakes* sibling of PLAY: same
   footprint, muted fill, so it reads as an option rather than the main action. */
.mg-sel-footer { display: flex; gap: 10px; }
.mg-sel-practice {
    flex: 1 1 0; padding: 14px 10px; border: 2px solid rgba(96,165,250,.55);
    border-radius: 14px; background: rgba(96,165,250,.12); color: #bfdbfe;
    font-size: calc(19px * var(--ts, 1)); letter-spacing: 1px; cursor: pointer;
    transition: transform .08s ease, background .15s ease;
}
.mg-sel-practice:active:not(:disabled) { transform: scale(.97); background: rgba(96,165,250,.24); }
.mg-sel-practice:disabled { opacity: .35; cursor: default; }
.mg-sel-footer .mg-sel-play { flex: 1 1 0; }
.mg-sel-hint {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.45); text-align: center; padding: 8px 18px 14px;
}
/* A pill, not a strip. Sitting above the orientation diagram, anything taller
   pushes WE'RE READY off the bottom of a phone. */
.mg-practice-banner {
    display: inline-block;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(11px * var(--ts, 1)); font-weight: 800;
    letter-spacing: .4px; color: #93c5fd; background: rgba(96,165,250,.14);
    border: 1px solid rgba(96,165,250,.4); border-radius: 999px;
    padding: 3px 12px; margin: 0 auto 6px;
}
.mg-practice-btn {
    margin-top: 10px; background: rgba(96,165,250,.14) !important;
    border: 2px solid rgba(96,165,250,.5) !important; color: #bfdbfe !important;
}

/* Minigame-incoming banner reuses the realm-banner element. */
#realm-banner .rb-tag { opacity: .8; }

/* ── Win screen: landscape presentation ──────────────────────────────────────
   The phone lies flat between two players, so the end-of-match summary is
   rotated 90° and sized against the swapped viewport. Turning the device
   sideways then gives a proper wide layout both players can read from the long
   edge, instead of a tall column only one of them can face. */
#win-screen { overflow: hidden; }
.win-inner {
    position: absolute; top: 50%; left: 50%;
    width: 100vh; height: 100vw;                 /* swapped on purpose */
    transform: translate(-50%, -50%) rotate(90deg);
    transform-origin: center center;
    display: flex; flex-direction: column; align-items: center;
    justify-content: flex-start; gap: 8px;
    padding: 14px 22px; box-sizing: border-box;
    transition: transform .35s cubic-bezier(.22,.9,.3,1);
}
#win-screen.portrait .win-inner {
    width: 100vw; height: 100vh;
    transform: translate(-50%, -50%) rotate(0deg);
}
.win-rotate {
    position: absolute; top: 10px; right: 14px; z-index: 5;
    padding: 7px 12px; border-radius: 10px; cursor: pointer;
    background: rgba(255,255,255,.10); border: 1px solid rgba(255,255,255,.28);
    color: rgba(255,255,255,.85); font-size: calc(13px * var(--ts, 1)); letter-spacing: 1px;
}
.win-head { display: flex; align-items: center; gap: 14px; flex: 0 0 auto; }
.win-head .win-crown { font-size: calc(40px * var(--ts, 1)); margin: 0; }
.win-body {
    display: flex; gap: 16px; width: 100%; flex: 1 1 auto;
    align-items: stretch; justify-content: center; min-height: 0;
}
.win-body .win-cards { flex: 1 1 46%; margin: 0; align-content: flex-start; }
.win-chart-wrap {
    flex: 1 1 54%; display: flex; flex-direction: column; min-width: 0;
    background: rgba(0,0,0,.28); border: 1px solid rgba(255,255,255,.12);
    border-radius: 14px; padding: 10px 12px;
}
.win-chart-title {
    font-size: calc(15px * var(--ts, 1)); letter-spacing: 1.5px; color: rgba(255,255,255,.72);
    margin-bottom: 6px; text-align: center;
}
/* The SVG has no intrinsic height, so without min-height:0 the flex item
   refuses to shrink below its content and spills out of the card. The floor
   that keeps the chart readable is set per-orientation below. */
#win-chart { flex: 1 1 auto; min-height: 0; }
#win-chart svg { width: 100%; height: 100%; display: block; }
.win-chart-legend {
    display: flex; gap: 14px; justify-content: center; flex-wrap: wrap;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.66); margin-top: 6px; flex: 0 0 auto;
}
.win-chart-legend b { font-weight: 800; }
.wc-swatch { display: inline-block; width: 10px; height: 3px; border-radius: 2px; margin-right: 5px; vertical-align: middle; }
.win-inner .win-btn-row { margin-top: 4px; flex: 0 0 auto; }
.win-inner .win-stats { flex: 0 0 auto; }

/* ── Landscape (default): only ~410px of height to work with ────────────────
   Everything is a fixed-height band except .win-body, which absorbs the slack.
   The numbers below are chosen so head + stats + buttons never squeeze the
   chart out of the card — that was the original overflow. */
/* The rotated inner box is exactly one screen-width tall (412px on a phone),
   and the bands inside it added up to a few pixels more — which #win-screen's
   overflow:hidden clipped, taking REMATCH with it. Let it scroll: the stat
   grid can grow and the buttons stay reachable either way. */
#win-screen:not(.portrait) .win-inner {
    padding: 6px 18px 8px; gap: 4px; justify-content: flex-start;
    overflow-y: auto; overflow-x: hidden;
    scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.22) transparent;
}
#win-screen:not(.portrait) .win-inner::-webkit-scrollbar { width: 4px; }
#win-screen:not(.portrait) .win-inner::-webkit-scrollbar-thumb { background: rgba(255,255,255,.22); border-radius: 2px; }
/* The chart needs a floor or it collapses to nothing when the cards are tall. */
#win-screen:not(.portrait) .win-body { min-height: 150px; }
/* A flex item defaults to min-height:auto, which refuses to shrink below its
   own content — so .win-body could never actually give back the height its
   min-height allowed, and the overflow landed on the button row instead. Let
   every level shrink; the stat block has its own scrollbar to absorb it. */
#win-screen:not(.portrait) .win-body > *,
#win-screen:not(.portrait) .win-cards,
#win-screen:not(.portrait) .win-card { min-height: 0; }
/* And clip at the body. Without this a card ten pixels taller than the row it
   sits in still contributes those ten pixels to .win-inner's scrollHeight —
   which is exactly how REMATCH kept ending up just past the bottom edge even
   though every band measured as fitting. The stat blocks scroll internally, so
   nothing is lost by clipping here. */
#win-screen:not(.portrait) .win-body { overflow: hidden; }
#win-screen:not(.portrait) .win-crown { font-size: calc(26px * var(--ts, 1)); }
#win-screen:not(.portrait) .win-head { gap: 10px; }
#win-screen:not(.portrait) .win-btn-row > button { line-height: 1.1; }
#win-screen:not(.portrait) .win-name { font-size: calc(27px * var(--ts, 1)); letter-spacing: 1.6px; line-height: 1; }
#win-screen:not(.portrait) .win-subtitle { font-size: calc(11px * var(--ts, 1)); margin-bottom: 0; line-height: 1.2; }
#win-screen:not(.portrait) .win-cards { max-width: none; padding: 0; gap: 8px; flex-wrap: nowrap; }
#win-screen:not(.portrait) .win-card { padding: 8px 10px; border-radius: 14px; min-width: 0; }
#win-screen:not(.portrait) .win-card-name { font-size: calc(14px * var(--ts, 1)); margin-bottom: 2px; }
#win-screen:not(.portrait) .win-card-score { font-size: calc(26px * var(--ts, 1)); margin-bottom: 4px; }
#win-screen:not(.portrait) .win-card-stat { font-size: calc(11px * var(--ts, 1)); margin-bottom: 1px; gap: 6px; }
#win-screen:not(.portrait) .ws-tile { padding: 3px 4px; }
#win-screen:not(.portrait) .ws-v { font-size: calc(15px * var(--ts, 1)); }
#win-screen:not(.portrait) .ws-l { font-size: calc(11px * var(--ts, 1)); }
#win-screen:not(.portrait) .ws-ic { font-size: calc(11px * var(--ts, 1)); }
#win-screen:not(.portrait) .ws-chip { font-size: calc(11px * var(--ts, 1)); padding: 1px 4px; }
#win-screen:not(.portrait) .ws-dlabel { font-size: calc(11px * var(--ts, 1)); }
#win-screen:not(.portrait) .win-chart-title { font-size: calc(12px * var(--ts, 1)); margin-bottom: 3px; }
#win-screen:not(.portrait) .win-chart-wrap { padding: 7px 10px; }
#win-screen:not(.portrait) .win-chart-legend { font-size: calc(11px * var(--ts, 1)); gap: 10px; margin-top: 4px; }
#win-screen:not(.portrait) .win-stats { font-size: calc(12px * var(--ts, 1)); margin: 0; min-height: 0; }
/* Side by side, not stacked — a stacked pair costs ~100px we don't have.
   Sticky, because the bands above add up to almost exactly the rotated screen
   height and a few pixels of font-metric drift used to push REMATCH clean off
   the edge. Pinned to the end of the scroll container it is reachable whatever
   the stat block does. */
#win-screen:not(.portrait) .win-btn-row {
    flex-direction: row; max-width: 460px; gap: 10px; margin-top: 0;
    position: sticky; bottom: 0; z-index: 3;
    padding-top: 4px;
    background: linear-gradient(to bottom, rgba(15,12,41,0), rgba(15,12,41,.92) 45%);
}
#win-screen:not(.portrait) .win-btn-row > button {
    flex: 1 1 0; margin-top: 0; padding: 8px 14px; font-size: calc(16px * var(--ts, 1)); min-width: 0;
}

/* ── Portrait: the tall column, scrolled if the cards run long ───────────── */
#win-screen.portrait .win-inner { overflow-y: auto; justify-content: flex-start; padding-bottom: 24px; }
#win-screen.portrait .win-body { flex: 0 0 auto; }
#win-screen.portrait #win-chart { min-height: 200px; }

/* ══════════════════════════════════════════════════════════════════════════
   DUAL READ — both players reading the same message
   See src/ui/DualRead.js for the tier split this implements.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── The ⟳ flip button, and the rotation it drives ──────────────────────────
   Card rotation goes through a custom property so the tabletop turn-flip and
   the manual flip compose instead of overwriting each other. Order matters:
   .modal-box.flipped and .tabletop-p2-turn .modal-box tie on specificity, so
   the manual flip is declared last and the tabletop+flipped pair last of all. */
.modal-box { transform: rotate(var(--card-rot, 0deg)); transition: transform .32s cubic-bezier(.22,.9,.3,1); }
.tabletop-p2-turn .modal-box { --card-rot: 180deg; }
.modal-box.flipped { --card-rot: 180deg; }
.tabletop-p2-turn .modal-box.flipped { --card-rot: 0deg; }

#mg-page-info, #mg-page-hold { transform: rotate(var(--card-rot, 0deg)); transition: transform .32s cubic-bezier(.22,.9,.3,1); }
.tabletop-p2-turn #mg-intro-overlay #mg-page-info,
.tabletop-p2-turn #mg-intro-overlay #mg-page-hold { --card-rot: 180deg; }
#mg-page-info.flipped, #mg-page-hold.flipped { --card-rot: 180deg; }
.tabletop-p2-turn #mg-intro-overlay #mg-page-info.flipped,
.tabletop-p2-turn #mg-intro-overlay #mg-page-hold.flipped { --card-rot: 0deg; }

.card-flip-btn {
    position: absolute; top: 8px; right: 10px; z-index: 4;
    width: 34px; height: 34px; padding: 0; line-height: 1;
    border-radius: 10px; cursor: pointer;
    background: rgba(255,255,255,.09);
    border: 1px solid rgba(255,255,255,.26);
    color: rgba(255,255,255,.82); font-size: calc(18px * var(--ts, 1));
    display: flex; align-items: center; justify-content: center;
    transition: background .15s, transform .15s;
}
.card-flip-btn:active { transform: scale(.92); background: rgba(255,255,255,.18); }
/* The cards the button sits inside need to be a positioning context. */
.modal-box, #mg-page-info, #mg-page-hold { position: relative; }

/* ── The "you can flip this" note, first few cards only ──────────────────── */
.flip-hint {
    display: none; align-items: center; gap: 7px;
    margin: 12px auto 0; padding: 7px 11px; max-width: 300px;
    border-radius: 10px;
    background: rgba(245,200,66,.13);
    border: 1px solid rgba(245,200,66,.4);
    color: rgba(255,255,255,.88);
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.35;
    text-align: left;
    animation: fadeSlideUp .4s ease backwards;
}
.flip-hint .fh-arrow { font-size: calc(15px * var(--ts, 1)); color: var(--gold); flex: 0 0 auto; }
.flip-hint b { color: var(--gold); }

/* ── SHARED tier: the mirrored copy ─────────────────────────────────────────
   The container becomes a column and the copy sits above the original, rotated
   180°, so the player at the far edge reads their copy the right way up. */
#modal-overlay.dual-mode { flex-direction: column; gap: 10px; padding: 10px 0; }
#mg-intro-content.dual-mode { display: flex; flex-direction: column; gap: 10px; }
.dual-mirror { transform: rotate(180deg); }
/* Mirrored pairs must ignore the whose-turn-is-it flip: one copy always faces
   each player regardless of who is acting. */
.tabletop-p2-turn .modal-box.dual-host,
.tabletop-p2-turn #mg-intro-overlay #mg-page-info.dual-host,
.tabletop-p2-turn #mg-intro-overlay #mg-page-hold.dual-host { --card-rot: 0deg; }
/* Two copies share the height, so the card has to give some back. */
#modal-overlay.dual-mode .modal-box,
#modal-overlay.dual-mode .dual-mirror { max-height: 46vh; padding: 16px 20px; }
#modal-overlay.dual-mode .m-title { font-size: calc(24px * var(--ts, 1)); margin-bottom: 5px; }
#modal-overlay.dual-mode .m-desc  { font-size: calc(14px * var(--ts, 1)); margin-bottom: 13px; }
#modal-overlay.dual-mode .modal-box > div:first-of-type { font-size: calc(34px * var(--ts, 1)) !important; margin-bottom: 4px !important; }
#modal-overlay.dual-mode .btn-close { padding: 9px 22px; font-size: calc(16px * var(--ts, 1)); }
#mg-intro-content.dual-mode .mg-intro-title { font-size: calc(34px * var(--ts, 1)); margin-bottom: 7px; }
#mg-intro-content.dual-mode .mg-intro-desc  { font-size: calc(13px * var(--ts, 1)); margin-bottom: 11px !important; line-height: 1.4; }
/* The game icon, shrunk on both copies. Targeted by identity, not position: a
   `> div:first-child` rule caught whatever happened to lead the card, so on the
   mirrored copy it blew up the orientation heading and the practice tag. The
   mirror's ids are demoted to data-mirror-id, which is how the copy is named. */
#mg-intro-content.dual-mode #mg-intro-icon,
#mg-intro-content.dual-mode [data-mirror-id="mg-intro-icon"] { font-size: calc(32px * var(--ts, 1)) !important; margin-bottom: 4px !important; }
#mg-intro-content.dual-mode .btn-close { padding: 8px 20px; font-size: calc(15px * var(--ts, 1)); }
/* The diagram is 110×210 with absolutely-positioned parts, so it is scaled
   rather than resized; the wrap is given the scaled height so the layout
   actually gets the space back. */
#mg-intro-content.dual-mode .phone-diagram-wrap { margin: 8px auto !important; height: 132px; overflow: hidden; }
#mg-intro-content.dual-mode .phone-diagram { transform: scale(.6); flex: 0 0 auto; }
#mg-intro-content.dual-mode .orient-name { font-size: calc(26px * var(--ts, 1)); }
#mg-intro-content.dual-mode .orient-instructions { font-size: calc(11px * var(--ts, 1)); margin: 6px auto 10px; }
#mg-intro-content.dual-mode .mg-practice-banner { font-size: calc(11px * var(--ts, 1)); padding: 2px 10px; margin-bottom: 4px; }

/* Caption overlays with no buttons (realm banner, "minigame next"). */
.dual-cap { display: flex; flex-direction: column; align-items: center; }
.dual-cap.mirror { transform: rotate(180deg); margin-bottom: 26px; }
/* A mirrored banner already faces both ways — don't turn the pair as a unit. */
.tabletop-p2-turn #realm-banner > .dual-cap { transform: none; }
.tabletop-p2-turn #realm-banner > .dual-cap.mirror { transform: rotate(180deg); }

/* ── OWNER tier: the opponent's headline strip ───────────────────────────── */
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
.opp-ticker {
    position: fixed; left: 50%; z-index: 260;
    display: none; align-items: center; gap: 9px;
    max-width: 88vw; padding: 8px 15px;
    border-radius: 13px;
    background: rgba(12, 8, 26, .95);
    border: 1px solid rgba(255,255,255,.2);
    box-shadow: 0 10px 28px rgba(0,0,0,.7);
    pointer-events: none;
    /* Fade only. fadeSlideUp animates `transform`, which for 300ms overwrote the
       translateX(-50%) rotate(180deg) below — the strip flashed un-rotated and
       half a width off centre before snapping into place. */
    animation: fadeIn .3s ease backwards;
}
.opp-ticker .ot-ic { font-size: calc(21px * var(--ts, 1)); flex: 0 0 auto; }
.opp-ticker .ot-tx {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(13px * var(--ts, 1)); font-weight: 800;
    color: rgba(255,255,255,.92); letter-spacing: .3px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Player 2 sits at the top of the device, Player 1 at the bottom. The strip
   goes on whichever edge the *opponent* is at, facing them. */
.opp-ticker.at-top    { top: calc(var(--hud-h) + 10px);    transform: translateX(-50%) rotate(180deg); }
.opp-ticker.at-bottom { bottom: calc(var(--hud-h) + 10px); transform: translateX(-50%); }

/* ── Dual confirm: each side reports its own readiness ───────────────────── */
.dual-ready-note {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.6); margin-top: 8px; min-height: 15px;
}
.btn-close.dual-done {
    background: rgba(74,222,128,.16) !important;
    border-color: rgba(74,222,128,.6) !important;
    color: #4ade80 !important;
}

/* A toast is pinned to the centre of the screen, which is where a card sits;
   they were overlapping and neither was readable. While a card is up, the
   toast moves to the edge nearest whoever is being addressed. */
#toast-box { transition: bottom .25s ease, top .25s ease, transform .25s ease; }
/* A modal owns the lower half of the screen, so the rail steps aside rather
   than sitting under it. */
body.modal-open #toast-box { bottom: calc(var(--hud-h) + 4px); }
/* The gate card is anchored to the same edge as the rail and would sit on top
   of its ROLL button. The gate scene hides #ui-layer, so the opposite edge is
   empty — send the rail there for the duration. */
body.gate-scene #toast-box { bottom: auto; top: calc(var(--hud-h) + 10px); }
body.gate-scene.tabletop-p2-turn #toast-box { top: auto; bottom: calc(var(--hud-h) + 10px); }
/* The buddy report hugs the same edge the rail normally sits on, and round-end
   toasts (Banker interest, a buddy expiring) fire in the same beat — so without
   this a notification lands squarely on the report's GOT IT button. */
body.buddy-report #toast-box { bottom: auto; top: calc(var(--hud-h) + 10px); }
body.buddy-report.tabletop-p2-turn #toast-box { top: auto; bottom: calc(var(--hud-h) + 10px); }

/* ══════════════════════════════════════════════════════════════════════════
   POST-MINIGAME SCOREBOARD
   Two player cards with coin totals and a FIRST / SECOND badge, drawn twice —
   the top copy rotated — so both ends of the table read the whole comparison
   the right way up. Built by MinigameManager._showScoreboard.
   ══════════════════════════════════════════════════════════════════════════ */
.mg-score-screen {
    position: absolute; inset: 0; z-index: 90;
    display: flex; flex-direction: column;
    background: radial-gradient(ellipse at 50% 50%, rgba(24,14,48,.97), rgba(6,4,14,.99));
    animation: fadeSlideUp .28s ease backwards;
}
.mg-sc-panel {
    flex: 1 1 50%; min-height: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 7px; padding: 10px 12px;
}
.mg-sc-panel.mg-sc-top { transform: rotate(180deg); }
.mg-sc-head {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(30px * var(--ts, 1)); letter-spacing: 2px;
    color: var(--gold); text-shadow: 0 0 22px rgba(245,200,66,.55); text-align: center;
    line-height: 1.05;
}
.mg-sc-sub {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.55); letter-spacing: .5px; margin-bottom: 2px;
}
.mg-sc-row { display: flex; gap: 10px; width: 100%; max-width: 400px; justify-content: center; }
.mg-sc-card {
    flex: 1 1 0; min-width: 0; padding: 10px 8px;
    border-radius: 16px; text-align: center;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.14);
}
.mg-sc-card.mg-sc-win { border-color: var(--gold); background: rgba(245,200,66,.10); }
.mg-sc-rank {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(14px * var(--ts, 1)); letter-spacing: 2px;
    margin-bottom: 3px;
}
.mg-sc-rank.mg-sc-first  { color: var(--gold); }
.mg-sc-rank.mg-sc-second { color: rgba(255,255,255,.45); }
.mg-sc-rank.mg-sc-tie    { color: #a855f7; }
.mg-sc-name {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(17px * var(--ts, 1)); letter-spacing: 1px;
    margin-bottom: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mg-sc-coins {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(34px * var(--ts, 1)); color: #4ade80;
    line-height: 1; margin-bottom: 5px;
}
.mg-sc-coins .mg-sc-unit { font-size: calc(15px * var(--ts, 1)); margin-left: 3px; }
.mg-sc-line {
    display: block; font-family: 'Nunito', system-ui, sans-serif;
    font-size: calc(11px * var(--ts, 1)); color: rgba(255,255,255,.78); line-height: 1.5;
}
.mg-sc-line b { color: var(--gold); }
.mg-sc-dim { color: rgba(255,255,255,.42); }
.mg-sc-dim b { color: rgba(255,255,255,.7); }
.mg-sc-btn {
    margin-top: 6px; padding: 8px 26px; border-radius: 12px; cursor: pointer;
    font-family: 'Bebas Neue', sans-serif; font-size: calc(17px * var(--ts, 1)); letter-spacing: 2px;
    color: #1a1206; background: linear-gradient(180deg, #f7d777, #eab13a);
    border: none; box-shadow: 0 6px 18px rgba(245,200,66,.32);
    transition: opacity .2s, transform .1s;
}
.mg-sc-btn:disabled { opacity: .35; }
.mg-sc-btn:active:not(:disabled) { transform: scale(.96); }

/* Map tooltip: what the space does, and how far away it is. Scouting the road
   used to give you a name and a block number and nothing actionable.
   The base rule is nowrap for a one-line label; now that there is a sentence in
   here it has to be allowed to wrap. */
#map-tooltip { white-space: normal; max-width: 264px; }
/* A tile with a buddy standing beside it is the one square worth routing
   towards, so it says so above its own name. */
#map-tooltip .map-buddy {
    display: block; margin-bottom: 4px; padding-bottom: 4px;
    border-bottom: 1px solid rgba(251,191,36,.35);
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 900;
    letter-spacing: .5px; color: #fbbf24;
}
#map-tooltip .map-effect {
    display: block; margin-top: 5px;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.4;
    color: rgba(255,255,255,.86); white-space: normal; max-width: 240px;
}
#map-tooltip .map-range {
    display: block; margin-top: 5px; padding-top: 5px;
    border-top: 1px solid rgba(255,255,255,.14);
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    color: var(--gold); letter-spacing: .3px;
}

/* ============================================================
   CITY CIRCUIT — junction arrows, bounty panel, opening briefing
   ============================================================ */

/* --- The BOUNTIES square in the action stack --- */
.btn-action.bounty-btn {
    background: linear-gradient(145deg, #14532d, #16a34a) !important;
    border: none !important;
    box-shadow: 0 4px 18px rgba(34,197,94,0.38), inset 0 1px 0 rgba(255,255,255,0.1) !important;
}
/* "BOUNTIES" is the longest label in the stack and has to fit an 86px square. */
.btn-action.bounty-btn .ba-tx { font-size: calc(12px * var(--ts, 1)); letter-spacing: 0; }

/* ============================================================
   SHIELD MARKER — you are still carrying an active shield
   ============================================================
   Bottom-left: the action buttons own the right half of the screen and the
   toast rail is centred, so this corner is the one piece of persistent chrome
   space that nothing else competes for. Belongs to the active player, so it
   flips with everything else in tabletop. */
#shield-marker {
    position: fixed; z-index: 260; pointer-events: none;
    left: 12px; bottom: calc(var(--hud-h) + 12px); top: auto;
    display: flex; align-items: center; gap: 6px;
    padding: 6px 11px 6px 8px; border-radius: 999px;
    background: rgba(8, 22, 16, .9);
    border: 1.5px solid rgba(74, 222, 128, .65);
    box-shadow: 0 6px 20px rgba(0,0,0,.6), 0 0 22px -8px rgba(74,222,128,.9);
    animation: sm-breathe 2.6s ease-in-out infinite;
}
.tabletop-p2-turn #shield-marker {
    bottom: auto; top: calc(var(--hud-h) + 12px);
    left: auto; right: 12px;
    transform: rotate(180deg);
}
.sm-ic { font-size: calc(17px * var(--ts, 1)); line-height: 1; }
.sm-tx { font-size: calc(12.5px * var(--ts, 1)); letter-spacing: 1.3px; color: #86efac; }
@keyframes sm-breathe {
    0%, 100% { box-shadow: 0 6px 20px rgba(0,0,0,.6), 0 0 22px -8px rgba(74,222,128,.9); }
    50%      { box-shadow: 0 6px 20px rgba(0,0,0,.6), 0 0 26px -4px rgba(74,222,128,1); }
}

/* --- The fork: a choice dock ----------------------------------------------
   The road buttons used to float at projected points over the board, which
   put them on top of the bounty strip and the steps readout, under the action
   column, and half off the screen at two of the four City junctions. They now
   live in one dock in the thumb zone; the board carries matching arrows on
   the roads themselves (Renderer.showJunctionMarkers), and tapping one of
   those picks it too. While the dock is up the rest of the HUD steps back. */
#junction-layer { position: fixed; inset: 0; z-index: 60; pointer-events: none; }
#junction-dock {
    position: absolute; left: 10px; right: 10px;
    bottom: calc(var(--hud-h) + 10px);
    max-width: 560px; margin: 0 auto;
    display: flex; flex-direction: column; gap: 8px;
    padding: 10px 10px 8px; border-radius: 20px;
    background: rgba(8,6,18,.9); border: 1px solid rgba(255,255,255,.16);
    box-shadow: 0 14px 40px rgba(0,0,0,.65);
    pointer-events: auto;
    animation: jdIn .22s ease-out;
}
.tabletop-p2-turn #junction-dock { bottom: auto; top: calc(var(--hud-h) + 10px); transform: rotate(180deg); }
@keyframes jdIn { from { opacity: 0; translate: 0 14px; } to { opacity: 1; translate: 0 0; } }
body.reduce-motion #junction-dock { animation: none; }
.jd-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 0 4px; }
#junction-banner { font-size: calc(15px * var(--ts, 1)); letter-spacing: 1px; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
#junction-steps { white-space: nowrap; color: rgba(255,255,255,.85); font-size: calc(13px * var(--ts, 1)); letter-spacing: 1px; }
#junction-steps .js-num { color: var(--gold); font-size: calc(22px * var(--ts, 1)); }
#junction-arrows { display: grid; grid-template-columns: repeat(var(--jn, 2), minmax(0, 1fr)); gap: 8px; }
.j-arrow {
    display: flex; align-items: center; gap: 10px; min-height: 78px;
    padding: 10px 10px 10px 8px; border-radius: 16px;
    background: rgba(255,255,255,.06); border: 2px solid rgba(255,255,255,.3);
    color: #fff; text-align: left; cursor: pointer; font: inherit;
    transition: transform .1s ease, background .1s ease;
}
.j-arrow:active { transform: scale(.96); background: rgba(255,255,255,.14); }
.j-head {
    flex: 0 0 auto; width: 44px; height: 44px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: var(--jc, #94a3b8); color: #0b0913;
    font-size: calc(22px * var(--ts, 1)); line-height: 1;
    box-shadow: 0 0 16px -2px var(--jc, #94a3b8);
}
.j-head i { font-style: normal; display: inline-block; transform-origin: 50% 50%; transition: transform .15s linear; }
.j-label { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.j-name  { font-size: calc(15px * var(--ts, 1)); letter-spacing: .6px; line-height: 1.1; color: var(--jc, #fff); }
.j-meta  { font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800; color: var(--gold); }
.j-desc  { font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.75); line-height: 1.25; }
/* District colours: the card, its arrow and the marker on the road all match. */
.j-arrow { border-color: color-mix(in srgb, var(--jc, #94a3b8) 80%, transparent); }
.j-ring { --jc: #d5dde8; } .j-fin { --jc: #4ade80; } .j-ba { --jc: #a855f7; }
.j-shop { --jc: #f472b6; } .j-ind { --jc: #fb923c; }
.j-arrow.j-locked .j-desc { color: #fbbf24; }
#btn-junction-map {
    align-self: center; cursor: pointer;
    background: none; color: rgba(255,255,255,.82);
    border: 1px solid rgba(255,255,255,.24); border-radius: 18px;
    padding: 6px 16px; font-size: calc(12px * var(--ts, 1)); letter-spacing: .8px;
}
#btn-junction-map:active { transform: scale(.96); }
/* While a fork is open the choice is the whole screen: the action column, the
   bounty strip and the round counter step back. */
body.junction-open #p1-actions, body.junction-open #p2-actions,
body.junction-open #contracts-strip, body.junction-open #round-counter { visibility: hidden; }

/* --- Bounty panel ------------------------------------------------------ */
#bounty-panel {
    position: fixed; inset: 0; z-index: 170;
    display: none; align-items: center; justify-content: center;
    background: rgba(4,3,10,.82);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    padding: 16px;
}
.bounty-sheet {
    width: 100%; max-width: 460px; max-height: 88vh;
    display: flex; flex-direction: column; gap: 12px;
    background: var(--panel); border: 1px solid var(--border);
    border-radius: 20px; padding: 18px 16px 16px;
    box-shadow: 0 20px 60px rgba(0,0,0,.8);
}
.tabletop-p2-turn .bounty-sheet { transform: rotate(180deg); }
.bounty-head { text-align: center; }
.bounty-title { font-size: calc(22px * var(--ts, 1)); letter-spacing: 1.4px; color: #fff; }
.bounty-sub {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.6); margin-top: 4px; line-height: 1.4;
}
.bounty-list { display: flex; flex-direction: column; gap: 9px; overflow-y: auto; padding-right: 2px; }
.bounty-empty {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(13px * var(--ts, 1));
    color: rgba(255,255,255,.55); text-align: center; padding: 24px 0;
}
.bounty-card {
    display: flex; align-items: flex-start; gap: 10px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
    border-radius: 14px; padding: 11px 12px;
}
.bq-icon { flex: 0 0 auto; font-size: calc(24px * var(--ts, 1)); line-height: 1.1; }
.bq-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.bq-desc { font-size: calc(14px * var(--ts, 1)); letter-spacing: .4px; color: #fff; line-height: 1.2; }
.bq-hint {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.35;
    color: rgba(255,255,255,.58);
}
.bq-track { display: flex; align-items: center; gap: 6px; margin-top: 2px; }
.bq-who {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    width: 58px; flex: 0 0 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bq-bar { flex: 1 1 auto; height: 6px; border-radius: 3px; background: rgba(255,255,255,.12); overflow: hidden; }
.bq-fill { display: block; height: 100%; border-radius: 3px; transition: width .3s ease; }
.bq-num {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    color: rgba(255,255,255,.65); width: 30px; text-align: right; flex: 0 0 auto;
}
.bq-reward {
    flex: 0 0 auto; font-size: calc(17px * var(--ts, 1)); color: #4ade80; letter-spacing: .5px;
    display: flex; align-items: baseline; gap: 2px;
}
.bq-reward span { font-size: calc(12px * var(--ts, 1)); }
#btn-close-bounties {
    background: linear-gradient(150deg, #b37c0c, #f5c842); color: #1a0a00;
    border: none; border-radius: 14px; padding: 13px; font-size: calc(15px * var(--ts, 1)); letter-spacing: 1px;
    cursor: pointer; box-shadow: 0 6px 20px rgba(245,200,66,.4);
}
#btn-close-bounties:active { transform: scale(.97); }
/* A counted bounty one step from paying out is worth noticing at a glance. */
.contract-pill.contract-close {
    border-color: rgba(74,222,128,.7);
    box-shadow: 0 0 0 1px rgba(74,222,128,.25), 0 0 14px rgba(74,222,128,.25);
}

/* --- Opening city briefing --------------------------------------------- */
#city-briefing {
    position: fixed; inset: 0; z-index: 175;
    display: none; align-items: center; justify-content: center;
    background: rgba(4,3,10,.78);
    backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px);
    padding: 14px;
}
.cb-sheet {
    width: 100%; max-width: 470px; max-height: 90vh; overflow-y: auto;
    display: flex; flex-direction: column; gap: 10px;
    background: var(--panel); border: 1px solid var(--border);
    border-radius: 20px; padding: 18px 16px 16px;
    box-shadow: 0 20px 60px rgba(0,0,0,.8);
}
.cb-title { font-size: calc(24px * var(--ts, 1)); letter-spacing: 1.6px; color: #fff; text-align: center; }
.cb-lead {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12.5px * var(--ts, 1)); line-height: 1.45;
    color: rgba(255,255,255,.72); text-align: center; margin-bottom: 2px;
}
.cb-paths { display: flex; flex-direction: column; gap: 7px; }
.cb-path {
    display: flex; align-items: center; gap: 10px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
    border-radius: 13px; padding: 9px 11px;
}
.cb-ic { flex: 0 0 auto; font-size: calc(22px * var(--ts, 1)); }
.cb-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.cb-name { font-size: calc(13.5px * var(--ts, 1)); letter-spacing: .4px; color: #fff; }
.cb-desc { font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.62); line-height: 1.3; }
.cb-hq   { font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); color: var(--gold); line-height: 1.3; }
.cb-len {
    flex: 0 0 auto; font-size: calc(18px * var(--ts, 1)); color: rgba(255,255,255,.85);
    width: 30px; text-align: right;
}
.cb-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.cb-actions button {
    border-radius: 14px; padding: 13px; font-size: calc(15px * var(--ts, 1)); letter-spacing: 1px; cursor: pointer;
    border: 1px solid rgba(255,255,255,.28); background: rgba(255,255,255,.08); color: #fff;
}
.cb-actions button:active { transform: scale(.97); }
.cb-actions .cb-go {
    background: linear-gradient(150deg, #b37c0c, #f5c842); color: #1a0a00;
    border: none; box-shadow: 0 6px 20px rgba(245,200,66,.4);
}

/* Briefing in tabletop: both players read it at once, so it is drawn twice with
   the top copy turned. Two sheets have to share one screen, so everything in
   them gets tighter. */
#city-briefing.dual-mode { flex-direction: column; gap: 8px; padding: 8px 12px; }
#city-briefing.dual-mode .cb-sheet,
#city-briefing.dual-mode .dual-mirror {
    max-height: 45vh; overflow-y: auto; padding: 11px 13px 11px; gap: 6px;
}
#city-briefing.dual-mode .cb-title { font-size: calc(17px * var(--ts, 1)); }
#city-briefing.dual-mode .cb-lead  { font-size: calc(11px * var(--ts, 1)); }
#city-briefing.dual-mode .cb-path  { padding: 5px 8px; gap: 7px; border-radius: 10px; }
#city-briefing.dual-mode .cb-ic    { font-size: calc(16px * var(--ts, 1)); }
#city-briefing.dual-mode .cb-name  { font-size: calc(11px * var(--ts, 1)); }
#city-briefing.dual-mode .cb-desc  { font-size: calc(11px * var(--ts, 1)); }
#city-briefing.dual-mode .cb-hq    { font-size: calc(11px * var(--ts, 1)); }
#city-briefing.dual-mode .cb-len   { font-size: calc(14px * var(--ts, 1)); width: 24px; }
#city-briefing.dual-mode .cb-actions { flex-direction: row; gap: 6px; }
#city-briefing.dual-mode .cb-actions button { padding: 9px 8px; font-size: calc(11px * var(--ts, 1)); letter-spacing: .4px; flex: 1; }

/* ============================================================
   WIN SCREEN — stat tiles
   ============================================================
   Ten stacked label/value rows per player did not fit the landscape screen:
   they ran past the chart and pushed REMATCH clean off the edge. The stats are
   now a two-across tile grid — half the height for the same information — and
   the block scrolls inside its card, so nothing below it can ever be displaced
   again however many stats get added later. */
.win-card { display: flex; flex-direction: column; min-height: 0; }
.win-card-head { flex: 0 0 auto; }
.win-card-stats {
    flex: 1 1 auto; min-height: 0; overflow-y: auto;
    scrollbar-width: thin; scrollbar-color: rgba(255,255,255,.25) transparent;
}
.win-card-stats::-webkit-scrollbar { width: 4px; }
.win-card-stats::-webkit-scrollbar-thumb { background: rgba(255,255,255,.25); border-radius: 2px; }

.ws-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.ws-tile {
    position: relative; display: flex; flex-direction: column; align-items: center;
    gap: 0; padding: 5px 4px; border-radius: 9px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.09);
    min-width: 0;
}
.ws-ic { position: absolute; top: 3px; left: 4px; font-size: calc(12px * var(--ts, 1)); opacity: .75; line-height: 1; }
.ws-v {
    font-family: 'Bebas Neue', Impact, sans-serif; font-size: calc(18px * var(--ts, 1)); line-height: 1.05;
    color: #fff; letter-spacing: .5px;
}
.ws-l {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.15;
    color: rgba(255,255,255,.52); text-align: center;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
/* Four districts as one strip of chips instead of four full-width rows. */
.ws-districts {
    display: flex; align-items: center; gap: 6px; margin-top: 5px;
    padding-top: 5px; border-top: 1px solid rgba(255,255,255,.1);
}
.ws-dlabel {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    letter-spacing: .5px; color: rgba(255,255,255,.42); flex: 0 0 auto;
}
.ws-chips { display: flex; gap: 3px; flex: 1 1 auto; justify-content: flex-end; flex-wrap: wrap; }
.ws-chip {
    display: inline-flex; align-items: center; gap: 2px; padding: 1px 5px;
    border-radius: 8px; background: rgba(255,255,255,.06);
    border: 1px solid rgba(255,255,255,.1);
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.7); line-height: 1.3;
}
.ws-chip b { font-weight: 800; color: #fff; }
.ws-chip i { font-style: normal; font-size: calc(12px * var(--ts, 1)); }
.ws-chip.ws-held { border-color: rgba(251,191,36,.65); background: rgba(251,191,36,.13); }

/* ============================================================
   TURN BANNER — whose turn it is, at every hand-over
   ============================================================
   Two copies, one on each player's edge, because it matters to both: whoever
   is up needs to know they are up, and whoever just finished needs to know
   they are done. Purely informational — it must never eat a tap meant for the
   roll button, hence pointer-events: none the whole way down. */
#turn-banner {
    position: fixed; inset: 0; z-index: 120;
    pointer-events: none;
}
.tb-card {
    position: absolute; left: 50%;
    display: flex; align-items: center; gap: 10px;
    padding: 9px 18px 9px 14px; border-radius: 999px;
    background: rgba(8, 6, 18, .9);
    border: 1.5px solid var(--tb-col, rgba(255,255,255,.4));
    box-shadow: 0 10px 30px rgba(0,0,0,.6), 0 0 22px -6px var(--tb-col, transparent);
    white-space: nowrap; opacity: 0;
    transition: opacity .22s ease, transform .28s cubic-bezier(.22,.9,.3,1);
}
/* Whoever is NOT up gets the quieter copy — same information, less shout. */
.tb-card:not(.tb-active) { opacity: 0; filter: saturate(.4); }
.tb-bottom { bottom: calc(var(--hud-h) + 108px); transform: translate(-50%, 14px); }
.tb-top    { top: calc(var(--hud-h) + 108px);    transform: translate(-50%, -14px) rotate(180deg); }
/* Pass-and-play and 1P: the device is not being shared across a table, so one
   upright banner is the whole story — the mirrored copy would just be upside
   down for the only person looking. */
body:not(.tabletop-mode) #turn-banner .tb-top { display: none; }
body:not(.tabletop-mode) .tb-bottom { bottom: auto; top: 50%; transform: translate(-50%, -50%) scale(.94); }

#turn-banner.tb-show .tb-card { opacity: 1; }
#turn-banner.tb-show .tb-card:not(.tb-active) { opacity: .55; }
#turn-banner.tb-show .tb-bottom { transform: translate(-50%, 0); }
#turn-banner.tb-show .tb-top    { transform: translate(-50%, 0) rotate(180deg); }
body:not(.tabletop-mode) #turn-banner.tb-show .tb-bottom { transform: translate(-50%, -50%) scale(1); }

/* ---- The hand-over, said loudly -------------------------------------------
   The pill above is right for tabletop, where two people share one screen and
   a full-width card would cover the board for both of them. Everywhere else —
   pass-and-play, and every online match — the device belongs to ONE person and
   the question it has to answer from across a room is "is it me?". So off the
   tabletop it is a real announcement: the character at size, the name in the
   seat's own colour, and a line that says YOUR TURN in the second person when
   it is yours rather than making you match a name against the HUD. */
body:not(.tabletop-mode) .tb-bottom {
    flex-direction: column; gap: 10px;
    padding: 26px 34px; border-radius: 22px;
    border-width: 3px;
    background: linear-gradient(180deg, rgba(14,10,30,.97), rgba(8,6,18,.99));
    box-shadow: 0 26px 70px rgba(0,0,0,.72), 0 0 60px -14px var(--tb-col, transparent);
    min-width: min(74vw, 340px);
}
body:not(.tabletop-mode) .tb-bottom .tb-ic   { font-size: calc(66px * var(--ts, 1)); }
body:not(.tabletop-mode) .tb-bottom .tb-txt  { align-items: center; gap: 6px; }
body:not(.tabletop-mode) .tb-bottom .tb-name {
    font-size: calc(40px * var(--ts, 1)); letter-spacing: 2.4px; text-align: center;
    text-shadow: 0 0 26px var(--tb-col, transparent);
    display: flex; align-items: center; gap: 9px; flex-wrap: wrap; justify-content: center;
}
body:not(.tabletop-mode) .tb-bottom .tb-sub {
    font-size: calc(14px * var(--ts, 1)); letter-spacing: 1.2px; font-weight: 800;
    color: rgba(255,255,255,.72); text-transform: uppercase;
}
/* Your own turn earns the emphasis; somebody else's stays informational. */
body:not(.tabletop-mode) .tb-bottom.tb-mine .tb-sub {
    color: var(--tb-col, #fff); text-shadow: 0 0 16px var(--tb-col, transparent);
}
body:not(.tabletop-mode) #turn-banner.tb-show .tb-bottom.tb-mine {
    animation: tbPop .42s cubic-bezier(.2,1.5,.4,1);
}
@keyframes tbPop {
    0%   { transform: translate(-50%, -50%) scale(.86); }
    62%  { transform: translate(-50%, -50%) scale(1.05); }
    100% { transform: translate(-50%, -50%) scale(1); }
}

/* YOU — the marker that says which of these players you are driving. Small on
   purpose: it appears beside a name that is already in your seat's colour, so
   it only has to settle the question, not shout it. */
.you-badge, .tb-you {
    display: inline-block; margin-left: 6px; vertical-align: middle;
    font-family: 'Bebas Neue', system-ui, sans-serif;
    font-size: calc(12px * var(--ts, 1)); letter-spacing: 1.1px; line-height: 1;
    padding: 3px 6px; border-radius: 5px;
    background: rgba(255,255,255,.16); color: #fff;
    border: 1px solid rgba(255,255,255,.32);
}
.tb-you { font-size: calc(15px * var(--ts, 1)); padding: 5px 9px; letter-spacing: 1.6px; }
.rival-chip .you-badge { font-size: calc(12px * var(--ts, 1)); padding: 2px 5px; }

.tb-ic { font-size: calc(26px * var(--ts, 1)); line-height: 1; }
.tb-txt { display: flex; flex-direction: column; gap: 1px; }
.tb-name {
    font-size: calc(19px * var(--ts, 1)); letter-spacing: 1.4px; line-height: 1;
    color: var(--tb-col, #fff);
}
.tb-sub {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1));
    letter-spacing: .6px; color: rgba(255,255,255,.6); line-height: 1.1;
}

/* ============================================================
   CHARACTER PICKER — the real 3D piece, not an emoji
   ============================================================
   The portrait replaces the emoji rather than sitting next to it: the emoji is
   kept in the DOM as the fallback for a browser that gives us no WebGL
   context, and hidden as soon as a real render lands. */
.char-shot {
    display: block; width: 100%; max-width: 84px; height: auto;
    margin: 0 auto 2px; aspect-ratio: 1 / 1; object-fit: contain;
    filter: drop-shadow(0 4px 10px rgba(0,0,0,.55));
    pointer-events: none;
}
.char-card.has-shot .char-icon { display: none; }
.char-card .char-name { font-family: 'Nunito', system-ui, sans-serif; font-weight: 800; }
/* Player 2 picking: mark what Player 1 already took. Not disabled — two players
   may want the same piece, and they are drawn in different colours anyway. */
.char-card.taken { opacity: .55; }
.char-card.taken::after {
    content: 'P1'; position: absolute; top: 5px; right: 7px;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    letter-spacing: .5px; color: var(--p1); opacity: .9;
}
.char-card { position: relative; }

/* ============================================================
   ALLY ARRIVAL — the card that holds the minigame back
   ============================================================
   An ally lands at the close of a round, which is the same moment the minigame
   takes the screen. This waits for a press, so the player gets to see where it
   is before the board disappears. */
#ally-arrival {
    position: fixed; inset: 0; z-index: 190;
    display: none; align-items: flex-end; justify-content: center;
    /* Barely dimmed and anchored to an edge on purpose: the whole point of this
       card is that the camera has just swooped to the ally and you can SEE
       where it landed. A centred card over a blurred board would show you the
       card instead of the thing it is about. */
    background: rgba(4, 3, 10, .28);
    padding: 16px 16px calc(var(--hud-h) + 18px);
}
.tabletop-p2-turn #ally-arrival {
    align-items: flex-start;
    padding: calc(var(--hud-h) + 18px) 16px 16px;
}
/* Tabletop draws it twice, so the pair has to share one screen. */
#ally-arrival.dual-mode { flex-direction: column; gap: 8px; padding: 8px 14px; justify-content: center; align-items: center; }
.aa-card {
    width: 100%; max-width: 380px;
    display: flex; flex-direction: column; align-items: center; gap: 5px;
    background: var(--panel); border: 1.5px solid rgba(251, 191, 36, .6);
    border-radius: 20px; padding: 16px 18px 16px;
    box-shadow: 0 18px 50px rgba(0,0,0,.75), 0 0 34px -12px rgba(251,191,36,.6);
    text-align: center;
}
#ally-arrival.dual-mode .aa-card,
#ally-arrival.dual-mode .dual-mirror { max-height: 45vh; padding: 10px 14px; gap: 3px; }
.aa-tag {
    font-size: calc(12px * var(--ts, 1)); letter-spacing: 1.6px; color: rgba(251,191,36,.85);
}
.aa-icon { font-size: calc(40px * var(--ts, 1)); line-height: 1.1; }
.aa-name { font-size: calc(25px * var(--ts, 1)); letter-spacing: 1.6px; color: #fff; line-height: 1.05; }
.aa-power {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12.5px * var(--ts, 1)); line-height: 1.4;
    color: rgba(255,255,255,.78);
}
.aa-where {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    color: var(--gold); margin-top: 2px;
}
.aa-race {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.35;
    color: rgba(255,255,255,.5); margin-bottom: 4px;
}
/* How long the board buddy waits. Its own line, in warning colour, because it
   is the only part of the card that changes between rounds. */
.aa-clock {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    color: #fca5a5; letter-spacing: .2px;
}
/* What each player is already holding, and the clock on it. */
.aa-held {
    width: 100%; display: flex; flex-direction: column; gap: 4px;
    margin: 6px 0 8px; padding-top: 8px;
    border-top: 1px solid rgba(255,255,255,.12);
}
.aa-held-row {
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
    justify-content: center;
}
.aa-held-who {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 800;
    color: rgba(255,255,255,.55); letter-spacing: .4px;
}
.aa-chip {
    display: inline-flex; align-items: center; gap: 5px;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); font-weight: 700;
    color: rgba(255,255,255,.9);
    background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.14);
    border-radius: 999px; padding: 2px 8px;
}
.aa-chip b { color: var(--gold); font-weight: 800; font-size: calc(12px * var(--ts, 1)); }
#ally-arrival.dual-mode .aa-clock { font-size: calc(11px * var(--ts, 1)); }
#ally-arrival.dual-mode .aa-held  { margin: 4px 0 6px; padding-top: 5px; gap: 2px; }
#ally-arrival.dual-mode .aa-chip  { font-size: calc(11px * var(--ts, 1)); padding: 1px 6px; }
#ally-arrival.dual-mode .aa-icon { font-size: calc(26px * var(--ts, 1)); }
#ally-arrival.dual-mode .aa-name { font-size: calc(18px * var(--ts, 1)); }
#ally-arrival.dual-mode .aa-power { font-size: calc(11px * var(--ts, 1)); }
#ally-arrival.dual-mode .aa-where { font-size: calc(11px * var(--ts, 1)); }
#ally-arrival.dual-mode .aa-race  { font-size: calc(11px * var(--ts, 1)); }
#ally-arrival .btn-close { width: 100%; }

/* District copy — the tagline says what the place IS, the lore says what it is
   like to be there. Both live in DISTRICT_BIOMES with the colours and props, so
   the writing and the place cannot drift apart. */
.cb-lore {
    display: block; margin-top: 3px;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.4;
    color: rgba(255,255,255,.55); font-style: italic;
}
#realm-banner .rb-lore {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.4;
    color: rgba(255,255,255,.62); max-width: 300px; margin-top: 4px;
    text-align: center;
}

/* ============================================================
   ROLL CALLOUT — the number, at the size it deserves
   ============================================================
   The roll used to be one line of toast text on the rail, the same weight as
   "+3 coins" and "Passed the Grand Mall". It is not the same weight: it is the
   thing the whole turn is about, and the token deliberately does not move for
   DICE_READ (1500ms) after the dice settle. That beat now belongs to it.

   Centred, because nothing else is on screen during it and it is gone before
   the token takes a step — the rule that keeps notifications off the board
   applies to things that fire WHILE the board is moving. */
#roll-callout {
    position: fixed; inset: 0; z-index: 240;
    display: none; align-items: center; justify-content: center;
    pointer-events: none;
}
#roll-callout.up { display: flex; }
.rc-card {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 14px 40px 18px; border-radius: 28px;
    background: radial-gradient(ellipse at center, rgba(8,6,18,.86) 45%, rgba(8,6,18,0) 76%);
    animation: rc-pop .34s cubic-bezier(.2,1.4,.4,1) both;
}
.tabletop-p2-turn .rc-card { transform: rotate(180deg); }
.rc-cap {
    font-size: calc(15px * var(--ts, 1)); letter-spacing: 4px; color: rgba(255,255,255,.8);
    text-shadow: 0 2px 12px rgba(0,0,0,.95);
}
.rc-num {
    font-size: calc(132px * var(--ts, 1)); line-height: .88; color: var(--gold);
    letter-spacing: 2px;
    text-shadow: 0 6px 26px rgba(0,0,0,.95);
    filter: drop-shadow(0 0 34px rgba(251,191,36,.75));
}
/* Pips under the number, so it reads as a die face and not just a digit. */
.rc-pips { display: flex; gap: 7px; margin-top: 6px; }
.rc-pips i {
    width: 11px; height: 11px; border-radius: 50%;
    background: var(--gold); display: block;
    box-shadow: 0 0 12px rgba(251,191,36,.9);
}
@keyframes rc-pop {
    0%   { opacity: 0; transform: scale(.55); }
    60%  { opacity: 1; transform: scale(1.06); }
    100% { opacity: 1; transform: scale(1); }
}
.tabletop-p2-turn .rc-card { animation-name: rc-pop-flip; }
@keyframes rc-pop-flip {
    0%   { opacity: 0; transform: rotate(180deg) scale(.55); }
    60%  { opacity: 1; transform: rotate(180deg) scale(1.06); }
    100% { opacity: 1; transform: rotate(180deg) scale(1); }
}
#roll-callout.out .rc-card { animation: rc-out .22s ease-in both; }
@keyframes rc-out { to { opacity: 0; transform: scale(1.15); } }

/* ============================================================
   FINAL ROUND — the one beat where the plan changes
   ============================================================
   Nothing left to bank for, nothing left to walk to. It used to go by unmarked:
   the round counter ticked over, and it was ticking a round behind anyway. */
#final-round {
    position: fixed; inset: 0; z-index: 1600;
    display: none; flex-direction: column; align-items: center; justify-content: center;
    gap: 10px; pointer-events: none;
    background: radial-gradient(ellipse at center, rgba(60,6,6,.5) 0%, rgba(4,3,10,.34) 55%, rgba(4,3,10,0) 80%);
    animation: fr-in .45s ease both;
}
#final-round > * {
    display: flex; flex-direction: column; align-items: center;
    text-align: center; padding: 0 22px;
}
/* In tabletop DualRead.dualHTML() emits TWO copies, one turned for the far
   player. Centring both in a column stacked them into each other — this pushes
   them to their own halves and takes the type down to fit. The `.mirror` copy
   carries its own rotation from DualRead. */
#final-round:has(.dual-cap) { justify-content: space-evenly; }
#final-round .dual-cap .fr-name { font-size: calc(42px * var(--ts, 1)); }
#final-round .dual-cap .fr-tag  { font-size: calc(12px * var(--ts, 1)); letter-spacing: 4px; }
#final-round .dual-cap .fr-sub  { font-size: calc(12px * var(--ts, 1)); max-width: 260px; }
.fr-tag {
    font-size: calc(14px * var(--ts, 1)); letter-spacing: 5px; color: #fca5a5;
    text-shadow: 0 2px 12px rgba(0,0,0,.95);
}
.fr-name {
    font-size: calc(58px * var(--ts, 1)); line-height: 1; letter-spacing: 3px; color: #fff;
    text-shadow: 0 4px 24px rgba(0,0,0,.95);
    filter: drop-shadow(0 0 30px rgba(239,68,68,.75));
    animation: fr-pulse 1.5s ease-in-out infinite;
}
.fr-sub {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12.5px * var(--ts, 1)); line-height: 1.45;
    font-weight: 700; color: rgba(255,255,255,.82); max-width: 290px; margin-top: 5px;
    text-shadow: 0 2px 10px rgba(0,0,0,.95);
}
@keyframes fr-in {
    0%   { opacity: 0; }
    100% { opacity: 1; }
}
@keyframes fr-pulse {
    0%, 100% { filter: drop-shadow(0 0 30px rgba(239,68,68,.75)); }
    50%      { filter: drop-shadow(0 0 46px rgba(239,68,68,1)); }
}


/* Three or four result cards do not fit the two-across row the win screen was
   built for; they scroll in a column instead, ranked best first. */
.win-cards.win-cards-many { flex-wrap: wrap; overflow-y: auto; max-height: 46vh; align-content: flex-start; }
.win-cards.win-cards-many .win-card { flex: 1 1 46%; min-width: 200px; }

/* ============================================================
   ONLINE LOBBY
   ============================================================
   One screen, two phases. `data-phase` on #lobby switches between picking a
   role (host / join) and the room itself, so there is no second overlay to
   keep in sync and no way for both to be on screen at once. */
.sbtn-online { border-color: rgba(74,222,128,.55); box-shadow: 0 0 18px rgba(74,222,128,.16); }

#lobby {
    position: fixed; inset: 0; z-index: 120;
    display: flex; align-items: center; justify-content: center;
    background: radial-gradient(circle at 50% 20%, #241a4d 0%, #140c2b 70%);
    padding: 14px; overflow-y: auto;
}
.lobby-sheet {
    width: 100%; max-width: 440px;
    background: var(--panel); border: 1px solid var(--border); border-radius: 18px;
    padding: 14px 16px 18px; display: flex; flex-direction: column; gap: 10px;
}
.lobby-head { display: flex; align-items: center; justify-content: space-between; }
.lobby-title { font-size: calc(22px * var(--ts, 1)); color: var(--gold); letter-spacing: 1px; }
.lobby-back {
    background: rgba(255,255,255,.08); border: 1px solid var(--border); color: #fff;
    border-radius: 9px; padding: 6px 10px; font-size: calc(13px * var(--ts, 1)); width: 64px;
}
.lobby-label { font-size: calc(13px * var(--ts, 1)); letter-spacing: 1.4px; color: rgba(255,255,255,.62); margin-top: 4px; }
.lobby-input {
    width: 100%; padding: 12px 14px; border-radius: 11px;
    background: rgba(255,255,255,.07); border: 1px solid var(--border);
    color: #fff; font-family: 'Nunito', system-ui, sans-serif; font-size: calc(16px * var(--ts, 1)); font-weight: 700;
}
.lobby-input:focus { outline: none; border-color: var(--gold); }
.lobby-code-entry { text-align: center; font-size: calc(30px * var(--ts, 1)); letter-spacing: 12px; text-indent: 12px; }
.lobby-cta { width: 100%; margin: 0; }
/* Two doors, each a card of its own, so HOST and JOIN read as a choice
   between two things rather than as one form with a button halfway down. */
.lobby-pick { gap: 12px; }
.lobby-door {
    display: flex; flex-direction: column; gap: 8px;
    padding: 14px; border-radius: 14px;
    background: rgba(255,255,255,.045); border: 1px solid rgba(255,255,255,.11);
}
.lobby-door-cta { margin: 0; }
.lobby-door-note { font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.45); text-align: center; }
.lobby-or {
    display: flex; align-items: center; gap: 10px;
    font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.38); letter-spacing: 2px; margin: 0;
}
.lobby-or::before, .lobby-or::after {
    content: ''; flex: 1; height: 1px; background: rgba(255,255,255,.12);
}
/* Naming yourself, in the room, above the roster you are joining. */
.lobby-name-row { display: flex; flex-direction: column; gap: 5px; }
.lobby-name-row .lobby-label { margin-top: 0; }

/* Phase switch */
#lobby[data-phase="pick"] .lobby-room { display: none; }
#lobby[data-phase="room"] .lobby-pick { display: none; }
.lobby-pick, .lobby-room { display: flex; flex-direction: column; gap: 9px; }

.lobby-code-row { display: flex; align-items: center; gap: 10px; }
.lobby-code {
    flex: 1; text-align: center; font-size: calc(40px * var(--ts, 1)); letter-spacing: 9px; text-indent: 9px;
    color: var(--gold); text-shadow: 0 0 18px rgba(245,200,66,.45);
    background: rgba(245,200,66,.08); border: 1px solid rgba(245,200,66,.32);
    border-radius: 12px; padding: 6px 4px;
}
.lobby-share {
    flex: 0 0 auto; background: rgba(255,255,255,.09); border: 1px solid var(--border);
    color: #fff; border-radius: 11px; padding: 11px 12px; font-size: calc(12px * var(--ts, 1)); letter-spacing: .6px;
}
.lobby-share-note { font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.5); text-align: center; }

/* One row per seat, in that seat's colour, plus the empty ones. */
.lobby-seats { display: flex; flex-direction: column; gap: 6px; }
.lobby-seat {
    display: grid; grid-template-columns: 22px 1fr auto auto; align-items: center; gap: 8px;
    padding: 8px 10px; border-radius: 10px;
    background: rgba(255,255,255,.045); border: 1px solid rgba(255,255,255,.1);
    border-left: 4px solid var(--ls, rgba(255,255,255,.2));
    font-size: calc(13px * var(--ts, 1)); font-weight: 700; color: rgba(255,255,255,.85);
}
.lobby-seat.ls-mine  { background: rgba(255,255,255,.1); }
.lobby-seat.ls-ready { border-color: rgba(74,222,128,.5); }
.lobby-seat.ls-empty { opacity: .38; border-left-color: rgba(255,255,255,.14); font-weight: 600; font-style: italic; }
.ls-num   { color: rgba(255,255,255,.4); font-size: calc(12px * var(--ts, 1)); }
.ls-name  { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.ls-char  { font-size: calc(12px * var(--ts, 1)); opacity: .85; white-space: nowrap; }
.ls-char.ls-none { opacity: .4; font-style: italic; }
.ls-state { font-size: calc(12px * var(--ts, 1)); letter-spacing: .5px; color: rgba(74,222,128,.9); white-space: nowrap; }
.lobby-seat:not(.ls-ready) .ls-state { color: rgba(255,255,255,.3); }
.ls-tag {
    font-size: calc(12px * var(--ts, 1)); letter-spacing: .8px; padding: 1px 5px; border-radius: 5px;
    background: rgba(255,255,255,.14); color: rgba(255,255,255,.75);
}
.ls-tag.ls-you { background: rgba(245,200,66,.24); color: var(--gold); }
.ls-tag.ls-bot { background: rgba(168,85,247,.26); color: #c99cff; }
.ls-tag.ls-gone { background: rgba(239,68,68,.24); color: #ff9d9d; }

.lobby-chars { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.lobby-char {
    display: flex; flex-direction: column; align-items: center; gap: 2px;
    padding: 8px 4px; border-radius: 10px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
    color: #fff; font-family: 'Nunito', system-ui, sans-serif;
}
.lobby-char .lc-ic { font-size: calc(22px * var(--ts, 1)); }
/* The real mesh, rendered in this seat's colour. Sized so nine of them still
   fit on a phone without scrolling the ready button off the bottom. */
.lobby-char .lc-shot {
    width: 100%; max-width: 62px; aspect-ratio: 1; object-fit: contain;
    display: block; filter: drop-shadow(0 3px 5px rgba(0,0,0,.45));
}
.lobby-char .lc-nm { font-size: calc(12px * var(--ts, 1)); font-weight: 700; opacity: .8; }
/* The chip is always in the DOM (the cards are patched, never rebuilt), so it
   has to take up no room at all when the character is free. */
.lobby-char .lc-by:empty { display: none; }
.lobby-char .lc-by {
    font-size: calc(12px * var(--ts, 1)); letter-spacing: .4px; padding: 1px 5px; border-radius: 5px; margin-top: 1px;
    background: rgba(255,255,255,.13); color: rgba(255,255,255,.7);
    max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lobby-char.sel   { border-color: var(--gold); background: rgba(245,200,66,.15); }
.lobby-char.sel .lc-shot { filter: drop-shadow(0 0 9px rgba(245,200,66,.6)); }
/* Taken is dimmed, not invisible — you still need to read whose it is. */
.lobby-char.taken { opacity: .45; }
.lobby-char.taken .lc-shot { filter: grayscale(1) brightness(.7); }
.lobby-char-hint  { font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.45); text-align: center; }

.lobby-actions { display: flex; gap: 8px; }
.lobby-actions .btn-primary {
    flex: 1; margin: 0; font-size: calc(15px * var(--ts, 1)); padding: 13px 8px;
    line-height: 1.15; white-space: nowrap;
}
#btn-lobby-ready.is-ready { background: rgba(74,222,128,.22); border-color: rgba(74,222,128,.6); }
.lobby-status { font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.55); text-align: center; min-height: 16px; }
.lobby-status.is-error { color: #ff9d9d; }

/* Watching somebody else's turn. A quiet strip rather than a card: it is
   status, not an event, and it sits for as long as their turn takes. Pinned
   just above the (empty) action row so it never covers the board's middle. */
#net-waiting {
    position: absolute; left: 50%; transform: translateX(-50%);
    bottom: calc(var(--hud-h) + 18px);
    display: none; align-items: center; gap: 8px;
    padding: 8px 16px; border-radius: 999px;
    background: rgba(8, 6, 18, .82);
    border: 1px solid color-mix(in srgb, var(--nw, #fff) 45%, transparent);
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(13px * var(--ts, 1)); font-weight: 800;
    color: rgba(255,255,255,.88); white-space: nowrap;
    pointer-events: none; z-index: 40;
}
#net-waiting .nw-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--nw, #fff); box-shadow: 0 0 10px var(--nw, #fff);
    animation: nwPulse 1.5s ease-in-out infinite;
}
@keyframes nwPulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }

/* The briefing's START is a ready vote online: pressed, it says so and waits
   for the rest of the table rather than going quiet. */
.cb-actions .cb-go.cb-waiting {
    opacity: .78; cursor: default;
    background: rgba(74,222,128,.18);
    border-color: rgba(74,222,128,.55);
    color: #b9f6cd;
}

/* ============================================================
   A ROUND PLAYED ON EVERY PHONE AT ONCE
   ============================================================
   The split-screen minigame chrome (#minigame-layer) is two players sharing
   one device. These are the screens around a game played alone on four of
   them: the card that announces it, and the scoreboard that ends it. */
#solo-layer {
    position: fixed; inset: 0; z-index: 260;
    display: flex; align-items: center; justify-content: center;
    background: radial-gradient(circle at 50% 35%, #1d1b3a 0%, #0d0c1c 70%);
    padding: 22px;
}
.solo-card, .solo-board {
    width: 100%; max-width: 420px; text-align: center;
    display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.solo-kicker {
    font-size: calc(13px * var(--ts, 1)); letter-spacing: 3px; color: var(--gold);
    text-shadow: 0 0 14px rgba(245,200,66,.5);
}
.solo-icon { font-size: calc(58px * var(--ts, 1)); line-height: 1; }
.solo-title { font-size: calc(34px * var(--ts, 1)); letter-spacing: 1.5px; color: #fff; }
.solo-desc {
    font-size: calc(14px * var(--ts, 1)); line-height: 1.5; color: rgba(255,255,255,.72);
    max-height: 30vh; overflow-y: auto;
}
.solo-note { font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.5); min-height: 16px; }
.solo-players { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.solo-players .sp {
    display: flex; align-items: center; gap: 5px;
    padding: 5px 10px; border-radius: 999px;
    background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.14);
    border-left: 3px solid var(--sp, rgba(255,255,255,.3));
    font-size: calc(12px * var(--ts, 1)); font-weight: 700; color: rgba(255,255,255,.85);
}
.solo-players .sp.is-me { background: rgba(245,200,66,.16); border-color: rgba(245,200,66,.5); }
#btn-solo-go { width: 100%; margin: 6px 0 0; }

/* The scoreboard. One row per player, ordered by score, the winner marked. */
.solo-rows { width: 100%; display: flex; flex-direction: column; gap: 7px; }
.solo-row {
    display: grid; grid-template-columns: 26px 1fr auto; align-items: center; gap: 9px;
    padding: 11px 13px; border-radius: 12px;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.11);
    border-left: 4px solid var(--sp, rgba(255,255,255,.25));
    font-size: calc(14px * var(--ts, 1)); font-weight: 700; color: rgba(255,255,255,.86);
}
.solo-row.is-win {
    background: rgba(245,200,66,.16); border-color: rgba(245,200,66,.55);
    color: #fff;
}
.solo-row .sr-rank { font-size: calc(12px * var(--ts, 1)); color: rgba(255,255,255,.42); }
.solo-row.is-win .sr-rank { color: var(--gold); }
.solo-row .sr-score { font-family: 'Bebas Neue', sans-serif; font-size: calc(21px * var(--ts, 1)); letter-spacing: .5px; }
.solo-row .sr-you {
    font-size: calc(12px * var(--ts, 1)); letter-spacing: .8px; padding: 1px 5px; border-radius: 5px; margin-left: 5px;
    background: rgba(245,200,66,.24); color: var(--gold);
}
.solo-row.is-quiet { opacity: .55; font-style: italic; }


/* ============================================================
   THE ROUND BOARD — three or four players, one minigame
   ============================================================
   Two people sharing a screen can see each other's half, so a two-player round
   needs none of this. Alone on a relay leg, or sitting out two legs of a
   bracket, you cannot tell whether you are winning — and a contest you cannot
   feel is a number announced at the end. */

/* The rail. It sits in the 46px band at the top that the mirrored status strip
   leaves empty whenever one person has the screen to themselves, so the game
   underneath is exactly as big as it was. */
#round-rail {
    position: fixed; top: 0; left: 0; right: 0; z-index: 130;
    height: 46px; display: flex; align-items: center; justify-content: center;
    gap: 6px; padding: 6px 8px 0;
    pointer-events: none;
    padding-top: max(6px, env(safe-area-inset-top));
}
.rr-chip {
    flex: 1 1 0; min-width: 0; max-width: 150px;
    display: flex; align-items: center; justify-content: center; gap: 5px;
    height: 34px; padding: 0 8px; border-radius: 10px;
    background: rgba(8, 6, 18, .78); border: 1px solid rgba(255,255,255,.14);
    border-bottom: 3px solid var(--rr, rgba(255,255,255,.3));
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    font-family: 'Bebas Neue', sans-serif; letter-spacing: 1px;
    color: rgba(255,255,255,.9); font-size: calc(15px * var(--ts, 1));
    overflow: hidden; white-space: nowrap;
}
.rr-chip .rr-who { font-size: calc(14px * var(--ts, 1)); }
.rr-chip .rr-val { font-size: calc(17px * var(--ts, 1)); color: #fff; }
/* Whoever is playing right now. Their number is the screen underneath. */
.rr-chip.rr-now { background: rgba(245,200,66,.2); border-color: rgba(245,200,66,.6); }
.rr-chip.rr-now .rr-val { color: var(--gold); }
/* The mark being chased. */
.rr-chip.rr-lead { border-color: rgba(245,200,66,.5); }
.rr-chip.rr-lead .rr-val { color: var(--gold); text-shadow: 0 0 10px rgba(245,200,66,.5); }

/* (The between-legs card went with the relay and the bracket — a round is
   one game now, with every seat in it, so there is nothing to sit between.) */


/* ============================================================
   THE ARCADE, SORTED — surface, then genre
   ============================================================
   Twenty-two games in a flat grid told you nothing about which of them the
   people in the room could actually play. Two filters now sit above it: the
   SURFACE (a segmented control — one axis, four options, always visible) and
   the GENRE (a scrolling chip row). Ineligible games are greyed with a reason
   rather than hidden, which is the documented practice for context-dependent
   unavailability and doubly right in a testing area, where "why is this not
   here" is the question being asked. */
.mg-seg {
    display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
    margin: 10px 12px 0; padding: 3px; flex-shrink: 0;
    background: rgba(255,255,255,.05); border: 1px solid rgba(255,255,255,.12);
    border-radius: 11px;
}
.mg-seg-btn {
    padding: 9px 2px; border: 0; border-radius: 8px; cursor: pointer;
    background: transparent; color: rgba(255,255,255,.5);
    font-size: calc(15px * var(--ts, 1)); letter-spacing: 1px; transition: background .15s, color .15s;
}
.mg-seg-btn.sel { background: var(--gold); color: #2a1c05; }
.mg-seg-btn:not(.sel):active { background: rgba(255,255,255,.09); }

.mg-chips {
    display: flex; gap: 6px; overflow-x: auto; flex-shrink: 0;
    padding: 10px 12px 2px; scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.mg-chips::-webkit-scrollbar { display: none; }
.mg-chip {
    flex: 0 0 auto; padding: 6px 13px; border-radius: 999px; cursor: pointer;
    border: 1px solid rgba(255,255,255,.16); background: transparent;
    color: rgba(255,255,255,.55); font-family: 'Bebas Neue', sans-serif;
    font-size: calc(14px * var(--ts, 1)); letter-spacing: 1px; white-space: nowrap;
}
.mg-chip.sel { border-color: var(--gold); color: var(--gold); background: rgba(245,200,66,.12); }

.mg-sel-bar {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 14px 2px; flex-shrink: 0;
}
.mg-sel-count {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(13px * var(--ts, 1)); letter-spacing: 1.5px;
    color: rgba(255,255,255,.42);
}
.mg-sort {
    background: rgba(255,255,255,.06); border: 1px solid rgba(255,255,255,.14);
    color: rgba(255,255,255,.7); padding: 5px 11px; border-radius: 8px;
    font-size: calc(13px * var(--ts, 1)); letter-spacing: 1px; cursor: pointer;
}
.mg-sort:active { background: rgba(255,255,255,.14); }

/* The genre, on the card, in the accent — the one authored property a player
   is meant to read. */
.mg-sel-genre {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(11px * var(--ts, 1)); letter-spacing: 1.4px;
    color: var(--gold); opacity: .85;
}
/* Three pips: the whole capability of a game, readable without leaving the
   filter you are in. */
.mg-pips { display: flex; gap: 4px; margin-top: auto; padding-top: 6px; }
.mg-pip {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(11px * var(--ts, 1)); letter-spacing: .5px;
    padding: 2px 5px; border-radius: 4px;
    border: 1px solid rgba(255,255,255,.13); color: rgba(255,255,255,.3);
}
.mg-pip.on-two    { background: rgba(255,90,90,.20);  border-color: #ff5a5a; color: #ffb3b3; }
.mg-pip.on-many   { background: rgba(61,220,132,.18); border-color: #3ddc84; color: #a8f0c8; }
.mg-pip.on-online { background: rgba(107,163,255,.20); border-color: #6ba3ff; color: #b8d2ff; }
/* Why this one is not available under the current filter. */
.mg-sel-why {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(11px * var(--ts, 1)); line-height: 1.3;
    color: rgba(255,255,255,.42); text-align: center;
}
.mg-sel-card.blocked { opacity: .42; }
.mg-sel-card.blocked .mg-sel-name { color: rgba(255,255,255,.6); }
.mg-sel-empty {
    grid-column: 1 / -1; text-align: center; padding: 34px 18px;
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(13px * var(--ts, 1)); color: rgba(255,255,255,.45);
}
@keyframes mgRandomPick {
    0%   { transform: scale(1); }
    45%  { transform: scale(1.06); box-shadow: 0 0 22px rgba(245,200,66,.55); }
    100% { transform: scale(1); }
}
.mg-sel-card.picked { animation: mgRandomPick .42s ease; }

/* The draw-bag depth under the seat picker. Says what a screen this size can
   actually deal at this seat count, instead of recommending a tablet and
   leaving the player to wonder why. */
.seat-depth {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(12px * var(--ts, 1)); line-height: 1.4;
    color: rgba(255,255,255,.55); margin-top: 8px; min-height: 16px; text-align: center;
}
.seat-depth b { color: var(--gold); font-weight: 800; }

/* The arcade header now carries two filter rows under it, so the title has to
   stay on one line — wrapped, it pushed the first row of cards off a 390px
   screen entirely. */
#mg-select-overlay .mg-sel-title { font-size: calc(21px * var(--ts, 1)); white-space: nowrap; }
#mg-select-overlay .mg-sel-back { flex: 0 0 auto; padding: 7px 12px; font-size: calc(13px * var(--ts, 1)); }
/* Playable at 3-4, but only on a screen big enough for a playfield each. Amber
   rather than green, and the card says so in words — a glyph in a 10px pip is
   not a thing anybody can read. */
.mg-pip.on-many-tablet { background: rgba(245,200,66,.18); border-color: var(--gold); color: #ffe9a8; }
.mg-sel-note {
    font-family: 'Nunito', system-ui, sans-serif; font-size: calc(11px * var(--ts, 1)); line-height: 1.3;
    color: rgba(245,200,66,.75); text-align: center;
}

/* The waiting line under the parallel round's READY button. A player who has
   pressed needs to know the round is held on somebody else rather than broken. */
.solo-wait {
    margin-top: 8px; min-height: 16px;
    font-size: calc(12px * var(--ts, 1)); letter-spacing: .3px;
    color: rgba(255,255,255,.55); text-align: center;
}
#btn-solo-go.is-waiting { opacity: .62; }

/* ============================================================
   CHOOSE YOUR RIVAL
   ============================================================
   One card per rival, because the choice is a COMPARISON: who is close
   enough to be worth fighting, who is carrying enough to be worth taking it
   from. Showing the name alone would make it a coin flip with extra steps, so
   each card carries the two numbers the decision actually turns on and a tag
   naming why this rival stands out. */
.duel-pick-list { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.duel-pick {
    display: grid; grid-template-columns: 40px 1fr auto; align-items: center; gap: 10px;
    width: 100%; padding: 11px 13px; border-radius: 13px; cursor: pointer;
    background: rgba(255,255,255,.05);
    border: 1px solid rgba(255,255,255,.12);
    border-left: 4px solid var(--dp, rgba(255,255,255,.3));
    font-family: 'Nunito', system-ui, sans-serif; text-align: left;
    transition: transform .12s ease, background .12s ease, border-color .12s ease;
}
.duel-pick:active { transform: scale(.975); }
.duel-pick.is-hot {
    background: rgba(239,68,68,.16);
    border-color: rgba(239,68,68,.6);
}
.duel-pick .dp-face { font-size: calc(27px * var(--ts, 1)); line-height: 1; text-align: center; }
.duel-pick .dp-name {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(19px * var(--ts, 1)); letter-spacing: .5px;
    color: #fff; line-height: 1.1;
}
.duel-pick .dp-tag {
    display: block; font-family: 'Nunito', sans-serif; font-size: calc(12px * var(--ts, 1));
    color: rgba(255,255,255,.55); letter-spacing: .2px;
}
.duel-pick .dp-coins {
    font-family: 'Bebas Neue', sans-serif; font-size: calc(20px * var(--ts, 1)); color: var(--gold);
    white-space: nowrap;
}
/* The bot's pick lands as a highlight before the screen moves on, so a choice
   made by the computer still reads as a choice rather than a jump cut. */
@keyframes duelPickChosen {
    0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(239,68,68,.55); }
    45%  { transform: scale(1.04); box-shadow: 0 0 0 12px rgba(239,68,68,0); }
    100% { transform: scale(1);    box-shadow: 0 0 0 0 rgba(239,68,68,0); }
}
.duel-pick.is-chosen { animation: duelPickChosen .62s ease both; }

/* ── The SIDE-ON hold (MG_ORIENTATIONS.sideon) ─────────────────────────────
   Two players side by side along one long edge, the phone's home edge on their
   right. The game turns its own stage (Stage.js); this turns the manager's
   chrome to match. `rotate` is the individual transform property, so it
   composes with countPop's `transform` and the P2 zone's 180° instead of
   replacing them. The status pills sit on the short edges, sideways to both
   players, so a side-on game draws its prompts in its own HUD instead. */
#minigame-layer.is-sideon #mg-countdown { rotate: 90deg; }
#minigame-layer.is-sideon #mg-ready-1   { rotate: 90deg; }
#minigame-layer.is-sideon #mg-ready-2   { rotate: -90deg; }
#minigame-layer.is-sideon #mg-neutral,
#minigame-layer.is-sideon #mg-neutral-mirror { visibility: hidden; }
/* The upright hold (pass & play) is one reader at a time, the right way up:
   no mirrored pill for a player at the far end, and the game's own bar says
   whose turn it is, so neither pill sits over its controls. */
#minigame-layer.is-upright #mg-neutral,
#minigame-layer.is-upright #mg-neutral-mirror { visibility: hidden; }

/* The countdown is moved into #minigame-layer when it starts, and the layer is
   a flex column with no centring — so for every game "3 · 2 · 1 · GO" drew in
   the top-left corner, half of it in P2's zone. Pin it to the middle with the
   individual `translate` property, which leaves countPop's `transform` alone. */
#minigame-layer > #mg-countdown { top: 50%; left: 50%; translate: -50% -50%; }

/* ---- Pause (RELEASE_AUDIT RA-04) ----
   The button sits opposite the rules button, above the swipe zone (15) for the
   same reason. The overlay is below Settings / Rules / How to play (1500) so
   those open on top of it and close back to it. */
#btn-pause {
    position: absolute; top: calc(var(--hud-h) + 10px); left: 10px; z-index: 16;
    width: 44px; height: 44px; border-radius: 50%; pointer-events: auto; cursor: pointer;
    background: rgba(18,14,38,0.92); border: 1px solid var(--border); color: rgba(255,255,255,0.85);
    font-size: calc(17px * var(--ts, 1)); box-shadow: 0 4px 12px rgba(0,0,0,0.5);
}
#btn-pause:active { background: rgba(245,200,66,0.2); border-color: var(--gold); }
.tabletop-p2-turn #btn-pause { transform: rotate(180deg); top: auto; bottom: calc(var(--hud-h) + 10px); left: auto; right: 62px; }
#pause-overlay { z-index: 1400; }
.pause-panel { display: flex; flex-direction: column; gap: 10px; align-items: stretch; max-width: 340px; width: 100%; padding: 22px 18px; }
.pause-panel .ob-title { text-align: center; font-size: calc(30px * var(--ts, 1)); }
.pause-sub { text-align: center; font-size: calc(14px * var(--ts, 1)); color: rgba(255,255,255,.72); margin: -4px 0 6px; }
.pause-btn { min-height: 48px; font-size: calc(16px * var(--ts, 1)); }
.pause-confirm { display: flex; flex-direction: column; gap: 10px; text-align: center; font-size: calc(14px * var(--ts, 1)); color: rgba(255,255,255,.85); padding-top: 6px; border-top: 1px solid rgba(255,255,255,.12); }
.pause-row { display: flex; gap: 10px; }
.pause-row > * { flex: 1; }
.ob-btn-danger { background: #dc2626; color: #fff; border: none; }

/* The briefing's buttons stay on screen. They were at the end of a scrolling
   sheet — below the fold on every phone measured, iPhone SE to Pro Max, with
   nothing to say the sheet scrolled (RELEASE_AUDIT UX-01). */
.cb-actions {
    position: sticky; bottom: -16px; z-index: 2;
    margin: 4px -16px -16px; padding: 12px 16px 16px;
    background: linear-gradient(to bottom, rgba(20,16,40,0), var(--panel) 18px);
}
#city-briefing.cb-compact .cb-lore,
#city-briefing.cb-compact .cb-hq { display: none; }

/* ---- Safe areas (RELEASE_AUDIT T-04) ----
   In an edge-to-edge app the notch / Dynamic Island covers the top rival bar
   and the home indicator sits on the P1 bar and ROLL. The whole HUD layer is
   inset instead of each bar, because every HUD element is positioned from
   var(--hud-h) inside it; the inset strips are painted the bar colour so the
   board does not show through behind the status bar. No-op where there is no
   inset (desktop, most Android). */
#ui-layer {
    top: env(safe-area-inset-top, 0px); bottom: env(safe-area-inset-bottom, 0px);
    left: env(safe-area-inset-left, 0px); right: env(safe-area-inset-right, 0px);
}
#ui-layer::before, #ui-layer::after {
    content: ''; position: absolute; left: calc(-1 * env(safe-area-inset-left, 0px));
    right: calc(-1 * env(safe-area-inset-right, 0px)); background: var(--panel); pointer-events: none;
}
#ui-layer::before { top: calc(-1 * env(safe-area-inset-top, 0px)); height: env(safe-area-inset-top, 0px); }
#ui-layer::after  { bottom: calc(-1 * env(safe-area-inset-bottom, 0px)); height: env(safe-area-inset-bottom, 0px); }
.fs-overlay, #city-briefing, #win-screen {
    padding-top: max(14px, env(safe-area-inset-top, 0px));
    padding-bottom: max(14px, env(safe-area-inset-bottom, 0px));
}
.ob-nav .ob-btn { white-space: nowrap; }   /* LET'S GO ✓ wrapped to two lines at 390 px */
/* The hint sits high in the swipe zone, clear of the token the follow camera
   puts in the lower half (RELEASE_AUDIT G-03). */
#swipe-zone { justify-content: flex-start; }

/* Settings additions: text size, battery saver, music, privacy (RELEASE_AUDIT A-03, RA-03, VA-01, RA-02) */
.set-row-stack { flex-direction: column; align-items: stretch; gap: 8px; }
.set-seg { display: flex; gap: 6px; }
.set-seg-btn { flex: 1; min-height: 44px; border-radius: 12px; border: 1px solid rgba(255,255,255,.28); background: rgba(255,255,255,.06); color: #fff; font-family: 'Nunito', system-ui, sans-serif; font-weight: 800; font-size: calc(14px * var(--ts, 1)); cursor: pointer; }
.set-seg-btn.on { background: rgba(245,200,66,.22); border-color: var(--gold); color: var(--gold); }
.set-note { display: block; font-size: calc(12px * var(--ts, 1)); opacity: .65; font-weight: 600; }
.set-link { display: flex; align-items: center; justify-content: center; text-decoration: none; color: #fff; }

/* UX-05: the rival bar at 390 px wrapped its name, badge and district to two
   lines each. One line, ellipsised. */
.hud-info { min-width: 0; flex: 1 1 auto; }
.hud-name, .hud-pos-badge, .hud-turn-badge { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hud-name { max-width: 100%; }
.hud-pos-badge { max-width: 118px; }
/* UX-06: the bounty strip clipped its last pill off the right edge, and with
   pointer-events off it could not even be scrolled. It wraps instead, clear of
   the ⏸ and ❓ buttons at either side. */
#contracts-strip { flex-wrap: wrap; justify-content: center; max-width: calc(100vw - 120px); overflow: visible; row-gap: 4px; }
/* VA-05: prompts dimmed the board to near-black; a lighter scrim keeps where
   you are in view behind a yes/no question. */
#modal-overlay { background: radial-gradient(ellipse at 50% 38%, rgba(28,8,58,0.72), rgba(0,0,0,0.80)) !important; }
/* UX-08: on a 667-high phone the title was clipped and HOW TO PLAY / SETTINGS
   sat below the fold. */
@media (max-height: 700px) {
    .stitle { font-size: calc(44px * var(--ts, 1)); margin-bottom: 6px; }
    .sbtn { padding-top: 9px !important; padding-bottom: 9px !important; margin-bottom: 7px !important; }
}
@media (max-height: 430px) {
    .stitle { font-size: calc(30px * var(--ts, 1)); }
    .sbtn { padding-top: 6px !important; padding-bottom: 6px !important; margin-bottom: 5px !important; }
}
/* A-04: every board-side control is at least 44 × 44 (CONTINUE, ENTER SHOP and
   LEAVE SHOP were 42 tall; ✕ was 34; ROTATE was 31 wide). */
#modal-overlay button, .ob-x, #win-screen button, #btn-close-map, #btn-junction-map, .cb-actions button { min-height: 44px; }
.ob-x, #btn-win-rotate { min-width: 44px; }
#btn-resume { background: linear-gradient(135deg, #16a34a, #4ade80); color: #062012; margin-bottom: 10px; }
#btn-resume .resume-sub { font-family: 'Nunito', system-ui, sans-serif; font-size: calc(13px * var(--ts, 1)); font-weight: 800; letter-spacing: .3px; opacity: .85; }
/* Anything that names no family used the browser default — a serif — and a
   <button> never inherits the page font on its own (RETURN TO GAME, the map
   hint). One default for the page, and controls inherit it. */
body { font-family: 'Nunito', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; }
button, input, select, textarea { font-family: inherit; }
/* The title scales with the screen as well as the text-size setting, so a
   wider fallback face can never push HUNDRED off the edges. */
.stitle { font-size: min(calc(60px * var(--ts, 1)), 15vw); }
@media (max-height: 700px) { .stitle { font-size: min(calc(44px * var(--ts, 1)), 13vw); } }
/* C-05: the map sheet was about 45 % of the screen; the board is the point. */
#map-ui .map-panel { padding: 10px 14px; gap: 6px; }
/* The slider's 28 px thumb overhangs its 8 px track; with the tighter gap it
   sat over the second line of the hint. Room above and below it. */
#map-drag-hint { font-size: calc(13px * var(--ts, 1)); line-height: 1.3; text-align: center; color: rgba(255,255,255,.75); }
#map-ui #map-slider { margin: 12px 0 8px; }
#map-ui .map-title { font-size: calc(20px * var(--ts, 1)); }

/* UX-09: a phone on its side has no room for the board — the two HUD bars took
   40 % of the height and the action column ran into both. Say so, over the
   board only: minigames (z-index 100) and modals still draw above this. */
#rotate-hint { display: none; position: fixed; inset: 0; z-index: 95; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
    background: radial-gradient(ellipse at 50% 40%, #241a44, #0b0918); color: #fff; text-align: center; padding: 24px; }
#rotate-hint .rh-phone { font-size: calc(48px * var(--ts, 1)); animation: rhTurn 2.4s ease-in-out infinite; }
#rotate-hint .rh-title { font-size: calc(26px * var(--ts, 1)); letter-spacing: 2px; color: var(--gold); }
#rotate-hint .rh-sub { font-size: calc(14px * var(--ts, 1)); opacity: .8; max-width: 420px; }
@keyframes rhTurn { 0%, 30% { transform: rotate(-90deg); } 60%, 100% { transform: rotate(0deg); } }
body.reduce-motion #rotate-hint .rh-phone { animation: none; }
@media (orientation: landscape) and (max-height: 500px) {
    body.board-on #rotate-hint { display: flex; }
}

/* UX-10: on a tablet the HUD was phone-sized islands in a big screen. */
html.is-tablet { --hud-h: 96px; }
html.is-tablet .btn-action { width: 108px; height: 108px; border-radius: 26px; }
html.is-tablet #btn-rules, html.is-tablet #btn-pause { width: 52px; height: 52px; }
html.is-tablet .inv-slot { width: 30px; height: 30px; }

