/* ════════════════════════════════════════════════════════════════════════
   Card Room theme — a layered reskin of Israeli Whist.

   Loaded AFTER styles.css so these rules win the cascade. Nothing in the
   ~4.7k-line legacy stylesheet is edited; the whole new visual system lives
   here and can be removed by dropping the one <link> in index.html.

   Direction: one accent (brass) on deep felt with ivory cards. Suit red/black
   appear only on cards. Fraunces (display) + Hanken Grotesk (UI). The seat to
   act gets a "turn spotlight". See the before/after review for the rationale.
   ════════════════════════════════════════════════════════════════════════ */

:root {
    --cr-felt:    #1c3a2c;
    --cr-felt-2:  #132419;
    --cr-felt-hi: #274d3b;
    --cr-brass:   #cba552;
    --cr-brass-deep: #a9863a;
    --cr-ivory:   #f0ebdc;
    --cr-ink:     #ece7d6;
    --cr-ink-soft:#aeb7a6;
    --cr-line:    rgba(240,235,220,.14);
    --cr-red:     #c0433a;
    --cr-black:   #20242a;
    --cr-serif: "Fraunces", "Iowan Old Style", Palatino, Georgia, serif;
    --cr-sans:  "Hanken Grotesk", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

    /* The side safe areas, named. Layout that has to fit BETWEEN the notch and
       the home indicator reads these instead of env() directly, so a test can
       stand in for a notched phone -- Chromium has no way to emulate one, and
       the landscape hand is sized against exactly this budget. */
    --sa-t: env(safe-area-inset-top, 0px);
    --sa-b: env(safe-area-inset-bottom, 0px);
    --sa-l: env(safe-area-inset-left, 0px);
    --sa-r: env(safe-area-inset-right, 0px);
}

/* ── 1. Ground + type ──────────────────────────────────────────────────── */
body {
    background: #0d1a14 !important;
    background: radial-gradient(130% 90% at 50% -10%, #17301f 0%, #0d1a14 58%) fixed !important;
    color: var(--cr-ink) !important;
    font-family: var(--cr-sans) !important;
}
.game-container h1 {
    font-family: var(--cr-serif) !important;
    color: var(--cr-brass) !important;
    font-weight: 600 !important;
    letter-spacing: .005em !important;
    text-shadow: none !important;
    animation: none !important;
    font-size: 2.4em !important;
}

/* ── 2. The felt board ─────────────────────────────────────────────────── */
.game-board {
    background:
        radial-gradient(ellipse 70% 60% at 50% 32%, var(--cr-felt-hi) 0%, var(--cr-felt) 52%, var(--cr-felt-2) 100%) !important;
    border: 1px solid rgba(240,235,220,.10) !important;
    border-radius: 18px !important;
    box-shadow: inset 0 2px 40px rgba(0,0,0,.28), inset 0 0 120px rgba(0,0,0,.30),
                0 24px 60px -26px rgba(0,0,0,.7) !important;
}

/* The trick square is a brown "wooden table" left over from the pre-theme
   design (styles.css §.trick-area: saddle-brown radial + gold hatching + a 3px
   #8B4513 border). The reskin only replaced it inside the portrait-phone block,
   so on tablets and desktop an orange slab still sat in the middle of the felt.
   Same treatment everywhere: a faint brass pool where the trick lands. */
.trick-area {
    background: radial-gradient(ellipse at center, rgba(203,165,82,.12), rgba(203,165,82,0) 72%) !important;
    border: 1px solid rgba(203,165,82,.30) !important;
    border-radius: 16px !important;
    box-shadow: none !important;
}

/* ── 3. Status bar: legible labels, trump pip, trick progress ──────────── */
.game-status {
    background: rgba(14,28,21,.82) !important;
    border: 1px solid var(--cr-line) !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: blur(6px) !important;
    backdrop-filter: blur(6px) !important;
    position: relative !important;
    overflow: hidden !important;
}
.game-status *,
.game-status span {
    color: var(--cr-ink) !important;
    text-shadow: none !important;
    font-family: var(--cr-sans) !important;
    letter-spacing: .01em !important;
}
/* Labels ("Round:", "Bid:", "Gamlet:") read as quiet eyebrows; the values pop. */
.tricks-counter, .current-turn, .round-counter { font-weight: 500 !important; color: var(--cr-ink-soft) !important; }
.tricks-counter span, .current-turn span, .round-counter span {
    color: var(--cr-ink) !important; font-weight: 700 !important; font-variant-numeric: tabular-nums;
}
/* Trump becomes an ivory "pip". */
.trump-suit span {
    display: inline-grid !important; place-items: center !important;
    width: 36px !important; height: 36px !important;
    background: var(--cr-ivory) !important; color: var(--cr-black) !important;
    border-radius: 9px !important; font-size: 1.35em !important;
    box-shadow: 0 1px 3px rgba(0,0,0,.35) !important;
}
/* ── 4. Total score box ────────────────────────────────────────────────── */
.total-score-box {
    background: rgba(14,28,21,.9) !important;
    border: 1px solid var(--cr-line) !important;
    border-top: 2px solid var(--cr-brass) !important;
    box-shadow: 0 14px 34px -18px rgba(0,0,0,.7) !important;
}
.total-score-title, #score-view-title { font-family: var(--cr-serif) !important; color: var(--cr-ivory) !important; text-shadow: none !important; font-weight: 600 !important; }
.total-score-content *, .extended-score-content * { color: var(--cr-ink) !important; text-shadow: none !important; font-style: normal !important; }
.total-score-content .score-value, .extended-score-content .score-value { color: var(--cr-brass) !important; font-variant-numeric: tabular-nums; }

/* ── 5. One button system ──────────────────────────────────────────────── */
.action-btn, #start-game-btn, #bid-btn, #deal-btn, .more-btn,
.new-game-btn, .rules-btn, .hint-btn, .last-trick-btn, #pass-btn, .less-btn,
.hint-btn-close, .extended-view-btn {
    font-family: var(--cr-sans) !important;
    font-weight: 600 !important;
    letter-spacing: .01em !important;
    border-radius: 9px !important;
    text-shadow: none !important;
    box-shadow: none !important;
    text-transform: none !important;
    transition: filter .15s ease, background .15s ease, border-color .15s ease !important;
}
/* Primary — the action you take */
.action-btn, #start-game-btn, #bid-btn, #deal-btn, .more-btn {
    background: linear-gradient(180deg, var(--cr-brass), var(--cr-brass-deep)) !important;
    color: #221803 !important;
    border: 1px solid var(--cr-brass-deep) !important;
}
/* Secondary — quiet, outlined */
.new-game-btn, .rules-btn, .hint-btn, .last-trick-btn, #pass-btn, .less-btn, .hint-btn-close {
    background: transparent !important;
    color: var(--cr-brass) !important;
    border: 1px solid color-mix(in oklab, var(--cr-brass) 55%, transparent) !important;
}
.action-btn:hover, #start-game-btn:hover, #bid-btn:hover, #deal-btn:hover, .more-btn:hover,
.new-game-btn:hover, .rules-btn:hover, .hint-btn:hover, .last-trick-btn:hover:not(:disabled),
#pass-btn:hover, .less-btn:hover { filter: brightness(1.08) !important; transform: none !important; }
.last-trick-btn:disabled { opacity: .4 !important; }
.hint-btn[aria-pressed="true"] { background: rgba(203,165,82,.16) !important; }
.extended-view-btn { background: rgba(203,165,82,.16) !important; color: var(--cr-brass) !important; border: 1px solid color-mix(in oklab,var(--cr-brass) 45%,transparent) !important; }
:where(.action-btn,.new-game-btn,.rules-btn,.hint-btn,.last-trick-btn,#bid-btn,#pass-btn,#start-game-btn,#deal-btn,.more-btn,.less-btn,.trick-btn):focus-visible {
    outline: 2px solid var(--cr-brass) !important; outline-offset: 2px !important;
}

/* Turbo → a proper toggle look */
.fast-mode-label {
    color: var(--cr-ink) !important; font-family: var(--cr-sans) !important; font-weight: 600 !important;
    background: rgba(14,28,21,.75) !important; border: 1px solid var(--cr-line) !important;
    border-radius: 999px !important; padding: 6px 12px !important;
}
.fast-mode-text { color: var(--cr-ink) !important; }
.fast-mode-checkbox { accent-color: var(--cr-brass) !important; }

/* ── 6. Trick-number + suit selectors → felt chips with brass selection ── */
.trick-btn, .trick-button, .suit-button {
    background: rgba(240,235,220,.10) !important;
    color: var(--cr-ink) !important;
    border: 1px solid rgba(240,235,220,.16) !important;
    border-radius: 8px !important;
    box-shadow: none !important;
    text-shadow: none !important;
    font-weight: 600 !important;
    transition: background .12s ease, border-color .12s ease, transform .12s ease !important;
}
.trick-btn:hover:not(:disabled), .trick-button:hover, .suit-button:hover {
    background: rgba(240,235,220,.18) !important; border-color: var(--cr-brass) !important; transform: none !important;
}
.trick-btn.selected, .trick-button.selected, .suit-button.selected {
    background: linear-gradient(180deg, var(--cr-brass), var(--cr-brass-deep)) !important;
    color: #221803 !important; border-color: transparent !important;
}
.trick-btn:disabled { opacity: .32 !important; }
/* Suit glyphs keep their real colour on the chip for readability. */
.suit-button[data-value="hearts"], .suit-button[data-value="diamonds"] { color: var(--cr-red) !important; }
.suit-button[data-value="spades"], .suit-button[data-value="clubs"] { color: var(--cr-ink) !important; }
/* Override the legacy per-suit green selected + green hover (specificity 0,3,0). */
.suit-button.selected[data-value="clubs"], .suit-button.selected[data-value="diamonds"],
.suit-button.selected[data-value="hearts"], .suit-button.selected[data-value="spades"],
.suit-button.selected[data-value="notrump"] {
    background: linear-gradient(180deg, var(--cr-brass), var(--cr-brass-deep)) !important;
    color: #221803 !important; border-color: var(--cr-brass-deep) !important; box-shadow: none !important;
}
.suit-button[data-value="clubs"]:hover, .suit-button[data-value="spades"]:hover,
.suit-button[data-value="notrump"]:hover {
    background: rgba(240,235,220,.18) !important; color: var(--cr-ink) !important; border-color: var(--cr-brass) !important;
}
.suit-button[data-value="hearts"]:hover, .suit-button[data-value="diamonds"]:hover {
    background: rgba(240,235,220,.18) !important; color: var(--cr-red) !important; border-color: var(--cr-brass) !important;
}

/* ── 7. Seat plates + turn spotlight (the signature) ───────────────────── */
.player-info {
    background: rgba(12,24,18,.66) !important;
    border: 1px solid var(--cr-line) !important;
    border-radius: 12px !important;
    box-shadow: 0 6px 18px -10px rgba(0,0,0,.6) !important;
    -webkit-backdrop-filter: blur(4px) !important; backdrop-filter: blur(4px) !important;
    transition: box-shadow .25s ease, border-color .25s ease, transform .25s ease !important;
}
.player-name { font-family: var(--cr-sans) !important; font-weight: 650 !important; text-shadow: none !important; }
/* Beat the per-seat coloured names — one ink colour on the plate. */
.north-player .player-name, .east-player .player-name, .south-player .player-name, .west-player .player-name,
#north-player .player-name, #east-player .player-name, #south-player .player-name, #west-player .player-name,
.player-name[data-player] {
    color: var(--cr-ink) !important; text-shadow: none !important;
}
/* nowrap: a squeezed seat plate must never break "Bid: -" across two lines. */
.player-bid, .player-tricks { color: var(--cr-ink-soft) !important; font-weight: 600 !important; white-space: nowrap !important; }
.player-stats { flex-wrap: nowrap !important; }
.player-bid span, .player-tricks span { color: var(--cr-brass) !important; text-shadow: none !important; font-variant-numeric: tabular-nums; }
/* keep the big Phase-1 trump glyph size, drop the glow */
.player-bid span { text-shadow: none !important; }

/* The spotlight: lit ring + felt glow on the acting seat. Class toggled by JS. */
.player.seat-active { z-index: 30 !important; }
.player.seat-active .player-info {
    border-color: var(--cr-brass) !important;
    box-shadow: 0 0 0 2px var(--cr-brass), 0 0 28px 2px rgba(203,165,82,.45) !important;
}
.player.seat-active::after {
    content: ""; position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%);
    width: 230px; height: 150px; border-radius: 50%;
    background: radial-gradient(ellipse, rgba(203,165,82,.22), rgba(203,165,82,0) 70%);
    pointer-events: none; z-index: -1;
}

/* ── 8. Predictions panel + bidding tray: legible, docked look ──────────── */
.bidding-interface, .second-phase-bidding {
    background: linear-gradient(180deg, rgba(20,40,30,.97), rgba(15,30,22,.98)) !important;
    border: 1px solid var(--cr-line) !important;
    border-top: 2px solid var(--cr-brass) !important;
    box-shadow: 0 18px 40px -18px rgba(0,0,0,.8) !important;
    -webkit-backdrop-filter: blur(8px) !important; backdrop-filter: blur(8px) !important;
    color: var(--cr-ink) !important;
}
.bidding-interface h3, .second-phase-bidding h3, .second-phase-bidding h4, .your-prediction h4 {
    color: var(--cr-brass) !important; font-family: var(--cr-sans) !important; text-shadow: none !important; font-weight: 600 !important;
}
/* Kill the green-on-green predictions bug — solid dark, ivory text. */
.predictions-status {
    background: rgba(10,20,15,.6) !important; border: 1px solid var(--cr-line) !important;
}
.predictions-status *, .prediction-item, .prediction-item .player-name {
    color: var(--cr-ink) !important; text-shadow: none !important; opacity: 1 !important; font-family: var(--cr-sans) !important;
}
.prediction-value { color: var(--cr-brass) !important; font-weight: 800 !important; font-variant-numeric: tabular-nums; }
/* The seat name is the tile's label, so it sits back from the number. */
.prediction-item .player-name { color: var(--cr-ink-soft) !important; }
.prediction-total { color: var(--cr-ink-soft) !important; }
.prediction-total #prediction-total { color: var(--cr-ink) !important; }
.trump-reminder { background: rgba(203,165,82,.16) !important; border: 1px solid color-mix(in oklab,var(--cr-brass) 40%,transparent) !important; color: var(--cr-brass) !important; box-shadow: none !important; }
.waiting-message p { color: var(--cr-ink-soft) !important; }

/* ── 9. The hand: all 13 cards visible, never a scroll ─────────────────── */
/* The row is centred and never scrolls. Each card is a known width (--cw, set
   per breakpoint) and gets an overlap margin: min() yields a small positive gap
   when there's room and a negative (overlapping) margin when space is tight, so
   13 cards can never exceed the viewport. Rank/suit sit top-left, so overlapped
   cards stay readable; z-index already increases L→R, so the covered edge is the
   right one. All id-scoped to beat the legacy per-card nth-child rules. */
#south-cards.human-cards {
    --cw: 91px;
    display: flex !important;
    flex-wrap: nowrap !important;
    justify-content: center !important;
    align-items: flex-end !important;
    max-width: 100vw !important;
    padding: 0 12px !important;
    box-sizing: border-box !important;
    overflow: visible !important;
}
#south-cards.human-cards .card {
    flex: 0 0 auto !important;
    box-sizing: border-box !important;   /* --cw is the full outer width incl. border */
    width: var(--cw) !important;
    min-width: var(--cw) !important;
    height: calc(var(--cw) * 1.4) !important;
    margin: 0 !important;
    /* Fit within the board, which is 1000px on desktop and 95vw below the
       1050px breakpoint — it has overflow:hidden, so anything wider is clipped. */
    margin-left: min(6px, calc((min(95vw, 1000px) - 56px - 13 * var(--cw)) / 12)) !important;
}
#south-cards.human-cards .card:first-child { margin-left: 0 !important; }
/* layoutHumanHand() always emits the row break; only the two-row portrait
   layout wants it. Everywhere else the hand is one nowrap row. */
#south-cards .hand-break { display: none; }
/* Lift a card on hover so overlapped indices stay readable. */
#south-cards.human-cards .card:hover { transform: translateY(-14px) !important; z-index: 40 !important; }
@media (max-width: 900px) { #south-cards.human-cards { --cw: 58px; } }
@media (max-width: 560px) { #south-cards.human-cards { --cw: 48px; } }

/* ── 10. Modals + inputs: same card-room palette ───────────────────────── */
.name-content, .rules-content, .hint-content, .last-trick-content {
    background: linear-gradient(180deg, #16281e, #101d16) !important;
    border: 1px solid var(--cr-line) !important;
    border-top: 3px solid var(--cr-brass) !important;
    box-shadow: 0 30px 70px -30px rgba(0,0,0,.85) !important;
}
.name-header h2, .rules-header h2, .hint-header h3, .last-trick-header h3,
.rules-section h3 {
    font-family: var(--cr-serif) !important; color: var(--cr-brass) !important; text-shadow: none !important; font-weight: 600 !important;
}
.rules-header { background: rgba(203,165,82,.10) !important; border-bottom: 1px solid var(--cr-line) !important; }
.rules-header h2, .rules-header .close-btn { color: var(--cr-brass) !important; }
.rules-body, .rules-body p, .rules-body li, .hint-body, .name-body p { color: var(--cr-ink) !important; }
.rules-section li strong, .rules-body strong { color: var(--cr-brass) !important; }
.rules-section { border-bottom: 1px solid var(--cr-line) !important; }
#player-name-input {
    background: rgba(10,20,15,.7) !important; color: var(--cr-ink) !important;
    border: 1px solid var(--cr-line) !important; border-radius: 9px !important;
    box-shadow: none !important; font-family: var(--cr-sans) !important;
}
#player-name-input:focus { border-color: var(--cr-brass) !important; outline: none !important; box-shadow: 0 0 0 2px rgba(203,165,82,.35) !important; }
#player-name-input::placeholder { color: var(--cr-ink-soft) !important; }
.close-btn, .hint-close, .last-trick-close { color: var(--cr-ink-soft) !important; }
.close-btn:hover, .hint-close:hover, .last-trick-close:hover { color: var(--cr-brass) !important; }

/* ── 10b. The ☰ menu: one control instead of five ──────────────────────────
   Turbo + New Game / Rules / Last Trick used to sit in a row above the table;
   on a phone that row cost ~46px of board height and still crowded the status
   bar. They now live in a popover anchored to a single 44px button, so the top
   chrome is two glyphs — Hint in the left corner, ☰ in the right — and the felt
   gets the space back. Hint stays out because it's a mid-play action; making it
   two taps mid-trick is the one place the menu would actually cost something. */
.game-control-buttons { position: absolute; top: 20px; right: 20px; z-index: 1300; display: block; }
.quick-actions { position: absolute; top: 20px; left: 20px; z-index: 1300; display: block; }

.menu-btn {
    display: inline-grid !important; place-items: center !important;
    width: 44px !important; height: 44px !important; padding: 0 !important;
    font-family: var(--cr-sans) !important; font-size: 20px !important; line-height: 1 !important;
    background: rgba(14,28,21,.75) !important; color: var(--cr-brass) !important;
    border: 1px solid color-mix(in oklab, var(--cr-brass) 55%, transparent) !important;
    border-radius: 12px !important; cursor: pointer !important;
    -webkit-backdrop-filter: blur(6px) !important; backdrop-filter: blur(6px) !important;
    transition: background .15s ease, border-color .15s ease !important;
}
.menu-btn:hover { background: rgba(203,165,82,.16) !important; }
/* Hint matches the ☰ corner button on phones (icon only); on wide screens the
   label rides along and it keeps the ordinary secondary-button look. */
.quick-actions .hint-btn {
    display: inline-grid !important; place-items: center !important;
    grid-auto-flow: column !important; gap: 6px !important;
    min-height: 44px !important; line-height: 1 !important;
}
@media (max-width: 760px) { .btn-label { display: none !important; } }
.menu-btn[aria-expanded="true"] { background: rgba(203,165,82,.20) !important; border-color: var(--cr-brass) !important; }
.menu-btn:focus-visible { outline: 2px solid var(--cr-brass) !important; outline-offset: 2px !important; }

.game-menu {
    position: absolute !important; top: calc(100% + 8px) !important; right: 0 !important;
    display: flex; flex-direction: column; gap: 6px;
    min-width: 200px; padding: 8px;
    background: linear-gradient(180deg, #16281e, #101d16) !important;
    border: 1px solid var(--cr-line) !important; border-top: 2px solid var(--cr-brass) !important;
    border-radius: 12px !important;
    box-shadow: 0 26px 60px -24px rgba(0,0,0,.9) !important;
    z-index: 1600 !important;
    animation: cr-menuIn .16s ease-out;
}
@keyframes cr-menuIn { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }

/* Every row is the same full-width, left-aligned item — including Turbo, whose
   original .fast-mode-container was absolutely positioned in the page corner. */
.game-menu > button,
.game-menu .fast-mode-label {
    width: 100% !important; justify-content: flex-start !important;
    min-height: 44px !important; padding: 10px 12px !important;
    font-size: 15px !important; white-space: nowrap !important;
    border-radius: 9px !important; margin: 0 !important;
}
.game-menu .fast-mode-container {
    position: static !important; top: auto !important; left: auto !important; right: auto !important;
    transform: none !important; z-index: auto !important; width: 100% !important;
}
.game-menu .fast-mode-label {
    background: rgba(240,235,220,.06) !important;
    border: 1px solid var(--cr-line) !important; border-radius: 9px !important;
}
.game-menu .fast-mode-label.active { border-color: var(--cr-brass) !important; background: rgba(203,165,82,.16) !important; animation: none !important; }
.game-menu .fast-mode-checkbox { width: 18px !important; height: 18px !important; margin-right: 10px !important; }
.game-menu .fast-mode-text { color: var(--cr-ink) !important; font-size: 15px !important; font-weight: 600 !important; text-shadow: none !important; }

/* Transient "3 ♥ / Pass" bid badges are appended to the board with an inline
   z-index:2000, which drew them straight across the centred bid tray on phones.
   Keep them above the felt but below the tray (1400) and the modals (2000). */
.bid-animation { z-index: 1200 !important; }

/* iPad-class touch screens: styles.css caps the score box at 200px to keep it
   clear of the west nameplate, but that clipped the fourth seat's row off the
   bottom. Four rows plus the header need ~250px, and 260 still ends well above
   the plate (box top 165 + 260 = 425; the plate sits near 525). */
@media (min-width: 700px) and (max-width: 1400px) and (hover: none) {
    .total-score-box { max-height: 260px !important; }
}

/* ── 11. Mobile: portrait is playable, not blocked ─────────────────────── */
/* Retire the hard rotate wall. The board scales to fit and the hand overlaps. */
#rotate-device-overlay { display: none !important; }

@media (max-width: 900px) {
    html, body { overflow-x: hidden !important; }
    .game-container { padding: 10px !important; }
    /* Board fills its container (not 100vw, which ignores padding and shoves the
       centered hand off-axis). Width only — height belongs to the portrait block
       below and, in landscape, to the legacy full-viewport board; setting it here
       clobbered the landscape board (it collapsed to 62vh). */
    .game-board {
        width: 100% !important;
        max-width: 100% !important;
        margin: 6px auto 0 !important;
    }
}
@media (orientation: portrait) and (max-width: 760px) {
    /* Portrait fits ONE screen — no page scroll — as a flex column:
       title → controls → status → compact score → board (fills the rest, with
       the hand pinned at its bottom so the cards are always on-screen). */
    html, body { height: 100% !important; overflow: hidden !important; }
    .game-container {
        display: flex !important; flex-direction: column !important;
        height: 100vh !important; height: 100dvh !important;
        /* Keep chrome + hand clear of the notch and home indicator (installed app,
           viewport-fit=cover). */
        padding:
            calc(6px + var(--sa-t))
            calc(6px + var(--sa-r))
            var(--sa-b)
            calc(6px + var(--sa-l)) !important;
        gap: 3px !important;
        box-sizing: border-box !important; overflow: hidden !important;
    }
    /* Minimal top chrome — give the table the screen. The title is hidden and
       the whole control cluster is one ☰ button (see §10b), so the status row
       can sit at the very top instead of being pushed below a row of buttons;
       only that compact row and a thin score strip sit in flow above the board. */
    .game-container h1 { display: none !important; }

    /* The two corner buttons and the status bar share one 40px band. They used
       to disagree: the buttons were pinned 2px below the safe area while the
       bar, a flow item, started at the container's 6px padding and stood 38px
       tall — so the trump pip and the values rode ~7px below the glyphs. Same
       top edge, same height, one centre line. */
    .game-control-buttons {
        position: absolute !important; z-index: 1300 !important;
        top: calc(var(--sa-t) + 6px) !important; right: calc(var(--sa-r) + 6px) !important;
        transform: none !important; margin: 0 !important;
    }
    .quick-actions {
        position: absolute !important; z-index: 1300 !important;
        top: calc(var(--sa-t) + 6px) !important; left: calc(var(--sa-l) + 6px) !important;
        transform: none !important; margin: 0 !important;
    }
    .menu-btn, .quick-actions .hint-btn {
        width: 40px !important; height: 40px !important; min-height: 40px !important;
        padding: 0 !important; font-size: 19px !important;
    }

    /* Status: one compact row at the top. Symmetric side padding reserves room
       for the ☰ button that floats over its right end and keeps the values
       optically centred. */
    .game-status {
        flex: 0 0 auto !important; order: 1 !important;
        /* Exactly the button height (see above) — not a min-height, which grew
           with the trump pip and pushed the values off the buttons' centre. */
        height: 40px !important; min-height: 40px !important;
        margin: 0 !important; padding: 0 50px !important;
        flex-wrap: nowrap !important; justify-content: center !important;
        align-items: center !important;
    }
    .game-status > div { font-size: .74em !important; min-height: 0 !important; margin: 0 5px !important; }
    .tricks-counter { left: 0 !important; }
    .trump-suit span { width: 24px !important; height: 24px !important; font-size: .95em !important; }

    /* Total Score → a thin one-line strip (no "Total Score" header row). */
    .total-score-box {
        position: static !important; flex: 0 0 auto !important; order: 3 !important;
        width: auto !important; min-width: 0 !important; margin: 0 !important; padding: 2px 8px !important;
    }
    /* (The score strip used to be hidden while a bid panel was open, because the
       panel took its slot in the flex column. The panel is now a fixed overlay
       on the felt, so the strip can stay — and the board no longer resizes
       underneath you when bidding starts.) */
    .total-score-header { display: none !important; }
    #total-score-content {
        display: flex !important; flex-direction: row !important; flex-wrap: nowrap !important;
        justify-content: space-between !important; gap: 0 10px !important; margin: 0 !important;
    }
    .score-item {
        display: inline-flex !important; flex: 0 0 auto !important; width: auto !important;
        align-items: baseline !important; justify-content: flex-start !important;
        gap: 4px !important; margin: 0 !important; padding: 0 !important; font-size: .72em !important; border: none !important;
    }
    .score-item .player-label { margin: 0 !important; }

    /* Board takes the space between the score and the bid tray, and flex-shrinks
       so the tray + hand always fit. min-height keeps the hand fully visible. */
    .game-board { flex: 1 1 auto !important; width: 100% !important; height: auto !important; min-height: 132px !important; margin: 3px 0 0 !important; order: 4 !important; }
    /* Central trick square: modest + faint so it doesn't hide the seat stats, but
       big enough (and overflow:visible) that the four played cards spread into a
       clear cross — including the North card — and may spill just outside it. */
    .trick-area {
        width: 48vw !important; height: 48vw !important; max-width: 188px !important; max-height: 188px !important;
        margin-bottom: 0 !important; overflow: visible !important;
        background: radial-gradient(ellipse at center, rgba(203,165,82,.12), rgba(203,165,82,0) 72%) !important;
        border: 1px solid rgba(203,165,82,.30) !important;
    }
    /* Smaller played cards so all four fit around the centre with little overlap. */
    .played-card, .played-card .card {
        width: 56px !important; height: 78px !important; min-width: 56px !important; min-height: 78px !important;
    }
    .played-card .card { --card-rank-size: 22px !important; }
    .played-card .card .card-rank { font-weight: 800 !important; }
    .played-card .card .card-center-suit { font-size: 22px !important; }
    /* Nudge the N/S cards out to the square's edges so they don't pile on centre. */
    .north-played { top: -6px !important; }
    .south-played { bottom: -6px !important; }
    .west-played { left: -6px !important; }
    .east-played { right: -6px !important; }

    /* Hand on TWO rows. One row of 13 leaves ~22px of each card showing —
       narrower than a fingertip (~45px), which is why players kept grabbing
       the wrong card. layoutHumanHand() breaks the hand down the middle -- 6
       over 7 -- rather than on a suit boundary: --hand-cols is the LONGER of
       the two rows and the card width is whatever makes that row fit, so
       letting the deal's shape pick the break made a 4-4-2-3 hand 8 over 5 and
       shrank every card to fit a row nobody asked for. Even rows keep all
       thirteen as wide as they can be (~52px on a 402px phone). Seven is the
       floor, so a hand played down to its last few cards doesn't inflate
       them. */
    #south-cards.human-cards {
        --cw: min(56px, calc((100vw - 40px) / var(--hand-cols, 7))) !important;
        /* The seat is absolutely positioned, so it shrink-to-fits — and a
           wrapping flex child inside one collapses to its smallest line,
           which put 3 cards on 5 rows. An explicit width makes the wrap
           deliberate: exactly seven per row. (.player centres its children,
           so a full-width hand keeps the nameplate centred.) */
        width: 100vw !important;
        max-width: 100vw !important;
        flex-wrap: wrap !important;
        align-content: flex-end !important;
        align-items: flex-end !important;
        gap: 4px 2px !important;
        padding: 0 6px 4px !important;
        /* §.human-cards pulls the hand up 30px to tuck a single row under the
           nameplate; two rows are tall enough that the pull ran into it. */
        margin-top: 0 !important;
    }
    /* §9's overlap margin has nothing to do here — the cards no longer stack. */
    #south-cards.human-cards .card,
    #south-cards.human-cards .card:first-child { margin-left: 0 !important; }
    /* The row break: a zero-height full-width flex item. Wrapping each row in
       its own element would have broken every `parentElement.id` check in the
       card-gesture code, so the break is a sibling of the cards instead. */
    #south-cards.human-cards .hand-break {
        display: block !important;
        flex: 0 0 100% !important;
        width: 100% !important; height: 0 !important;
        margin: 0 !important; padding: 0 !important;
    }
    #south-cards.human-cards .card { --card-rank-size: 22px !important; }
    #south-cards.human-cards .card .card-rank { font-weight: 800 !important; }
    #south-cards.human-cards .card .card-center-suit { font-size: 22px !important; }

    /* Bidding/prediction tray sits ON THE TABLE, centred over the felt. It used
       to be docked above the board, where players simply didn't look — the eye
       is on the cards and the trick square. As a fixed overlay it also stops
       stealing height from the board, so the hand never has to shrink for it.
       54% (not 50%) is the optical centre of the felt: the status strip above is
       thinner than the hand below. */
    .bidding-interface, .second-phase-bidding {
        position: fixed !important;
        left: 50% !important;
        right: auto !important;
        /* Centred in the band BETWEEN the chrome and the hand, not on the raw
           viewport. Everything else in portrait lives inside .game-container,
           which is padded by the safe areas; a tray centred on the viewport is
           the one thing that isn't, so on a phone with a notch and a home
           indicator the hand rose, the chrome dropped, and the takes tray --
           the tallest of the two -- came down on the top row of the hand and
           hid the ranks. Cards are never coverable: they are the one thing a
           player must see while bidding.

           top:    the chrome above the board measures ~110px below the notch
                   at every phone size (title strip + status + compact score).
           bottom: the hand's own band. Two rows of --cw * 1.4, the 4px row gap,
                   and ~29px of seat padding below: 190px at the 56px card cap,
                   less on a narrower phone, where the extra just becomes air.
           Both top and bottom set, `height: fit-content` and `margin: auto 0`
           centre the tray in what's left and clamp it to that band -- so a tray
           too tall for a small phone scrolls inside itself instead of growing
           over the hand. */
        top: calc(var(--sa-t) + 110px) !important;
        bottom: calc(var(--sa-b) + 190px) !important;
        transform: translateX(-50%) !important;
        height: fit-content !important;
        width: min(90vw, 400px) !important; max-width: none !important; min-width: 0 !important;
        margin: auto 0 !important;
        /* fit-content alone can't shrink below the tray's min-content height,
           and a tray that big would grow back over the hand -- so the band is
           also an explicit cap, and the content scrolls inside it. */
        max-height: calc(100vh - var(--sa-t) - var(--sa-b) - 300px) !important;
        max-height: calc(100dvh - var(--sa-t) - var(--sa-b) - 300px) !important;
        overflow: auto !important;
        padding: 12px 14px 14px !important;
        border-radius: 16px !important;
        border: 1px solid color-mix(in oklab, var(--cr-brass) 45%, transparent) !important;
        border-top: 2px solid var(--cr-brass) !important;
        box-shadow: 0 30px 70px -20px rgba(0,0,0,.92), 0 0 0 1px rgba(0,0,0,.35) !important;
        z-index: 1400 !important;
        animation: cr-trayIn .26s ease-out !important;
    }
    /* Shrink the opponents' card-back piles so they don't crowd the seats. */
    .bot-cards { transform: scale(.72) !important; }

    /* North seat: `left: calc(50% + 50px)` capped the absolutely-positioned row
       at (board width − left) ≈ 154px, which squeezed the plate until "Bid:" and
       "Takes:" wrapped under their own labels. Centre the row and let it size to
       its content so Botti reads on one line like every other seat. */
    .north-player {
        left: 50% !important;
        width: max-content !important;
        max-width: calc(100% - 12px) !important;
        gap: 10px !important;
    }
    .north-player .player-info { flex-shrink: 0 !important; }
}

/* The centred tray keeps its own transform, so the legacy interfaceSlideUp
   (which animates to translate(-50%, 0)) would yank it off-centre. */
@keyframes cr-trayIn {
    from { opacity: 0; transform: translate(-50%, -44%) scale(.97); }
    to   { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}

/* Short portrait phones (e.g. iPhone SE, ≤670 tall): the compact chrome above
   already applies; here we only tighten the status margin and slim the hand /
   board / bid tray further so Bid/Pass never drop below the fold. */
@media (orientation: portrait) and (max-width: 760px) and (max-height: 670px) {
    .game-status > div { font-size: .68em !important; margin: 0 4px !important; }
    .score-item { font-size: .68em !important; }
    /* Slimmer board + hand so the tray (incl. Bid/Pass) always fits. */
    .game-board { min-height: 96px !important; }
    #south-cards.human-cards { --cw: min(46px, calc((100vw - 36px) / var(--hand-cols, 7))) !important; gap: 3px 2px !important; }
    #south-cards.human-cards .card { --card-rank-size: 19px !important; }
    #south-cards.human-cards .card .card-center-suit { font-size: 18px !important; }
    /* Tighter bid tray. */
    .bidding-interface, .second-phase-bidding { max-height: 50vh !important; }
    .trick-button, .suit-button { min-height: 26px !important; padding: 2px !important; }
    .suit-button { font-size: 18px !important; min-height: 28px !important; }
    #bid-btn, #pass-btn { padding: 5px 12px !important; }
}

/* ── 11b. Landscape on a phone ─────────────────────────────────────────────
   styles.css already ships a solid landscape-phone layout (fixed full-viewport
   board, hidden score box, hand pinned at the bottom) and we DON'T re-lay-it-out.
   What we do fix is scale. Both the legacy landscape rules and our own §9
   override were tuned against a 667px iPhone SE and then applied unchanged to a
   956px Pro Max: 13 cards at a hard 46px used barely half the width, and the
   seat plates sat at 0.6em. Card width is now driven by the viewport, so the
   wide phone gets its 64px and the SE keeps the ~46px it always had; the plates
   and status pill grow with it. §1–10 keep the Card Room look. */
@media (orientation: landscape) and (max-height: 500px) {
    /* styles.css scales the control cluster to 0.6, which left the single ☰ a
       24px tap target. With one button per corner there's room for full size.
       Both corners share the status pill's centre line: the pill hangs from the
       top edge at 40px tall, so a 36px button 2px down centres with it. */
    .game-control-buttons {
        transform: none !important; top: 2px !important;
        right: calc(var(--sa-r) + 6px) !important;
    }
    .quick-actions {
        position: fixed !important; z-index: 1300 !important;
        transform: none !important; top: 2px !important;
        left: calc(var(--sa-l) + 6px) !important;
    }
    .menu-btn, .quick-actions .hint-btn {
        width: 38px !important; height: 36px !important; min-height: 36px !important;
        padding: 0 !important; font-size: 18px !important;
    }
    /* The label is hidden below 760px, but a landscape phone is wider than that
       and the button is still icon-sized — "Hint" spilled out of its own box. */
    .btn-label { display: none !important; }

    /* styles.css zeroes the container padding for this layout, but a later
       max-width:768px block puts 10px back — which on a narrow landscape phone
       pushed the status bar 10px below the corner buttons it is supposed to
       sit level with. Everything else here is fixed or absolutely positioned,
       so the padding has nothing left to do. */
    .game-container { padding: 0 !important; }

    /* The status pill: readable at arm's length, and tall enough to hold the
       trump pip on the same line as the buttons. 38vw was set when a scaled
       Turbo control still sat in the left corner; one 38px button does not
       need 300px of clearance. */
    .game-status {
        font-size: .78em !important;
        height: 40px !important; min-height: 40px !important;
        padding: 0 12px !important; align-items: center !important;
        max-width: 72vw !important;
    }

    /* Seat plates carry the bid and takes — the only score on screen here, the
       total-score box being hidden in landscape. 0.6em was too small to read
       without leaning in. */
    .player-info { font-size: .78em !important; }

    /* 13 cards, an explicit 3px between them, and whatever width is left over —
       capped at 64px so they never outgrow the short viewport's height. */
    #south-cards.human-cards {
        /* The board is inset by the side safe areas above, so the row's budget
           is the space BETWEEN them -- not 100vw. Budgeting from 100vw made
           the row wider than the board that clips it, which cut ~40px off the
           end card on a notched phone held sideways. */
        --cw: min(64px, calc((100vw - var(--sa-l) - var(--sa-r) - 72px) / 13)) !important;
        padding: 2px 6px !important;
    }
    #south-cards.human-cards .card { --card-rank-size: 22px !important; margin-left: 3px !important; }
    #south-cards.human-cards .card:first-child { margin-left: 0 !important; }
    #south-cards.human-cards .card .card-rank { font-weight: 900 !important; }
    #south-cards.human-cards .card .card-center-suit { font-size: 26px !important; }
    /* Safe areas (installed app): keep the fixed board, hand and corner chrome
       clear of the side notch and the home indicator. */
    .game-board {
        left: var(--sa-l) !important;
        width: calc(100vw - var(--sa-l) - var(--sa-r)) !important;
    }
    .south-player { bottom: var(--sa-b) !important; }
    .fast-mode-container { left: var(--sa-l) !important; }
    .game-control-buttons { right: var(--sa-r) !important; }
}

/* ── 11c. Compact bid panel on mobile (portrait + landscape) ───────────────
   The trump/takes panel was eating half the screen and hiding the seats — so you
   couldn't see whose turn it was. Shrink the buttons and tighten the panel so the
   board (and the brass turn-spotlight on the acting seat) stays visible. The
   panel only shows on the human's turn, so we also label it plainly. */
@media (orientation: portrait) and (max-width: 760px),
       (orientation: landscape) and (max-height: 500px) {
    .bidding-interface { padding: 6px 10px 8px !important; }

    /* Phase 1 header → an explicit "your turn" cue (the panel is only shown to
       the human whose turn it is to bid). */
    .bidding-interface h3 { font-size: 0 !important; line-height: 0 !important; margin: 2px 0 !important; }
    .bidding-interface h3::after {
        content: "Your turn — pick trump & tricks";
        font-size: 13px; font-weight: 600; line-height: 1.35; color: var(--cr-brass);
    }
    .second-phase-bidding h3, .second-phase-bidding h4, .your-prediction h4 { font-size: .78em !important; margin: 2px 0 4px !important; }

    /* Tighter grids + smaller buttons. */
    .tricks-buttons, .suit-buttons { gap: 4px !important; margin-top: 4px !important; }
    .trick-button, .suit-button { padding: 3px 2px !important; min-height: 30px !important; font-size: 14px !important; border-width: 1px !important; }
    .suit-button { font-size: 21px !important; min-height: 32px !important; }
    .suit-button[data-value="notrump"] { font-size: 14px !important; }
    .bid-controls .input-row { gap: 8px !important; margin: 2px 0 !important; }
    .bid-controls .button-row { gap: 8px !important; margin-top: 6px !important; }
    #bid-btn, #pass-btn { padding: 6px 14px !important; font-size: .82em !important; min-width: 84px !important; }

    /* Phase 2 "how many tricks" buttons. */
    .your-prediction h4 { font-size: .8em !important; }
    .tricks-buttons-main, #tricks-buttons-main, .tricks-row { gap: 4px !important; }
    .trick-btn { width: 34px !important; min-width: 34px !important; height: 34px !important; font-size: 15px !important; }
    .more-btn, .less-btn { padding: 5px 10px !important; font-size: .8em !important; }
    /* Compact seats: the tray also carries the trick buttons and the Predict
       action here, so the takes panel has to stay under ~150px tall or the
       tray starts scrolling internally and hides the action. */
    .prediction-list { gap: 4px 8px !important; }
    .prediction-item { --pred-value-size: 19px !important; min-width: 60px !important; padding: 2px 6px !important; gap: 0 !important; }
    .prediction-item .player-name { font-size: 10px !important; letter-spacing: .06em !important; }
    .prediction-trump { width: 34px !important; height: 34px !important; font-size: 1.5em !important; }
    .prediction-total { margin-top: 4px !important; padding-top: 4px !important; font-size: 11px !important; gap: 6px !important; }
    .prediction-total #prediction-total { font-size: 15px !important; }
}

/* Short viewports (landscape phones, iPhone-SE-class portrait): height is the
   scarce axis and width is plentiful, so the takes compass folds into a single
   row — trump first, then the seats. The 3-row compass costs ~190px here, which
   pushed the trick buttons and Predict out of the tray's max-height. */
@media (orientation: landscape) and (max-height: 500px),
       (orientation: portrait) and (max-width: 760px) and (max-height: 670px) {
    .prediction-list {
        display: flex !important;
        flex-wrap: nowrap !important;
        justify-content: center !important;
        align-items: stretch !important;
        gap: 6px !important;
        max-width: none !important;
    }
    .prediction-trump { order: -1 !important; align-self: center !important; }
    .prediction-item { min-width: 54px !important; }
}

/* ── 11c-b. Portrait: the tray is on the felt now, so it can afford to be big ─
   §11c above shrank the controls to claw back height when the tray was docked in
   the layout flow. In portrait it floats over the board instead, so the targets
   go back up to a comfortable size — this is the panel the whole hand hinges on.
   Landscape phones keep the compact §11c sizes (they have ~375px of height), and
   so do short portrait phones (≤670 tall — the iPhone SE block above). */
@media (orientation: portrait) and (max-width: 760px) and (min-height: 671px) {
    .bidding-interface h3::after { font-size: 16px; }
    .second-phase-bidding h3, .second-phase-bidding h4, .your-prediction h4 { font-size: .95em !important; margin: 2px 0 8px !important; }

    .tricks-buttons, .suit-buttons { gap: 8px !important; margin-top: 10px !important; }
    .trick-button, .suit-button { padding: 8px 10px !important; min-height: 50px !important; font-size: 21px !important; }
    .suit-button { font-size: 32px !important; min-height: 52px !important; }
    .suit-button[data-value="notrump"] { font-size: 21px !important; }
    .bid-controls .input-row { gap: 12px !important; margin: 6px 0 !important; }
    .bid-controls .button-row { gap: 12px !important; margin-top: 12px !important; }
    #bid-btn, #pass-btn { padding: 13px 22px !important; font-size: 1.08em !important; min-width: 124px !important; }

    /* Phase 2 "how many tricks". */
    .trick-btn { width: 50px !important; min-width: 50px !important; height: 50px !important; font-size: 22px !important; }
    .tricks-buttons-main, #tricks-buttons-main, .tricks-row { gap: 8px !important; }
    .more-btn, .less-btn { padding: 9px 14px !important; font-size: .95em !important; }
    .prediction-list { gap: 8px 12px !important; }
    .prediction-item { --pred-value-size: 27px !important; min-width: 82px !important; padding: 6px 8px !important; }
    .prediction-item .player-name { font-size: 11px !important; letter-spacing: .1em !important; }
    .prediction-trump { width: 46px !important; height: 46px !important; }
}

/* ── 11d. No card highlighted until the player taps it ─────────────────────
   The old "suggested card" hint lifted + pulsed a card in the hand, which was
   distracting. Hint now shows text advice in a modal instead (see script.js), so
   this neutralises any leftover auto-highlight. Also stop :hover from lifting a
   card on touch screens, where the hover state sticks after a tap and leaves a
   card standing out. Tapping to select/play a card still animates normally. */
.human-cards .card.suggested-card {
    outline: none !important; animation: none !important; box-shadow: none !important;
    transform: none !important; z-index: auto !important;
}
@media (hover: none), (pointer: coarse) {
    #south-cards.human-cards .card:hover,
    .human-cards .card:hover {
        transform: none !important; z-index: auto !important; box-shadow: none !important;
    }
}

/* ── 11e. "Your turn" cue ──────────────────────────────────────────────────
   When it's the human's turn (body.my-turn, set by updateTableChrome), blink the
   south seat name + plate so it's obvious — especially during bidding. After 30s
   idle the JS adds body.turn-nudge for a brighter flash (and a haptic buzz where
   the browser supports it). */
body.my-turn #south-player .player-name {
    animation: myTurnName 1.15s ease-in-out infinite !important;
}
body.my-turn #south-player .player-info {
    animation: myTurnPlate 1.15s ease-in-out infinite !important;
}
@keyframes myTurnName {
    0%, 100% { color: var(--cr-ink); text-shadow: none; }
    50% { color: var(--cr-brass); text-shadow: 0 0 10px rgba(203,165,82,.85); }
}
@keyframes myTurnPlate {
    0%, 100% { box-shadow: 0 0 0 2px var(--cr-brass), 0 0 18px rgba(203,165,82,.32); border-color: var(--cr-brass); }
    50% { box-shadow: 0 0 0 2px var(--cr-brass), 0 0 30px 5px rgba(203,165,82,.7); border-color: var(--cr-brass); }
}
body.turn-nudge #south-player .player-info {
    animation: turnNudge .42s ease-in-out 5 !important;
}
@keyframes turnNudge {
    0%, 100% { box-shadow: 0 0 0 2px var(--cr-brass), 0 0 18px rgba(203,165,82,.4); }
    50% { box-shadow: 0 0 0 3px #fff, 0 0 34px 8px rgba(203,165,82,.95); }
}

/* ── 12. Accessibility niceties ────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    body.my-turn #south-player .player-name { animation: none !important; color: var(--cr-brass) !important; }
    body.my-turn #south-player .player-info, body.turn-nudge #south-player .player-info {
        animation: none !important; box-shadow: 0 0 0 2px var(--cr-brass), 0 0 26px 4px rgba(203,165,82,.6) !important;
    }
}

@media (prefers-reduced-motion: reduce) {
    .game-container h1, .player-info, #south-cards.human-cards .card, .trick-progress > i { animation: none !important; transition: none !important; }
}

/* ── 13. Card selection: lift, then drag or tap ────────────────────────────
   §9's id-scoped hand rules and §11d's hover neutraliser both set `transform`
   with !important, so the lifted / dragged states have to be declared at the
   same weight and later in the cascade to win. The drag itself sets an inline
   `transform` with priority `important` (see onCardPointerMove), which outranks
   everything here — these rules cover the resting lifted state only.

   Brass is the game's one accent: the lifted card wears the same ring as the
   active seat, and the trick square lights up as the drop target. */
#south-cards.human-cards .card.card-armed {
    transform: translateY(-24px) scale(1.16) !important;
    box-shadow:
        0 16px 28px rgba(0,0,0,.55),
        0 0 0 2px var(--cr-brass),
        0 0 22px -2px rgba(203,165,82,.85) !important;
    border-color: var(--cr-brass) !important;
    z-index: 200 !important;
}

/* Illegal under follow-suit: dimmed, and the pointer layer refuses the lift. */
#south-cards.human-cards .card.card-illegal {
    filter: grayscale(.7) brightness(.62) !important;
    cursor: not-allowed !important;
}

/* Nudge when a dimmed card is pressed — "not that one", without a toast. */
#south-cards.human-cards .card.card-refused {
    animation: cardRefuse .28s ease-in-out !important;
}

.trick-area.drop-active {
    border-color: rgba(203,165,82,.95) !important;
    box-shadow:
        0 0 0 1px rgba(203,165,82,.5),
        inset 0 0 34px -6px rgba(203,165,82,.75) !important;
    background: radial-gradient(ellipse at center, rgba(203,165,82,.22), rgba(203,165,82,0) 74%) !important;
}

@media (prefers-reduced-motion: reduce) {
    #south-cards.human-cards .card.card-refused { animation: none !important; }
}

/* ── 14. Bids total: "15 · OVER" ───────────────────────────────────────────
   The status bar used to read "Bid: 15 (Over)" — "Bid" means the trump bid
   everywhere else in the game. The state is a chip so it reads as a condition
   of the hand, not as part of the number. The "/13" that used to follow the
   total is gone: the denominator never changes and the chip already says which
   side of it the hand fell on, so the width goes to making the chip legible.
   announceBidTotal() says it once in full — "16 of 13" — when bidding closes. */
#turn-indicator { display: inline-flex; align-items: center; gap: 5px; }
#turn-indicator .bid-total { font-variant-numeric: tabular-nums; font-weight: 700; color: var(--cr-ink) !important; }
#turn-indicator .bid-state {
    /* display/height are forced explicitly: the status bar lays its spans out
       as flex items, which stretched this chip into a 45x40 disc. */
    display: inline-block !important;
    width: auto !important; height: auto !important; min-height: 0 !important;
    font-size: .72em; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
    padding: 1px 7px; border-radius: 999px; line-height: 1.7;
}
#turn-indicator .bid-state.over  { background: rgba(194,96,79,.9);  color: #1b0f0c; }
#turn-indicator .bid-state.under { background: rgba(143,208,166,.9); color: #0c1a12; }
#turn-indicator .bid-state.exact { background: var(--cr-brass);      color: #241c07; }

/* ── 15. Scoreboard: a ranking, not four numbers ───────────────────────────
   The strip used to be four name/number pairs in the handwriting face, evenly
   spaced, in no order — nothing said who was ahead, nothing said what the last
   hand cost, and your own score looked like everyone else's. Now: sorted, the
   leader lit, your row marked, and the swing from the hand just scored sitting
   beside the total until the next deal. */
.total-score-box .score-item {
    display: grid !important;
    grid-template-columns: 12px 1fr auto auto !important;
    align-items: center !important;
    gap: 8px !important;
    font-family: var(--cr-sans) !important;
    padding: 5px 9px !important;
    border-radius: 7px !important;
}
.total-score-box .score-item + .score-item { border-top: 1px solid var(--cr-line) !important; }
.total-score-box .score-rank {
    font-size: 10px; color: var(--cr-ink-soft); font-variant-numeric: tabular-nums; opacity: .8;
}
.total-score-box .player-label {
    font-family: var(--cr-sans) !important; font-size: 13px !important;
    color: var(--cr-ink) !important; white-space: nowrap;
}
.total-score-box .score-value {
    font-family: var(--cr-sans) !important; font-size: 15px !important; font-weight: 700 !important;
    font-variant-numeric: tabular-nums; color: var(--cr-ink) !important; text-shadow: none !important;
}
.total-score-box .score-delta {
    font-size: 10.5px; font-weight: 700; font-variant-numeric: tabular-nums;
    padding: 1px 6px; border-radius: 999px; line-height: 1.6;
}
.total-score-box .score-delta.gain { background: rgba(143,208,166,.16); color: #8fd0a6; }
.total-score-box .score-delta.loss { background: rgba(194,96,79,.18); color: #e0907f; }

/* The leader, and you. */
.total-score-box .score-item.first-place { background: rgba(203,165,82,.10) !important; }
.total-score-box .score-item.first-place .score-value,
.total-score-box .score-item.first-place .player-label { color: var(--cr-brass) !important; }
.total-score-box .score-item.is-you .player-label::after {
    content: "you"; margin-left: 6px; font-size: 9px; letter-spacing: .1em; text-transform: uppercase;
    color: #241c07; background: var(--cr-brass); border-radius: 999px; padding: 1px 6px; vertical-align: 1px;
}

/* Phones keep the score as a single strip (a four-row list would cost ~90px of
   board), so the ranking is carried by the order — the seats are already sorted
   leader-first — and the rank digit is dropped. The swing still shows; your
   seat is underlined in brass rather than tagged, which costs no width. */
@media (orientation: portrait) and (max-width: 760px) {
    /* Two short lines per seat — name above, total and swing below. On one
       line the four seats needed 499px against 372px of strip; stacked they
       need ~270px, and the strip only grows by a dozen pixels. */
    .total-score-box .score-item {
        display: grid !important;
        grid-template-columns: auto auto !important;
        grid-template-areas: "name name" "value delta" !important;
        justify-items: start !important;
        align-items: baseline !important;
        gap: 0 5px !important;
        border-radius: 0 !important;
    }
    /* 375px wide and 372 available: the strip has no slack, so the gap
       between seats does the fitting. */
    #total-score-content { gap: 0 6px !important; }
    .total-score-box .player-label { grid-area: name; }
    .total-score-box .score-value  { grid-area: value; }
    .total-score-box .score-delta  { grid-area: delta; }
    .total-score-box .score-rank { display: none !important; }
    .total-score-box .player-label { font-size: 11.5px !important; }
    .total-score-box .score-value { font-size: 12.5px !important; }
    .total-score-box .score-delta { font-size: 9px !important; padding: 0 4px !important; line-height: 1.5; }
    .total-score-box .score-item.first-place { background: none !important; }
    .total-score-box .score-item.is-you .player-label::after { display: none !important; }
    .total-score-box .score-item.is-you .player-label {
        text-decoration: underline; text-decoration-color: var(--cr-brass);
        text-underline-offset: 3px; text-decoration-thickness: 1.5px;
    }
}

/* iPhone-SE-class widths: the same strip with a little less air. */
@media (orientation: portrait) and (max-width: 390px) {
    #total-score-content { gap: 0 4px !important; }
    .total-score-box .player-label { font-size: 10.5px !important; }
    .total-score-box .score-value { font-size: 11.5px !important; }
    .total-score-box .score-delta { font-size: 8.5px !important; padding: 0 3px !important; }
}

/* ── 16. The name card, above the keyboard ────────────────────────────────
   setupNameModalViewport() sizes the modal to visualViewport, so the card
   centres in what the keyboard leaves rather than behind it. The card itself
   also gets shorter on a phone: with ~340px of visible height there is no
   room for 30px of padding and a two-line greeting. */
@media (max-width: 760px) {
    .name-content { padding: 18px 16px !important; width: 92% !important; }
    .name-header h2 { font-size: 1.05em !important; margin: 0 0 4px !important; }
    .name-body p { font-size: .82em !important; margin: 0 0 10px !important; }
    #player-name-input { padding: 10px 12px !important; font-size: 16px !important; }
    #start-game-btn { margin-top: 12px !important; padding: 11px 22px !important; }
}

/* Phones: the chip already says what the number is, so the "Bids:" label is
   dropped — with it the bar needed 443px against 390. font-size:0 on the
   wrapper hides the bare text node; the value restores its own size. The chip
   spends the width the dropped "/13" gave back: at 8px it was the smallest
   thing on the bar and the one carrying the verdict. */
@media (orientation: portrait) and (max-width: 760px) {
    .current-turn { font-size: 0 !important; }
    #turn-indicator { font-size: 15px !important; }
    #turn-indicator .bid-state { font-size: 11px !important; padding: 2px 9px !important; letter-spacing: .09em !important; }
    /* The chip is wider than the "(Over)" it replaces, so the rest of the bar
       gives back the difference: it has to hold Round, trump, bids and Gamlet
       inside 390px. */
    /* The bar spread its items edge-to-edge, which put the first and last of
       them under the Hint and menu buttons once the bids block grew. Centre
       the group instead, and drop the trick counter — of the four readouts it
       is the one the felt itself already tells you. */
    .game-status { justify-content: center !important; gap: 10px !important; padding: 2px 6px !important; }
    .game-status > div { font-size: .72em !important; margin: 0 !important; }
    .tricks-counter { display: none !important; }
    .trump-suit span { width: 22px !important; height: 22px !important; font-size: .9em !important; }
}

/* Bid summary in the Card Room palette. */
.bid-summary {
    border-color: var(--cr-brass) !important;
    background: linear-gradient(180deg, rgba(22,44,30,.97), rgba(13,26,18,.98)) !important;
    -webkit-backdrop-filter: blur(8px) !important; backdrop-filter: blur(8px) !important;
}
.bid-summary-total { font-family: var(--cr-serif) !important; color: var(--cr-brass) !important; font-weight: 600 !important; }
.bid-summary-verdict { font-family: var(--cr-sans) !important; color: var(--cr-ink) !important; }
.bid-summary.is-over { border-color: #d98a78 !important; }
.bid-summary.is-over .bid-summary-total { color: #eaa08d !important; }
@media (orientation: portrait) and (max-width: 760px) {
    .bid-summary { padding: 11px 15px !important; }
    .bid-summary-total { font-size: 16px !important; }
    .bid-summary-verdict { font-size: 11.5px !important; }
}

/* ── 12. Fanned hand (opt-in) ───────────────────────────────────────────────
   A second hand layout that reads as a hand being held: the same rows the
   unfanned layout produces, bowed on an arc. Nothing overlaps — a single
   13-card portrait arc leaves a 22px sliver per card, which is the mis-tap
   problem the two-row split was built to fix.

   The whole arc is driven by --fan-t, the card's place in its own row that
   layoutHumanHand() stamps (-1 … +1). `1 - t*t` is the bow: it peaks at the
   row's centre and falls to zero at both ends. Two knobs — --fan-tilt and
   --fan-bow — are all that separates portrait from landscape.

   Why !important, and why the states below are re-declared: the landscape
   block in styles.css sets `transform: none !important` on .human-cards
   .card, so the fan rule has to shout to exist at all. Shouting makes it
   outrank .card-armed's lift (which carries no !important), so that lift is
   restored here explicitly — otherwise switching the fan on silently breaks
   two-stage card selection. The drag path needs no help: it writes its
   transform inline with !important, which outranks any stylesheet rule, and
   removeProperty() falls back here. */

/* Desktop and tablets: one overlapping nowrap row of big cards. These are the
   DEFAULTS — the phone blocks below override them. Declaring them unscoped is
   what guarantees no viewport is left without magnitudes, which is how the
   menu toggle came to do nothing on desktop.

   --fan-bow is left at the same -14px used before the --cw narrowing below:
   as a fraction of card height (--cw * 1.4) it's ~11.9% at the 84px width
   below, matching portrait's ~12.4% (-9px / (52px * 1.4)) closely enough that
   it doesn't need to move just because the tilt did. */
/* The magnitudes live on the SEAT, not the row: the row reads them by
   inheritance, and so does the nameplate above it, which has to lift itself by
   whatever the bow raises the cards. */
body.hand-fanned .south-player {
    --fan-tilt: 5deg;
    --fan-bow: -14px;
}

/* True desktop only (tablets still fall to the 900px/560px --cw drops in
   section 9, untouched here): narrowing the card, not just raising the tilt,
   is what the phone blocks do too (56->52px, 64->60px) — it's the same lever
   pointed at the same problem: the rotation swing comes out of the card's own
   height, so a slightly narrower card buys the swing room the tilt spends.

   84px (an 8% cut from the unfanned 91px) is the width picked to reach 5deg
   with zero measured board clipping across the whole desktop range (901px
   through 1920px+, both the 95vw-scaled board below ~1053px and the
   fixed-1000px board above it) while costing the player as little of their
   own card as the tilt gain is worth: a narrower 68px card would reach 6deg,
   but only by shrinking the card a full 25% instead of 8% for one more
   degree, and it also crosses the row's margin-left: min(6px, calc(...))
   formula onto its other branch at wide-enough windows (~992px and up for
   68px), which flips the row from overlapping to non-overlapping and changes
   the hand's visual character — 84px stays on the same (tight/overlapping)
   branch as the unfanned 91px at every desktop width, so only the tilt and
   card size change, nothing else does. 6deg still clips at 84px, so 5deg is
   the ceiling this width buys. */
@media (min-width: 901px) {
    body.hand-fanned #south-cards.human-cards {
        --cw: 84px !important;
    }
}

/* --fan-t-all, not --fan-t: the hand is drawn as ONE row everywhere except
   portrait phones (the break is display:none), so one sweep is what a drawn
   row gets. The portrait block below, which is the only place the break is
   drawn, switches back to the per-row number. */
body.hand-fanned #south-cards.human-cards .card {
    --t: var(--fan-t-all, 0);
    transform:
        translateY(calc((1 - var(--t) * var(--t)) * var(--fan-bow, 0px)))
        rotate(calc(var(--t) * var(--fan-tilt, 0deg))) !important;
    transform-origin: 50% 160% !important;
}

/* Pulling a card out of a real hand straightens it, so the lifted states
   carry no rotation. These re-declare what the fan rule would otherwise
   outrank. */
body.hand-fanned #south-cards.human-cards .card.card-armed {
    transform: translateY(-24px) scale(1.16) !important;
}
@media (hover: hover) and (pointer: fine) {
    body.hand-fanned #south-cards.human-cards .card:hover {
        transform: translateY(-15px) scale(1.06) !important;
    }
}

/* Portrait phones: two rows, so each row only needs a gentle bow. The card
   width cap drops 56 -> 52px because rotating about a pivot below the card
   swings the end cards sideways, and that swing has to come out of the
   row's width or the outermost card hangs off the screen. */
@media (orientation: portrait) and (max-width: 760px) {
    body.hand-fanned .south-player {
        --fan-tilt: 6deg;
        --fan-bow: -9px;
    }
    body.hand-fanned #south-cards.human-cards {
        --cw: min(52px, calc((100vw - 52px) / var(--hand-cols, 7))) !important;
    }
    /* The only breakpoint that draws the row break, so the only one that wants
       a sweep per row rather than one across the hand. */
    body.hand-fanned #south-cards.human-cards .card {
        --t: var(--fan-t, 0);
    }
}

/* Landscape: the one place a real HELD fan fits -- one row of 13, width to
   spare, and no second row to keep clear of. So landscape doesn't just curve
   the row: it overlaps the cards, sweeps them 36deg end to end, and pivots
   them about a point far below the felt, the way a hand sits in a fist.

   The three magnitudes are one shape, not three knobs:
     --cw          the biggest card that still fits, by width AND by height.
                   Width: a fan this deep renders ~12.9 card widths across
                   (measured 724px at 56px, 616 at 48, 519 at 40), so the old
                   /13 budget still holds -- only the 96px it subtracted for
                   the gaps is now 16px, since the cards overlap instead of
                   sitting apart. Height: the fan stands ~1.5 cards tall above
                   the row's own bottom, and 303px of the screen is spoken for
                   (the 40px lift below, the trick square's 263px bottom edge
                   above), so 1.7 rather than 1.5 leaves the square a margin.
     margin-left   -43% of a card. What makes it a fan and not a curved row:
                   ~49px of each 56px card stays visible, and rank and suit
                   sit top-left, on the half that is never covered (z-index
                   already climbs left to right).
     origin 320%   the pivot, ~2.2 card-heights below the card's own bottom.
                   The far pivot is what splays the cards along an arc rather
                   than spinning each in place -- and it costs the end cards
                   ~0.8 of a card of drop, which padding-bottom pays back. */
@media (orientation: landscape) and (max-height: 500px) {
    body.hand-fanned .south-player {
        --fan-tilt: 11deg;
        --fan-bow: -16px;
    }
    body.hand-fanned #south-cards.human-cards {
        --cw: min(60px, calc((100vw - var(--sa-l) - var(--sa-r) - 96px) / 13)) !important;
    }
    /* The bow lifts the middle of the row above its own layout box, and the
       one thing sitting on that box is the player's own bid/takes chip -- the
       numbers you actually read mid-trick. Lift the chip by exactly what the
       fan rises, so it clears whatever bow this breakpoint asks for. */
    body.hand-fanned .south-player .player-info {
        margin-bottom: calc(4px - var(--fan-bow, 0px)) !important;
    }
}

/* ...but only a phone with room under the trick square gets the FULL fan. The
   square is pinned from the top of the board, so its bottom sits at ~269px on
   every landscape phone; below ~400px of height the deep fan's peak would have
   to rise through it (and through the played cards inside it), and shrinking
   the cards to fit puts them under 40px -- narrower than a fingertip. Those
   phones keep the gentle arc above. */
@media (orientation: landscape) and (min-height: 400px) and (max-height: 500px) {
    body.hand-fanned .south-player {
        --fan-tilt: 36deg;
        /* No extra bow here, unlike portrait: rotating about a pivot this far
           below already arcs the row, so the 1 - t² lift would only push the
           peak of the fan further up the felt for nothing. */
        --fan-bow: 0px;
    }
    body.hand-fanned #south-cards.human-cards {
        --cw: min(
            56px,
            calc((100vw - var(--sa-l) - var(--sa-r) - 16px) / 13),
            calc((100vh - var(--sa-b) - 303px) / 1.7)
        ) !important;
    }
    body.hand-fanned #south-cards.human-cards .card {
        margin-left: calc(var(--cw) * -0.43) !important;
        transform-origin: 50% 320% !important;
    }
    body.hand-fanned #south-cards.human-cards .card:first-child {
        margin-left: 0 !important;
    }
    /* Lift the whole SEAT, not the row inside it: the pivot drops the two end
       cards ~0.8 of a card below the middle, far enough that their centres --
       the half of a card a thumb aims at -- fell off the bottom of the screen.
       Padding inside the row would have lifted the cards out from under the
       nameplate that sits above them; moving the seat carries the plate along,
       so the chip stays exactly where it belongs, just above the fan.

       A held fan is also in front of the table, and it has to be: .center-area
       is z-index 50 (so played cards clear the name boxes) against the seats'
       10, so the trick square painted over the peak of the fan and swallowed
       the taps aimed at the middle cards. */
    body.hand-fanned .south-player {
        bottom: calc(var(--sa-b) + 40px) !important;
        z-index: 70 !important;
    }
}
