/* =========================================
   BALL SMACK! — "CLASSIC BOARD" THEME (1b)
   Bright classic-marble palette on a
   cream pegboard table. Clean-modern with retro
   nostalgia + glossy marble highlights.
   MOBILE-LOCKED: forced phone column on desktop.
   ========================================= */

@import url('https://fonts.googleapis.com/css2?family=Lilita+One&family=DM+Sans:wght@400;500;700;900&display=swap');

:root {
    --marble-red:    #e63946;
    --marble-amber:  #f4a300;
    --marble-green:  #2a9d5f;
    --marble-teal:   #2a9d8f;
    --marble-blue:   #457b9d;
    --marble-violet: #6a4c93;

    --table:      #f3e6c8;  /* cream felt */
    --parchment:  #fbf3df;  /* card fill */
    --board-gold: #d9b877;  /* frame border */
    --board-gold-dk: #b89355;
    --ink:        #4a2f1c;  /* dark brown text */
    --ink-soft:   #8a6e46;
    --ink-faint:  #a08b5f;

    --app-max: 480px;

    /* Logo rainbow -- evenly BANDED so every marble colour reads distinctly
       (a plain smooth sweep bunched into yellow/green in the middle). Shared by
       the header + splash logos, applied per-line so each line shows red->violet. */
    --logo-rainbow: linear-gradient(90deg,
        #e63946 0%, #e63946 16%,
        #f4a300 24%, #f4a300 34%,
        #2a9d5f 40%, #2a9d5f 46%,
        #2a9d8f 50%, #2a9d8f 55%,
        #457b9d 61%, #457b9d 70%,
        #6a4c93 77%, #6a4c93 100%);
}

*, *::before, *::after { box-sizing: border-box; }

/* ---- TABLE SURFACE + PEGBOARD ---- */
body {
    background-color: #d9c39a;
    background-image:
        radial-gradient(circle at 6% 4%, rgba(230,57,70,0.22), transparent 26%),
        radial-gradient(circle at 94% 6%, rgba(69,123,157,0.22), transparent 26%),
        radial-gradient(circle at 96% 96%, rgba(42,157,95,0.22), transparent 28%),
        radial-gradient(circle at 4% 94%, rgba(106,76,147,0.22), transparent 28%),
        radial-gradient(circle at 50% 50%, rgba(244,163,0,0.14), transparent 40%),
        radial-gradient(circle at center, rgba(255,255,255,0.30) 0%, rgba(120,90,45,0.26) 100%),
        radial-gradient(rgba(120,90,45,0.16) 1.6px, transparent 1.7px);
    background-size: 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 15px 15px;
    font-family: 'DM Sans', sans-serif;
    color: var(--ink);
    margin: 0; padding: 0; overflow: hidden;
}

/* =========================================
   MOBILE LOCK — center a phone column on desktop
   ========================================= */
/* MOBILE LOCK: centered portrait safe-area on desktop. The old hard drop-
   shadow framed the app like a phone-shaped website floating on a page; the
   redesign drops it so the themed table bleeds edge-to-edge behind a seamless
   column (native-app feel, not a card). */
.overlay, #gameWrapper {
    left: 50% !important;
    right: auto !important;
    transform: translateX(-50%);
    width: 100%;
    max-width: var(--app-max);
}

.overlay {
    position: fixed; top: 0; bottom: 0;
    overflow-y: auto; overflow-x: hidden; z-index: 500;
    padding: calc(env(safe-area-inset-top) + 12px) 12px 16px 12px;
    display: block;
    background-color: var(--table);
    background-image:
        radial-gradient(circle at 6% 4%, rgba(230,57,70,0.20), transparent 26%),
        radial-gradient(circle at 94% 6%, rgba(69,123,157,0.20), transparent 26%),
        radial-gradient(circle at 96% 96%, rgba(42,157,95,0.20), transparent 28%),
        radial-gradient(circle at 4% 94%, rgba(106,76,147,0.20), transparent 28%),
        radial-gradient(circle at 50% 0%, rgba(255,255,255,0.45) 0%, transparent 55%),
        radial-gradient(rgba(150,110,60,0.15) 1.6px, transparent 1.7px);
    background-size: 100% 100%, 100% 100%, 100% 100%, 100% 100%, 100% 100%, 15px 15px;
    /* hide the shell scrollbar (inner lists keep their own thin bars) */
    scrollbar-width: none;
}
.overlay::-webkit-scrollbar { display: none; }

/* =========================================
   TRANSITIONS (native redesign) — screens & dialogs animate IN on show.
   Pure CSS: a CSS `animation` restarts automatically whenever an element
   re-enters the render tree (display:none -> shown), so the EXISTING display
   swaps in network.js/main.js trigger these with zero JS changes -- the
   fragile screen-flow logic stays untouched. Enter-only (exit stays instant;
   the slide-out + scrim arrive with the bottom-sheet phase). The main-menu
   and lobby cards intentionally get only the overlay fade, NOT a transform,
   so the Google Sign-In iframe inside the menu can't flicker on an ancestor
   transform. Everything gated behind prefers-reduced-motion.
   ========================================= */
@media (prefers-reduced-motion: no-preference) {
    @keyframes bsScreenIn { from { opacity: 0; } to { opacity: 1; } }
    @keyframes bsCardRise {
        from { opacity: 0; transform: translateY(18px) scale(.985); }
        to   { opacity: 1; transform: none; }
    }
    @keyframes bsPaneIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
    .overlay, #gameWrapper { animation: bsScreenIn .26s ease both; }
    /* dialogs (popups + setup) and the rulebook rise; screens just fade */
    .modal-card, .rules-card { animation: bsCardRise .34s cubic-bezier(.22,1,.36,1) both; }
    /* bottom-sheet popups slide up from the bottom instead of rising in place */
    @keyframes bsSheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
    .sheet-overlay > .modal-card, .sheet-overlay > .rules-card { animation: bsSheetUp .34s cubic-bezier(.22,1,.36,1) both; }
    /* switching menu tabs cross-fades the incoming pane in */
    .menu-pane.active { animation: bsPaneIn .28s ease; }
}

/* =========================================
   PAGED MENU (native redesign) — one pane at a time + fixed bottom tab bar
   + docked global chat. The footer is position:fixed; its containing block is
   the transformed .overlay ancestor, so it pins to the centered column's
   bottom AND disappears with the menu (a fixed element inside a display:none
   ancestor isn't rendered) -- no JS toggles the footer's visibility.
   ========================================= */
.menu-shell { flex-direction: column; }
.menu-panes { width: 100%; padding-bottom: calc(116px + env(safe-area-inset-bottom)); }
.menu-pane { width: 100%; }
.menu-pane:not(.active) { display: none; }

.menu-footer {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
    display: flex; flex-direction: column;
}

/* docked chat: collapsed shows only the peek bar; taps expand the panel up */
.menu-chatpanel {
    max-height: 0; overflow: hidden; background: var(--parchment);
    transition: max-height .34s cubic-bezier(.22,1,.36,1);
}
.menu-chatpanel.open { max-height: 54vh; }
.menu-chatpanel .premium-chat-section { padding: 10px 12px 12px; }
.menu-chatpanel .premium-chat-box { max-height: 38vh; }

.menu-chatdock {
    display: flex; align-items: center; gap: 10px; width: 100%;
    border: none; border-top: 2px solid var(--board-gold); border-radius: 0;
    background: linear-gradient(180deg,#fdf8e9,#eaddc5);
    box-shadow: inset 0 2px 3px rgba(255,255,255,.7);
    padding: 10px 14px; cursor: pointer; text-align: left;
    text-transform: none; letter-spacing: 0; color: var(--ink);
    font-family: 'DM Sans', sans-serif;
}
.menu-chatdock:active { background: linear-gradient(180deg,#f3e9d2,#e2d3b6); }
.mcd-ic { font-size: 16px; line-height: 1; flex: none; }
.mcd-latest { flex: 1; min-width: 0; font-weight: 600; font-size: 12.5px; color: var(--ink);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; text-shadow: none; }
.mcd-badge { flex: none; background: var(--marble-red); color: #fff; min-width: 19px; height: 19px;
    border-radius: 10px; padding: 0 5px; font-weight: 800; font-size: 10px;
    display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 3px rgba(0,0,0,.35); }
.mcd-chev { flex: none; color: var(--ink-soft); font-size: 12px; transition: transform .32s cubic-bezier(.22,1,.36,1); }
.menu-chatdock.open .mcd-chev { transform: rotate(180deg); }

.menu-tabbar {
    display: flex; gap: 6px;
    padding: 8px 12px calc(env(safe-area-inset-bottom) + 12px);
    background: linear-gradient(180deg,#fdf8e9,#eaddc5);
    border-top: 3px solid var(--board-gold);
    box-shadow: 0 -5px 15px rgba(90,60,20,.2);
}
.menu-tab {
    position: relative; flex: 1; border: none; background: transparent; border-radius: 12px;
    padding: 9px 2px 6px; display: flex; flex-direction: column; align-items: center; gap: 3px;
    color: var(--ink-faint); font-family: 'DM Sans', sans-serif; font-weight: 800;
    font-size: 10px; letter-spacing: .5px; text-transform: none; text-shadow: none; box-shadow: none;
    transition: color .15s;
}
.menu-tab .mt-ic { font-size: 20px; line-height: 1; filter: grayscale(.5) opacity(.6);
    transition: transform .18s cubic-bezier(.22,1,.36,1), filter .18s; }
.menu-tab.active { color: var(--tc, var(--ink));
    background: radial-gradient(circle at 50% 125%, color-mix(in srgb, var(--tc,#f4a300) 34%, transparent), transparent 72%); }
.menu-tab.active .mt-ic { filter: none; transform: translateY(-2px) scale(1.15); }
.menu-tab.active::before { content: ""; position: absolute; top: 0; left: 24%; right: 24%; height: 3px;
    border-radius: 0 0 3px 3px; background: var(--tc,#f4a300); }
.menu-tab:active { transform: none; box-shadow: none; }

/* MORE tab = a hub of full-width link rows to the other areas (profile, marble
   works, friends, rulebook, manual, admin, sign out) + the sound toggle. */
.menu-links { display: flex; flex-direction: column; padding-top: 2px; }
.menu-link {
    display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
    background: var(--parchment) !important;
    border: 2px solid var(--board-gold); border-left-width: 6px;
    border-radius: 12px; padding: 12px 14px; margin-bottom: 10px;
    color: var(--ink); font-family: 'Lilita One', cursive; font-size: 14px;
    letter-spacing: .5px; text-transform: none; text-shadow: none;
    box-shadow: inset 0 2px 3px rgba(255,255,255,.6), 0 3px 0 var(--board-gold-dk);
    transition: transform .06s, box-shadow .06s;
}
.menu-link:active { transform: translateY(3px); box-shadow: inset 0 3px 6px rgba(0,0,0,.25); }
.menu-link .ml-ic { font-size: 20px; width: 26px; text-align: center; flex: none; line-height: 1; }
.menu-link .ml-cr { margin-left: auto; color: var(--ink-faint); font-family: 'DM Sans', sans-serif; font-weight: 900; }
/* friend-request badge moved off the (removed) player-card friends button onto
   the More TAB's corner, so pending-request notifications stay visible. */
.menu-tab > #friendsBadge { top: 2px; right: 16%; left: auto; z-index: 4; }
/* online-friend count on the opposite (top-left) corner of the More tab, so the
   two badges never overlap (mirrors their old two-corner layout). */
.menu-tab > #friendsOnlineBadge { top: 2px; left: 16%; right: auto; z-index: 4; }

/* =========================================
   TOGGLE SWITCHES (native redesign) — a checkbox restyled as an on-theme
   switch. The element stays <input type="checkbox">, so every .checked read
   in the JS keeps working untouched; only the appearance changes.
   ========================================= */
input.bs-toggle {
    -webkit-appearance: none; appearance: none;
    position: relative; flex: none; cursor: pointer; vertical-align: middle;
    /* !important: an ID rule (#authRememberMe) and .premium-checkbox input both
       force a 14-15px checkbox, which would shrink the track under the knob. */
    width: 42px !important; height: 24px !important; margin: 0 !important;
    border-radius: 13px;
    background: var(--parchment); border: 2px solid var(--board-gold);
    box-shadow: inset 0 2px 4px rgba(90,60,20,.28);
    transition: background .2s, border-color .2s;
}
input.bs-toggle::before {
    content: ""; position: absolute; top: 1px; left: 1px;
    width: 18px; height: 18px; border-radius: 50%;
    background: var(--board-gold-dk); box-shadow: 0 2px 4px rgba(0,0,0,.35);
    transition: transform .2s cubic-bezier(.22,1,.36,1), background .2s;
}
input.bs-toggle:checked { background: rgba(42,157,95,.22); border-color: var(--marble-green); }
input.bs-toggle:checked::before { transform: translateX(18px); background: var(--marble-green); }

/* =========================================
   BOTTOM-SHEET MODALS (native redesign) — popup overlays become a dimmed scrim
   with the card slid up from the bottom (rounded top + drag handle). The card
   is positioned absolutely against the overlay's bottom, so this works whether
   the opening JS shows the overlay as display:block OR flex (both are used) --
   we don't force the overlay's display, so the existing show/hide still works.
   ========================================= */
.sheet-overlay {
    background-color: rgba(35,22,8,.55) !important;
    background-image: none !important;
    padding: 0 !important;
}
.sheet-overlay > .modal-card,
.sheet-overlay > .rules-card {
    position: absolute; left: 0; right: 0; bottom: 0;
    width: 100% !important; max-width: 100% !important; margin: 0 !important;
    max-height: 92%; overflow-y: auto;
    border-radius: 22px 22px 0 0 !important; border-bottom: none !important;
    padding-top: 26px !important;
    box-shadow: 0 -14px 40px rgba(35,22,8,.5) !important;
    scrollbar-width: none;
}
.sheet-overlay > .modal-card::-webkit-scrollbar,
.sheet-overlay > .rules-card::-webkit-scrollbar { display: none; }
/* drag-handle affordance at the top of the sheet */
.sheet-overlay > .modal-card::before,
.sheet-overlay > .rules-card::before {
    content: ""; position: absolute; top: 9px; left: 50%; transform: translateX(-50%);
    width: 44px; height: 5px; border-radius: 3px; background: var(--board-gold-dk); opacity: .5;
    z-index: 3;
}

/* =========================================
   BOOT SPLASH (native redesign) — animated rainbow logo, fades to the menu.
   ========================================= */
#splashScreen {
    position: fixed; inset: 0; z-index: 3000;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px;
    background-color: var(--table);
    background-image:
        radial-gradient(circle at 50% 40%, rgba(255,255,255,.5), transparent 60%),
        radial-gradient(rgba(150,110,60,.14) 1.6px, transparent 1.7px);
    background-size: 100% 100%, 15px 15px;
    transition: opacity .5s ease, visibility .5s;
}
#splashScreen.gone { opacity: 0; visibility: hidden; pointer-events: none; }
.splash-logo {
    font-family: 'Lilita One', cursive; font-size: clamp(46px, 15vw, 74px); line-height: .9;
    text-align: center; letter-spacing: 1px; margin: 0; position: relative;
    background: var(--logo-rainbow);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    filter: drop-shadow(0 3px 0 rgba(90,60,20,.35));
}
.splash-sub {
    color: var(--ink-soft); font-family: 'DM Sans', sans-serif; font-weight: 900;
    letter-spacing: 3px; font-size: 12px; margin: 0;
}
.splash-marble {
    width: 28px; height: 28px; border-radius: 50%;
    background: radial-gradient(circle at 32% 28%, #fff 0 8%, #f4a300 40%, #b8700a 100%);
    box-shadow: 0 4px 8px rgba(0,0,0,.3), inset 0 -4px 6px rgba(0,0,0,.25);
}
@media (prefers-reduced-motion: no-preference) {
    @keyframes bsSplashPop  { from { opacity: 0; transform: translateY(14px) scale(.85); } to { opacity: 1; transform: none; } }
    @keyframes bsSplashRoll { from { opacity: 0; transform: translateX(-120px) rotate(-360deg); } to { opacity: 1; transform: none; } }
    @keyframes bsSplashSweep { to { transform: translateX(120%); } }
    .splash-marble { animation: bsSplashRoll 1s cubic-bezier(.22,1,.36,1) .1s backwards; }
    .splash-logo   { animation: bsSplashPop .55s cubic-bezier(.22,1,.36,1) .15s backwards; }
    .splash-logo::after {
        content: ""; position: absolute; inset: -10% -5%;
        background: linear-gradient(105deg, transparent 42%, rgba(255,255,255,.72) 50%, transparent 58%);
        mix-blend-mode: overlay; transform: translateX(-120%);
        animation: bsSplashSweep 1.2s ease .45s 1 forwards;
    }
    .splash-sub    { animation: bsSplashPop .5s cubic-bezier(.22,1,.36,1) .5s backwards; }
}

/* ---- CARDS = boxed board-game frames ---- */
h1, h2, h3, .retro-heading {
    font-family: 'Lilita One', cursive; color: var(--ink);
    margin: 0; letter-spacing: .5px; text-shadow: none;
}

.menu-card {
    background: var(--parchment);
    border: 3px solid var(--board-gold);
    box-shadow: 0 12px 26px rgba(90,60,20,.22), inset 0 0 0 2px rgba(255,255,255,.45);
    border-radius: 18px;
    padding: 14px;
    width: 100%;
    max-width: 100% !important;
    position: relative; margin: 6px auto 20px auto;
}

.wood-panel {
    background: rgba(255,255,255,.4);
    border: 2px solid var(--board-gold);
    border-radius: 12px; position: relative; width: 100%;
    box-shadow: inset 0 2px 4px rgba(255,255,255,.6);
}

/* =========================================
   COLOR SYSTEM (native redesign) — marble hues splashed across the shell.
   A reusable colored top-cap: default is the full marble rainbow; an element
   can override the hue with `--c`. Kept off containers whose top edge already
   holds content (e.g. .premium-list-section's pulled-up lobby tabs).
   ========================================= */
.login-card, .premium-chat-section { position: relative; }
.login-card::before, .premium-chat-section::before {
    content: ""; position: absolute; top: -2px; left: -2px; right: -2px; height: 6px;
    border-radius: 13px 13px 0 0;
    background: var(--c, linear-gradient(90deg,#e63946,#f4a300 25%,#2a9d8f 50%,#457b9d 75%,#6a4c93));
}
.premium-chat-section { --c: var(--marble-teal); }

/* rainbow crown on the signed-in player bar (#userProfileUI is a .wood-panel) */
#userProfileUI { overflow: hidden; }
#userProfileUI::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0; height: 5px; z-index: 2;
    background: linear-gradient(90deg,#e63946,#f4a300,#2a9d5f,#2a9d8f,#457b9d,#6a4c93);
}

/* colour-coded career stat cards */
.stat-card-lg { position: relative; }
.stat-grid .stat-card-lg:nth-child(1) { border-top: 4px solid var(--marble-blue); }
.stat-grid .stat-card-lg:nth-child(2) { border-top: 4px solid var(--marble-green); }
.stat-grid .stat-card-lg:nth-child(3) { border-top: 4px solid var(--marble-amber); }
.stat-grid .stat-card-lg:nth-child(4) { border-top: 4px solid var(--marble-red); }

/* modal title accents -- .refresh-text/.premium-list-header are shared
   markup used by every popup header, so each overlay's own id gives it a
   signature marble hue (mirrors the mockup's per-sheet <span class="ttl">
   colour): Marble Works = violet, Social = teal, Admin/Career Profile =
   gold, Global Chat (lobby screen) = teal to match its own top-cap above. */
#marbleWorksPopup .refresh-text { color: var(--marble-violet); }
#marbleWorksPopup .premium-list-header { border-bottom-color: var(--marble-violet); }
#friendsOverlay .refresh-text { color: var(--marble-teal); }
#friendsOverlay .premium-list-header { border-bottom-color: var(--marble-teal); }
#adminOverlay .refresh-text { color: var(--marble-amber); }
#adminOverlay .premium-list-header { border-bottom-color: var(--marble-amber); }
#profileOverlay .refresh-text { color: var(--marble-amber); }
#profileOverlay .premium-list-header { border-bottom-color: var(--marble-amber); }
/* "CAREER RECORD" wood-panel heading -- index.html sets an inline
   color:var(--ink) on this h3, so it needs !important to pick up the same
   gold accent as the rest of this modal (consistent with other inline-style
   overrides already in this file, e.g. #playerCountDesc below). */
#profileOverlay .wood-panel > h3 { color: var(--marble-amber) !important; }
.premium-chat-section .refresh-text { color: var(--marble-teal); }
/* SOCIAL panel's ONLINE/OFFLINE group labels pick up the same teal as the
   panel's own title instead of the generic small-label brown. */
.friends-group-label { color: var(--marble-teal); }

/* retire the old dark scrollwork corners */
.scrollwork::before, .scrollwork::after { display: none !important; }

/* =========================================
   LOGO
   ========================================= */
.logo-container {
    width: 100%; max-width: 100%; margin: 0 auto 15px auto;
    padding: 24px 15px; text-align: center;
    background: var(--parchment);
    border: 3px solid var(--board-gold); border-radius: 18px;
    box-shadow: inset 0 0 0 2px rgba(255,255,255,.45), 0 12px 26px rgba(90,60,20,.22);
    position: relative;
    background-image: radial-gradient(rgba(150,110,60,0.12) 1.6px, transparent 1.7px);
    background-size: 15px 15px;
}
.game-logo {
    font-family: 'Lilita One', cursive; font-size: clamp(40px, 12vw, 60px);
    margin: 0; line-height: .95; letter-spacing: 1px;
    /* smooth full-spectrum rainbow sweep (was hard-stop colour blocks) */
    background: var(--logo-rainbow);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
    text-shadow: none !important;
    filter: drop-shadow(0px 3px 0px rgba(90,60,20,.35));
}
/* Each logo line ("BALL" / "SMACK!") gets its OWN full-width gradient, so both
   lines run red->violet instead of each sampling only the middle of one shared
   gradient (which was cutting off red at the start and violet at the end). */
.game-logo span, .splash-logo span {
    display: block;
    background: var(--logo-rainbow);
    -webkit-background-clip: text; background-clip: text;
    -webkit-text-fill-color: transparent; color: transparent;
}
.logo-subtext {
    color: var(--ink-soft); font-size: clamp(10px, 2.5vw, 13px);
    font-family: 'DM Sans', sans-serif; font-weight: 900;
    letter-spacing: 3px; margin: 12px 0 0 0; text-shadow: none;
}

/* =========================================
   BUTTON SYSTEM — chunky glossy marbles w/ hard drop
   ========================================= */
button {
    font-family: 'Lilita One', cursive;
    font-weight: 400;
    font-size: 13px;
    padding: 11px 14px;
    border-radius: 12px;
    cursor: pointer;
    transition: transform 0.06s, box-shadow 0.06s;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #fff;
    text-shadow: 0 1px 1px rgba(0,0,0,.3);
    border: 2px solid rgba(0,0,0,.18);
    background:
        radial-gradient(circle at 30% 22%, rgba(255,255,255,.7), rgba(255,255,255,0) 44%),
        linear-gradient(165deg,#8a7355,#6d5738 55%,#4a3826);
    box-shadow: inset 0 -4px 7px rgba(0,0,0,.26), inset 0 2px 3px rgba(255,255,255,.35), 0 5px 0 rgba(0,0,0,.22), 0 8px 12px rgba(0,0,0,.15);
}
button:active:not(:disabled) {
    transform: translateY(4px);
    box-shadow: inset 0 3px 6px rgba(0,0,0,.4), 0 1px 0 rgba(0,0,0,.2) !important;
}
button:disabled { opacity: .45; cursor: not-allowed; transform: none !important; box-shadow: none !important; }

/* marble color maps */
.btn-green, .btn-premium-action, .victory-btn {
    background: radial-gradient(circle at 30% 22%, rgba(255,255,255,.7), rgba(255,255,255,0) 44%), linear-gradient(165deg,#57c98a,#2a9d5f 55%,#1c7a45);
    border-color: #1c7a45; box-shadow: inset 0 -4px 7px rgba(0,0,0,.24), inset 0 2px 3px rgba(255,255,255,.4), 0 5px 0 #1c7a45, 0 8px 12px rgba(42,157,95,.28);
}
.btn-red {
    background: radial-gradient(circle at 30% 22%, rgba(255,255,255,.65), rgba(255,255,255,0) 44%), linear-gradient(165deg,#f0646f,#e63946 55%,#b02a36);
    border-color: #8f2318; box-shadow: inset 0 -4px 7px rgba(0,0,0,.24), inset 0 2px 3px rgba(255,255,255,.4), 0 5px 0 #8f2318, 0 8px 12px rgba(230,57,70,.28);
}
.btn-blue, .btn-premium {
    background: radial-gradient(circle at 30% 22%, rgba(255,255,255,.65), rgba(255,255,255,0) 44%), linear-gradient(165deg,#6fa3c4,#457b9d 55%,#2f5c78);
    border-color: #2f5c78; box-shadow: inset 0 -4px 7px rgba(0,0,0,.24), inset 0 2px 3px rgba(255,255,255,.4), 0 5px 0 #2f5c78, 0 8px 12px rgba(69,123,157,.28);
}
.btn-gold, .btn-lobby-join {
    background: radial-gradient(circle at 30% 22%, rgba(255,255,255,.85), rgba(255,255,255,0) 44%), linear-gradient(165deg,#ffce6b,#f4a300 55%,#c47f00);
    border-color: #b8860b; color: #5a3d00; text-shadow: 0 1px 0 rgba(255,255,255,.4);
    box-shadow: inset 0 -4px 7px rgba(0,0,0,.2), inset 0 2px 3px rgba(255,255,255,.55), 0 5px 0 #b8860b, 0 8px 12px rgba(244,163,0,.3);
}
/* gray = neutral parchment/bone button */
.btn-gray, .btn-book-close, .btn-bone, .btn-plastic-ivory {
    background: linear-gradient(180deg,#fdf8e9 0%,#eaddc5 100%) !important;
    border: 2px solid var(--board-gold-dk) !important;
    color: var(--ink) !important;
    text-shadow: 0 1px 0 rgba(255,255,255,.8) !important;
    box-shadow: inset 0 2px 4px rgba(255,255,255,.85), 0 5px 0 var(--board-gold-dk), 0 8px 12px rgba(90,60,20,.16) !important;
}
/* Lobby seat buttons carry their SEAT's marble colour, set inline per-seat in
   network.js. They must NOT inherit the bone background above (its !important
   was clobbering the colour), so they get their own glossy plastic frame that
   leaves the inline background + white label intact. */
.btn-plastic-seat {
    border: 2px solid rgba(0,0,0,.28) !important;
    color: #fff !important;
    text-shadow: 0 1px 2px rgba(0,0,0,.45) !important;
    box-shadow: inset 0 2px 5px rgba(255,255,255,.45), inset 0 -4px 7px rgba(0,0,0,.28), 0 4px 8px rgba(90,60,20,.2) !important;
}
.btn-bone.active-mode {
    background: radial-gradient(circle at 30% 22%, rgba(255,255,255,.6), rgba(255,255,255,0) 44%), linear-gradient(165deg,#f0646f,#e63946 55%,#b02a36) !important;
    border-color: #8f2318 !important; color: #fff !important;
    text-shadow: 0 1px 1px rgba(0,0,0,.3) !important;
    box-shadow: inset 0 3px 6px rgba(0,0,0,.3), 0 2px 0 #8f2318 !important;
    transform: translateY(3px);
}

.btn-icon-round { width: 34px; height: 34px; border-radius: 50%; padding: 0 !important; display: flex; align-items: center; justify-content: center; font-size: 16px; flex: none !important; min-width: 0; }
.btn-icon-small { width: 28px; height: 28px; font-size: 14px; }
.btn-small { padding: 6px 12px !important; font-size: 11px !important; height: auto; box-shadow: inset 0 2px 3px rgba(255,255,255,.7), 0 3px 0 var(--board-gold-dk) !important; }

/* =========================================
   INPUTS
   ========================================= */
input[type="text"], input[type="password"], input[type="email"] {
    background: var(--parchment); border: 2px solid var(--board-gold); border-radius: 10px;
    color: var(--ink); padding: 9px 11px; font-size: 13px; font-family: 'DM Sans', sans-serif; font-weight: 600;
    outline: none; transition: border-color 0.2s; max-width: 100%;
    box-shadow: inset 0 2px 4px rgba(120,90,45,.12);
}
input::placeholder { color: var(--ink-faint); }
input:focus { border-color: var(--marble-red); box-shadow: 0 0 0 3px rgba(230,57,70,.18), inset 0 2px 4px rgba(120,90,45,.12); }

::-webkit-scrollbar { width: 5px; }
::-webkit-scrollbar-track { background: rgba(120,90,45,.08); border-radius: 3px; }
::-webkit-scrollbar-thumb { background: var(--board-gold-dk); border-radius: 3px; }

/* Marble Works skin grids (marbleWorks.js): the default 5px thumb above is
   too thin to reliably grab, especially on touch -- a wider, higher-contrast
   thumb plus an explicit touch-action so a swipe that starts on a skin card
   itself still pans the list instead of being eaten by the card's own
   pointerdown handler. */
.mw-grid-scroll { scrollbar-width: auto; touch-action: pan-y; }
.mw-grid-scroll::-webkit-scrollbar { width: 10px; }
.mw-grid-scroll::-webkit-scrollbar-track { background: rgba(120,90,45,.14); border-radius: 5px; }
.mw-grid-scroll::-webkit-scrollbar-thumb { background: var(--board-gold-dk); border-radius: 5px; border: 2px solid rgba(255,255,255,.3); }

/* =========================================
   LAYOUT HELPERS + PROFILE + HUB + LISTS + CHAT
   ========================================= */
.flex-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.flex-nowrap { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.flex-between { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; }

.sign-out-btn { position: absolute; top: 8px; right: 8px; margin: 0; z-index: 10; }
.profile-info-container { padding: 8px 50px 8px 8px; }
.avatar-wrapper { position: relative; flex-shrink: 0; width: 42px; height: 42px; }
.avatar-wrapper img { width: 100%; height: 100%; border-radius: 50%; border: 2px solid var(--board-gold); object-fit: cover; box-shadow: inset 0 -2px 5px rgba(0,0,0,.25); background:#fff; }
#profileName { font-size: 17px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: 'Lilita One', cursive; font-weight: 400; }
.stats-text { font-size: 11px; color: var(--ink-soft); margin-top: 3px; white-space: nowrap; font-weight: 700; }
.highlight-gold { color: var(--marble-amber); font-weight: bold; }
.divider { margin: 0 3px; color: var(--board-gold); }
.bullet { margin: 0 2px; }

.login-card { padding: 12px 15px; }
.login-header { text-align: center; color: var(--ink); font-size: 14px; letter-spacing: 1px; margin-bottom: 12px; }
.login-grid { display: flex; flex-direction: column; gap: 12px; }
.google-auth-col { display: flex; justify-content: center; align-items: center; width: 100%; }
#googleLoginUIContainer { min-height: 40px; display: flex; align-items: center; justify-content: center; width: 100%; }
#googleLoginUI { width: 100%; display: flex; justify-content: center; }
.login-divider { display: flex; align-items: center; text-align: center; color: var(--ink-faint); font-size: 10px; font-weight: 900; letter-spacing: 1px; }
.login-divider::before, .login-divider::after { content: ""; flex: 1; border-bottom: 2px solid var(--board-gold); }
.login-divider::before { margin-right: 10px; } .login-divider::after { margin-left: 10px; }
.custom-auth-col { display: flex; flex-direction: column; gap: 8px; width: 100%; }
.custom-auth-inputs { display: flex; flex-direction: column; gap: 8px; }
.custom-auth-inputs input, .custom-auth-inputs button { height: 38px; width: 100%; margin: 0; }
.remember-me-container { text-align: center; margin-top: 10px; }
.remember-me-container label { color: var(--ink-soft); font-size: 11px; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; font-weight: 700; }
#authRememberMe { width: 14px; height: 14px; margin: 0; accent-color: var(--marble-red); }
#authErrorText { color: var(--marble-red); font-size: 11px; margin: 10px 0 0 0; text-align: center; font-weight: bold; }

.premium-action-hub { padding: 5px 0 15px 0; display: flex; flex-direction: column; gap: 10px; }
.action-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; width: 100%; }
.join-row { background: var(--parchment); border: 2px solid var(--board-gold); border-radius: 12px; padding: 5px 5px 5px 4px; display: flex; }
.premium-input { flex: 1; background: transparent; border: none; color: var(--ink); text-align: center; font-family: 'Lilita One', cursive; font-size: 15px; outline: none; letter-spacing: 2px; min-width: 50px; box-shadow: none; }
.premium-input::placeholder { color: var(--ink-faint); font-size: 13px; }
.btn-premium { flex: 1; }
.btn-premium-action { flex-shrink: 0; }
.premium-checkbox { color: var(--ink-soft); font-size: 12px; font-weight: 700; display: flex; align-items: center; gap: 5px; cursor: pointer; flex-shrink: 0; }
.premium-checkbox input { accent-color: var(--marble-red); width: 15px; height: 15px; margin: 0; }

.premium-list-section { display: flex; flex-direction: column; height: 220px; margin-bottom: 5px; background: rgba(255,255,255,.35); border: 2px solid var(--board-gold); border-radius: 14px; padding: 10px; position: relative; }

/* LOBBY TABS: a real tab-control look, not just two buttons in a row --
   pulled out to the section's own top/side edges (negative margin cancels
   the section's own padding) so the strip spans its full width and sits
   flush against its top border, like the section is the tab content panel.
   The ACTIVE tab's fill matches a lobby card's parchment tone and its
   bottom edge has no border, so it visually fuses into the list below;
   the inactive tab stays a duller, recessed tone with a hairline border
   all around (including its bottom, which reads as the seam separating it
   from the active content panel) so it clearly reads as "behind". */
.lobby-tabs { display: flex; margin: -10px -10px 8px -10px; position: relative; z-index: 2; }
.lobby-tab {
    flex: 1; padding: 9px 4px 8px; margin: 0; border: none;
    font-family: 'Lilita One', cursive; font-size: 12px; letter-spacing: .5px;
    cursor: pointer; box-shadow: none !important;
    border-radius: 0; transition: background .15s, color .15s, border-color .15s;
    /* Inactive: a cool blue-teal wash (distinct HUE from the active tab, not
       just a darker shade of the same tan) so the two read as genuinely
       different options rather than light/dark variants of one color. */
    background: linear-gradient(180deg, rgba(69,123,157,.22), rgba(69,123,157,.12));
    color: var(--marble-blue);
    border-bottom: 2px solid var(--board-gold);
}
.lobby-tab:first-child { border-top-left-radius: 12px; }
.lobby-tab:last-child { border-top-right-radius: 12px; }
.lobby-tab + .lobby-tab { border-left: 1px solid rgba(120,90,45,.3); }
.lobby-tab.active {
    /* Active: warm gold wash (matches the game's premium/gold accent
       family) with a vivid red underline -- the same accent color as a
       lobby card's own left border stripe -- ties the tab to the content
       it controls instead of just lightening the same neutral tone. */
    background: linear-gradient(180deg, #ffe8b3, #f4c96b);
    color: var(--ink); font-weight: 700;
    border-bottom: 3px solid var(--marble-red);
}
.premium-chat-section { display: flex; flex-direction: column; height: 170px; background: rgba(255,255,255,.35); border: 2px solid var(--board-gold); border-radius: 14px; padding: 10px; margin-top: 10px; }
.premium-list-header { border-bottom: 2px solid var(--board-gold); padding-bottom: 6px; margin-bottom: 6px; }
.refresh-text { font-family: 'Lilita One', cursive; color: var(--ink); font-size: 14px; letter-spacing: .5px; }

.premium-lobby-list { flex: 1; overflow-y: auto; padding-right: 4px; display: flex; flex-direction: column; gap: 6px; }
.premium-lobby-card { background: var(--parchment); border: 1px solid #e0c98f; border-left: 4px solid var(--marble-red); border-radius: 10px; padding: 8px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 5px rgba(90,60,20,.1); }
.lobby-card-info { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; }
.lobby-host-avatar { width: 32px; height: 32px; border-radius: 50%; border: 2px solid var(--board-gold); object-fit: cover; background: #fff; flex-shrink: 0; box-shadow: inset 0 -2px 4px rgba(0,0,0,.2); }
.lobby-host-name { font-weight: 400; color: var(--ink); font-size: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: 'Lilita One', cursive; }
.lobby-status { font-size: 9px; color: var(--ink-faint); text-transform: uppercase; margin-top: 2px; font-weight: 700; }
.lobby-card-action { display: flex; flex-direction: row; align-items: center; gap: 10px; flex-shrink: 0; }
.lobby-players { font-size: 13px; color: var(--marble-red); font-weight: bold; text-align: right; line-height: 1.1; font-family: 'Lilita One', cursive; }
.plyrs-label { font-size: 8px; color: var(--ink-faint); font-weight: normal; display: block; margin-top: 1px; }
.btn-lobby-join { padding: 7px 12px; font-size: 11px; }

/* FRIENDS SYSTEM */
.friends-badge {
    position: absolute; top: -4px; right: -4px; min-width: 16px; height: 16px; padding: 0 3px;
    background: var(--marble-red); color: #fff; border-radius: 8px; font-size: 9px; font-weight: 700;
    display: flex; align-items: center; justify-content: center; box-shadow: 0 1px 3px rgba(0,0,0,.4);
    font-family: 'Lilita One', cursive;
}
.friend-presence-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; border: 1px solid rgba(0,0,0,.3); }
.friend-presence-dot.status-menu { background: #f1c40f; }
.friend-presence-dot.status-lobby { background: #3498db; }
.friend-presence-dot.status-in-game { background: #2ecc71; }
.friend-presence-dot.status-offline { background: #7a7a7a; }

/* Unified SOCIAL panel (Phase: Friends/Player-Gallery merge). Online-count
   badge mirrors .friends-badge (gold pending-request badge) but sits on the
   opposite corner in green, so the two never overlap. */
.friends-badge-online { left: -4px; right: auto; background: #2ecc71; }
/* Group subheaders (ONLINE/OFFLINE) in the FRIENDS tab -- same small-label
   look already used for INCOMING/OUTGOING in the REQUESTS tab. */
.friends-group-label { font-size: 9px; letter-spacing: 1px; color: #8d6e63; margin: 8px 0 4px; font-weight: 700; }
/* Multi-invite selection checkbox on ONLINE friend rows (host-only). Scoped
   under .modal-card to beat the `.modal-card input { width:100% }` rule
   (line ~335) -- without the extra specificity the checkbox stretched to fill
   the whole action group and shoved the JOIN/INVITE/remove/block buttons off
   the card edge. */
.modal-card .friend-invite-checkbox { width: 16px; height: 16px; margin: 0; flex: 0 0 16px; cursor: pointer; accent-color: var(--marble-red); }

.premium-chat-box { flex-grow: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 4px; font-size: 12px; padding-bottom: 5px; word-wrap: break-word; text-align: left; color: var(--ink); }
.premium-chat-input { margin-top: auto; border-top: 2px solid var(--board-gold); padding-top: 6px; }
#globalChatInput { width: 100%; padding: 9px; font-size: 12px; border-radius: 9px; margin: 0; }
.chat-message { line-height: 1.4; }

/* =========================================
   MODALS / REGISTER / SETUP / LOBBY
   ========================================= */
.modal-card { max-width: 100% !important; text-align: center; margin: 15px auto; padding: 16px; }
.modal-card h2 { margin-bottom: 5px; font-size: 20px; color: var(--ink); }
.modal-avatar-container { display: flex; justify-content: center; margin-bottom: 12px; }
#regAvatarPreview { width: 54px; height: 54px; border-radius: 50%; border: 2px solid var(--board-gold); object-fit: cover; background: #fff; }
#btnUploadRegAvatar { position: absolute; bottom: -6px; right: -6px; background: var(--marble-amber) !important; border: 2px solid #fff !important; border-radius: 50%; width: 22px; height: 22px; font-size: 9px; color: #5a3d00 !important; padding: 0; box-shadow: 0 2px 4px rgba(0,0,0,.3) !important; }
.modal-card input { width: 100%; margin-bottom: 8px; }
#regUsername, #regDisplayName, #modalEditNameInput { text-transform: uppercase; }
#regErrorText, #verifyErrorText { color: var(--marble-red); font-size: 10px; margin-top: 8px; display: none; }
.verification-input { text-align: center; font-size: 20px; letter-spacing: 6px; margin: 12px 0; font-family: 'Lilita One', cursive; }
.warning-card { border-color: var(--marble-red) !important; }
.warning-card h2 { color: var(--marble-red); margin-bottom: 10px; }

.setup-header { color: var(--ink-soft); font-size: 11px; font-family: 'DM Sans'; font-weight: 900; letter-spacing: 1px; }
/* SETUP SCREEN: each section header picks up its own marble hue (room code
   = red like #setupRoomCodeText below it; game mode = blue like the
   2/3/4/6-player btn-blue buttons; AFK protection = green for "safety").
   The wrapping divs carry no class/id, only inline layout styles, so they're
   matched structurally (first-child / the divs' own border inline styles)
   rather than editing index.html. */
#setupOverlay .modal-card > h3.setup-header:first-child { color: var(--marble-red); }
#setupOverlay div[style*="border-bottom: 1px solid #8d6e63"] > h3.setup-header { color: var(--marble-blue); }
#setupOverlay div[style*="border-top: 1px solid #8d6e63"] > h3.setup-header { color: var(--marble-green); }
#setupRoomCodeText, #lobbyRoomCodeText, #setupRoomCodeText { color: var(--marble-red); }
#setupRoomCodeText { font-size: 34px; margin: 5px 0 12px 0; letter-spacing: 3px; }
.full-width-btn { margin-top: 15px; width: 100%; }
#playerCountDesc, #modeOverviewText { color: var(--ink) !important; background: rgba(255,255,255,.4) !important; border: 2px solid var(--board-gold) !important; border-radius: 10px !important; }
#playerCountDesc strong, #modeOverviewText { color: var(--marble-red) !important; }
#playerCountDesc strong { color: var(--marble-red) !important; }

.lobby-card { max-width: 100% !important; margin: 15px auto; padding: 15px; }
.lobby-header-row { margin-bottom: 12px; border-bottom: 2px solid var(--board-gold); padding-bottom: 8px; }
.lobby-label { color: var(--ink-soft); font-size: 9px; font-weight: 900; letter-spacing: 1px; }
#lobbyRoomCodeText { font-size: 26px; margin: 0; line-height: 1; letter-spacing: 2px; }
#lobbyPrivacyBadge { background: var(--marble-green); padding: 4px 10px; border-radius: 20px; font-size: 9px; border: none; color: #fff; font-family: 'Lilita One', cursive; letter-spacing: 1px; }
.invite-link-container { padding: 8px 10px; margin-bottom: 12px; border-radius: 10px; }
#inviteLink { font-size: 10px; color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
#copyBtn { margin-left: 8px; }
#lobbySlots { display: flex; flex-direction: column; gap: 6px; }
#waitingText, #victoryWaitingText { color: var(--ink-soft) !important; font-size: 11px; text-align: center; margin-top: 12px; text-shadow: none !important; }

.victory-overlay { background: transparent !important; box-shadow: none !important; justify-content: center; align-items: flex-end; padding-bottom: 10vh; pointer-events: none; }
.victory-btn { padding: 13px 26px; font-size: 15px; pointer-events: auto; }

/* =========================================
   BOOK PAGES (rules / manual) — parchment, on-theme
   ========================================= */
.book-page { background: var(--parchment); border: 3px solid var(--board-gold); border-radius: 6px 18px 18px 6px; box-shadow: inset 8px 0 15px rgba(120,90,45,.08), 0 12px 26px rgba(90,60,20,.28); padding: 20px 16px; max-width: 100% !important; margin: 20px auto 40px auto; }
.book-page .book-title { color: var(--marble-red); font-family: 'Lilita One', cursive; font-size: 24px; text-align: center; border-bottom: 2px solid var(--board-gold); padding-bottom: 12px; margin-bottom: 15px; text-shadow: none; }
.book-content { text-align: left; color: var(--ink); }
.book-content h3 { color: var(--marble-blue); font-family: 'Lilita One', cursive; font-size: 15px; margin-top: 16px; margin-bottom: 8px; text-shadow: none; letter-spacing: .5px; }
.book-content p { font-family: 'DM Sans', sans-serif; font-size: 13px; line-height: 1.6; margin: 0 0 10px 0; color: var(--ink); }
.book-content em { font-family: 'DM Sans', sans-serif; color: var(--marble-red); font-style: italic; font-size: 12px; display: block; margin-bottom: 12px; padding-left: 10px; border-left: 3px solid var(--marble-amber); }
.book-content strong { color: var(--marble-red); }
.ui-instructions { padding-left: 20px; margin: 10px 0 0 0; font-family: 'DM Sans', sans-serif; }
.ui-instructions li { font-size: 13px; line-height: 1.6; color: var(--ink); margin-bottom: 12px; }
.ui-instructions strong { color: var(--marble-blue); font-family: 'Lilita One', cursive; letter-spacing: .5px; font-size: 12px; }
.btn-book-close { margin-top: 15px; width: 100%; }

/* =========================================
   IN-GAME HUD + BOARD SURFACE
   ========================================= */
#gameWrapper { position: fixed; top: 0; bottom: 0; flex-direction: column; z-index: 100; display: flex; }
.canvas-wrapper { flex: 1 1 auto; width: 100%; position: relative; display: flex; align-items: flex-start; justify-content: center; margin: 0 auto; padding: 0 10px 10px 10px; }

/* Visual polish pass: the game screen wants to fill the real device width to
   maximize the play area -- release the ".overlay, #gameWrapper" mobile-lock
   column (a fixed max-width, centered via left:50%+translateX) that every
   menu/overlay still wants. Scoped to html.r3d (set by scene3d.js's
   initScene3d() only once the 3D scene has actually finished booting -- see
   its own comment: WebGL can still fail on a real device, so this is a real
   "confirmed live" gate, not just a leftover flag check), so menus and the
   plain #gameWrapper column stay untouched during that boot window / on
   failure. left/transform must be reset together with max-width -- the
   50%+translateX(-50%) centering trick only makes sense for a fixed-width
   box; left at 100% width would push half the screen off to the left. */
html.r3d #gameWrapper {
    left: 0 !important;
    right: 0 !important;
    transform: none;
    max-width: none;
    box-shadow: none;
}
html.r3d .canvas-wrapper { padding: 0; }
/* #gameCanvas3d's own width/max-height:84vh combo drove its box size
   independently of the real available space -- the height calculated from
   width+intrinsic-aspect regularly exceeded 84vh, got clamped, and left the
   box's OWN aspect mismatched from the canvas's intrinsic 768:1180, which is
   what object-fit:contain was pillarboxing/letterboxing inside the canvas
   (the gold #boardContainer background showing through at the sides).
   Naively fixing this with `height:100%` inside the flex chain doesn't
   work: <canvas> is a replaced element with its own intrinsic aspect-ratio
   sizing, and that fights percentage-height resolution inside nested
   flex-grow containers (confirmed empirically -- the canvas kept its
   unclamped intrinsic-aspect height and overflowed the panel by more the
   wider the viewport, e.g. -526px at 768px wide). Taking the canvas out of
   flow with position:absolute sidesteps the ambiguity entirely: percentage
   sizing on an absolutely-positioned element resolves reliably against its
   nearest positioned ancestor's padding box (here, .canvas-wrapper, which
   is already position:relative and is correctly flex-sized to the real
   available space up the #boardContainer/#gameWrapper chain).

   object-fit:cover (tried first) crops whichever axis overflows once the
   box's CSS aspect ratio drifts from the canvas's fixed 768:1180 backing
   store -- confirmed clipping the top of the board on a 768x1024 viewport.
   Fixed properly instead by making scene3d.js's resizeToDisplay() feed the
   camera the box's own live aspect ratio (so the composed render already
   matches the box shape) and using object-fit:fill here -- a distortion-
   free stretch once the aspect ratio is pre-matched, with zero cropping at
   any device shape. border-radius:0 removes the rounded corners the canvas's
   own pre-boot default carries (see #gameCanvas3d below), which read as a
   gap against the flush-with-panel full-bleed treatment. */
html.r3d #gameCanvas3d {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    max-height: none;
    object-fit: fill;
    border-radius: 0;
}
#boardContainer {
    flex: 1; position: relative; overflow: hidden; display: flex; flex-direction: column;
    background-color: #e8c88a;
    background-image:
        radial-gradient(circle at 50% 42%, rgba(255,248,230,0.7) 0%, rgba(210,150,70,0.22) 120%),
        radial-gradient(rgba(150,100,45,0.13) 1.6px, transparent 1.7px);
    background-size: 100% 100%, 15px 15px;
}
/* Pre-boot / WebGL-unavailable-fallback default (display:none) -- scene3d.js's
   initScene3d() flips this to display:block once the 3D scene has actually
   finished booting (see the html.r3d rule below for the full-bleed layout
   that then takes over). */
#gameCanvas3d { width: 100%; max-height: 84vh; object-fit: contain; display: none; border-radius: 8px; }

.in-game-ui {
    background: var(--panel-bg, linear-gradient(180deg,#fdf8e9 0%,#eaddc5 100%));
    border-top: 3px solid var(--panel-border-top, var(--board-gold));
    border-bottom: 3px solid var(--panel-border-bottom, var(--board-gold-dk));
    box-shadow: 0 -5px 15px rgba(90,60,20,.25);
    display: flex; flex-direction: column; padding: 10px; gap: 8px; z-index: 500; width: 100%; position: relative;
}
.ui-row { display: flex; align-items: center; width: 100%; gap: 8px; }
#turnDisplay { color: var(--ink); font-weight: 400; font-family: 'Lilita One', cursive; font-size: 14px; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex-grow: 1; min-width: 0; padding-right: 5px; }
.ui-action-group { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
/* BOARD LOCK LEVER: a small physical-switch look (track + sliding handle)
   rather than another flat icon button, so its job (a latch, not an action)
   reads at a glance. Unlocked = handle left / open-lock icon / gold track;
   locked = handle right / closed-lock icon / red track. */
.lever-toggle { position: relative; width: 40px; height: 22px; padding: 0; border: none; background: transparent; cursor: pointer; flex-shrink: 0; }
.lever-toggle .lever-track { position: absolute; inset: 0; background: var(--parchment); border: 2px solid var(--board-gold); border-radius: 12px; box-shadow: inset 0 2px 4px rgba(90,60,20,.3); transition: background .2s, border-color .2s; }
.lever-toggle .lever-handle { position: absolute; top: 1px; left: 1px; width: 18px; height: 18px; border-radius: 50%; background: var(--marble-amber); display: flex; align-items: center; justify-content: center; font-size: 10px; line-height: 1; box-shadow: 0 2px 4px rgba(0,0,0,.35); transition: transform .2s ease, background .2s; }
.lever-toggle.locked .lever-track { border-color: var(--marble-red); }
.lever-toggle.locked .lever-handle { transform: translateX(18px); background: var(--marble-red); }
#messageLog { font-family: 'DM Sans'; font-weight: 700; font-size: 11px; color: var(--panel-accent, var(--ink)); text-align: center; width: 100%; background: var(--parchment); border: 2px solid var(--board-gold); padding: 0 8px; border-radius: 9px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; height: 32px; display: flex; align-items: center; justify-content: center; box-shadow: inset 0 2px 4px rgba(120,90,45,.14); }
#lastRollDisplay { background: var(--parchment) !important; border: 2px solid var(--board-gold) !important; border-radius: 9px !important; box-shadow: inset 0 2px 4px rgba(120,90,45,.14) !important; align-items: center; justify-content: center; gap: 5px; height: 32px; padding: 0 10px; }
.roll-die-icon { font-size: 16px; line-height: 1; font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", sans-serif; }
.roll-die-num { font-family: 'Lilita One', cursive; font-size: 18px; line-height: 1; color: var(--marble-red); }
#btnPromptExit, #btnInGameRules { height: 34px; margin: 0; }
#inGameChatInput { flex-grow: 1; min-width: 0; height: 34px; border-radius: 18px; padding: 0 14px; font-size: 12px; margin: 0; }
#rollBtn { height: 38px; padding: 0 20px; margin: 0; }
#btnPolish { border-radius: 50%; padding: 0; margin: 0; height: 34px; width: 34px; }

/* EMOJI PICKER: popover anchored above the chat input row (#chatInputRow is
   the positioned ancestor -- see index.html). Built from main.js's
   QUICK_EMOJIS/ADD_EMOJIS arrays; markup here is just the shell + grid. */
#chatInputRow { position: relative; }
#emojiPicker {
    position: absolute; bottom: calc(100% + 8px); left: 0; right: 0; max-width: 100%;
    background: linear-gradient(180deg,#fdf8e9 0%,#eaddc5 100%);
    border: 2px solid var(--board-gold-dk); border-radius: 12px;
    box-shadow: 0 -6px 18px rgba(90,60,20,.3), inset 0 2px 4px rgba(255,255,255,.6);
    padding: 8px 10px 10px; z-index: 800; box-sizing: border-box;
}
/* teal to match the chat system's own teal identity (.premium-chat-section's
   top-cap / the Global Chat header text above) -- this picker only ever
   appears anchored above a chat input. */
.emoji-picker-label { font-size: 9px; letter-spacing: 1px; color: var(--marble-teal); margin: 6px 0 4px; font-weight: 700; }
.emoji-picker-label:first-child { margin-top: 0; }
.emoji-quick-row { display: flex; flex-wrap: nowrap; gap: 6px; justify-content: space-between; }
.emoji-add-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; max-height: 160px; overflow-y: auto; }
.emoji-btn {
    background: transparent; border: none; box-shadow: none; padding: 0; margin: 0;
    width: 34px; height: 34px; min-width: 34px; font-size: 20px; line-height: 1;
    display: flex; align-items: center; justify-content: center; border-radius: 8px;
    cursor: pointer; transition: background-color .12s ease;
}
.emoji-btn:hover, .emoji-btn:active { background: rgba(212,175,55,.25); }
/* MY EMOTES row: same button shell as .emoji-btn, but sized/padded to
   frame an admin-uploaded image rather than a single unicode glyph. */
.emoji-custom-row { display: flex; flex-wrap: wrap; gap: 6px; }
.emoji-btn-custom { padding: 3px; }
.emoji-btn-custom img { width: 26px; height: 26px; object-fit: contain; display: block; }
/* In-game chat :shortcode: -> <img> (network.js's renderChatText). Sized to
   sit inline with surrounding text at the default 12px chat-message font. */
.chat-emote { height: 22px; width: auto; vertical-align: middle; }

/* border tinted marble-blue (was flat board-gold) -- a small in-game-HUD
   accent that doesn't touch the --panel-* skin variables the rest of the
   HUD respects, since this button sits outside #ui/.in-game-ui entirely. */
.spectator-toggle { position: absolute; top: max(10px, env(safe-area-inset-top)); right: 10px; z-index: 400; background: var(--parchment); border: 2px solid var(--marble-blue); color: var(--ink); font-family: 'DM Sans'; font-weight: 800; font-size: 12px; padding: 5px 12px; border-radius: 20px; cursor: pointer; display: flex; align-items: center; gap: 4px; box-shadow: 0 4px 10px rgba(90,60,20,.25); }
/* The old slide-in .spectator-drawer is gone -- #spectatorList (same id,
   network.js still writes to it untouched) now lives inside the unified
   SOCIAL overlay's #friendsPlayersPane. */
.spectator-list { flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
.spectator-item { display: flex; align-items: center; gap: 8px; background: rgba(255,255,255,.5); border: 1px solid var(--board-gold); padding: 5px; border-radius: 8px; }
.spectator-item img { width: 24px; height: 24px; border-radius: 50%; border: 1px solid var(--board-gold); object-fit: cover; background: #fff; }
.spectator-name { font-size: 10px; color: var(--ink); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.empty-spectators { font-size: 10px; color: var(--ink-faint); text-align: center; font-style: italic; margin-top: 15px; }
.chat-overlay-container { position: absolute; bottom: 0; left: 0; right: 0; min-height: 40px; width: 100%; margin: 0 auto; display: flex; flex-direction: column; justify-content: flex-end; padding: 10px 15px; pointer-events: none; z-index: 700; }
#inGameChatOverlay { display: flex; flex-direction: column; gap: 4px; pointer-events: none; width: 100%; }

/* ANIM TEST HUD (animTestHud.js): admin-only debug tool for triggering
   celebration effects on demand during a live match, without needing to
   actually reach the game condition that normally fires them. Mirrors
   .spectator-toggle's corner-button styling but pinned to the opposite
   (left) corner and gated to display:none by default -- network.js's
   applyIsAdmin() is the sole thing that ever shows it. */
.animtest-toggle { position: absolute; top: max(10px, env(safe-area-inset-top)); left: 10px; z-index: 400; background: var(--parchment); border: 2px solid var(--marble-red); color: var(--ink); font-family: 'DM Sans'; font-weight: 800; font-size: 12px; padding: 5px 12px; border-radius: 20px; cursor: pointer; display: none; align-items: center; gap: 4px; box-shadow: 0 4px 10px rgba(90,60,20,.25); }
.animtest-panel { position: absolute; top: max(46px, calc(env(safe-area-inset-top) + 46px)); left: 10px; z-index: 400; background: var(--parchment); border: 2px solid var(--marble-red); border-radius: 12px; padding: 10px; display: none; flex-direction: column; gap: 6px; width: 190px; box-shadow: 0 4px 14px rgba(90,60,20,.3); }
.animtest-panel.open { display: flex; }
.animtest-panel select, .animtest-panel button { font-family: 'DM Sans'; font-size: 11px; padding: 5px 8px; border-radius: 8px; border: 1px solid var(--board-gold); cursor: pointer; }
.animtest-panel button { background: var(--marble-blue); color: #fff; font-weight: 700; }
.animtest-panel button.animtest-clear { background: var(--marble-red); }
.animtest-panel .animtest-label { font-size: 10px; color: var(--ink); font-weight: 700; }

/* =========================================
   STAT CARDS / MARBLE WORKS / TEXT HELPERS
   ========================================= */
.stat-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 15px; }
.stat-card-lg { background: var(--parchment); border: 2px solid var(--board-gold); border-radius: 10px; padding: 10px 5px; text-align: center; box-shadow: inset 0 2px 4px rgba(255,255,255,.6); }
.stat-card-lg .stat-title { font-size: 9px; color: var(--ink-soft); font-weight: 900; letter-spacing: 1px; margin-bottom: 4px; }
.stat-card-lg .stat-value { font-size: 22px; font-family: 'Lilita One', cursive; text-shadow: none; margin-bottom: 2px; color: var(--ink); }
.stat-card-lg .stat-desc { font-size: 8px; color: var(--ink-faint); text-transform: uppercase; }
.win-rate-container { background: rgba(255,255,255,.4); padding: 10px; border-radius: 10px; border: 2px solid var(--board-gold); position: relative; }
/* same reusable colored top-cap as .login-card/.premium-chat-section above,
   sized down to this smaller card's 10px radius */
.win-rate-container::before { content: ""; position: absolute; top: -2px; left: -2px; right: -2px; height: 4px; border-radius: 8px 8px 0 0; background: var(--marble-blue); }
.win-rate-container h4 { color: var(--marble-blue) !important; }
.win-rate-row { font-size: 11px; color: var(--ink); font-family: 'DM Sans'; }
.win-rate-box { text-align: center; }
.win-rate-box span:last-child { font-size: 8px; color: var(--ink-faint); }
/* mode labels (DUEL / FREE-FOR-ALL / FREE-FOR-ALL) each get a distinct
   marble hue instead of one flat grey, echoing the mockup's colour-coded
   mode pills since these rows have no separate badge element to colour */
.win-rate-row .win-rate-box:nth-child(1) span:last-child { color: var(--marble-red); }
.win-rate-row .win-rate-box:nth-child(2) span:last-child { color: var(--marble-blue); }
.win-rate-row .win-rate-box:nth-child(3) span:last-child { color: var(--marble-teal); }
.flavor-text { text-align: center; font-size: 9px; color: var(--ink-faint); font-style: italic; margin-top: 12px; line-height: 1.3; }

.tiny-playercard { background: var(--parchment); border: 1px solid #e0c98f; border-left: 4px solid var(--marble-amber); border-radius: 10px; padding: 8px; display: flex; align-items: center; gap: 10px; box-shadow: 0 2px 5px rgba(90,60,20,.12); }
.tpc-avatar { width: 36px; height: 36px; border-radius: 50%; border: 2px solid var(--board-gold); object-fit: cover; background: #fff; flex-shrink: 0; }
.tpc-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.tpc-name { color: var(--ink); font-size: 12px; font-family: 'Lilita One', cursive; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* AFK PROTECTION: greys out a seat's player-gallery card while it's held
   (server-side pendingDepartures) and tags it with a countdown -- see
   network.js's syncState handler. */
.tiny-playercard-afk { opacity: 0.55; filter: grayscale(0.6); }
.tpc-afk-tag { background: var(--marble-amber); color: #fff; padding: 1px 5px; border-radius: 4px; font-size: 8px; margin-left: 4px; font-family: 'DM Sans'; font-weight: 700; }
.tpc-stats { display: flex; justify-content: space-between; align-items: center; background: rgba(255,255,255,.5); padding: 4px; border-radius: 6px; border: 1px solid var(--board-gold); }
.tpc-stat-box { display: flex; flex-direction: column; align-items: center; }
.tpc-val { font-size: 11px; font-weight: 700; font-family: 'DM Sans'; color: var(--ink); line-height: 1; }
.tpc-lbl { font-size: 7px; color: var(--ink-faint); margin-top: 2px; text-transform: uppercase; font-weight: 700; }

.highlight-green { color: var(--marble-green) !important; }
.highlight-red { color: var(--marble-red) !important; }
.text-rainbow { background: linear-gradient(90deg,#e63946,#f4a300,#2a9d5f,#457b9d,#6a4c93); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; font-family: 'Lilita One', cursive; text-shadow: none !important; }
.text-grey { color: var(--ink-faint); font-weight: bold; text-shadow: none !important; }

/* Marble Works tabs stay chunky; grids inherit button theme */
#mwStatusText { color: var(--marble-green) !important; font-family: 'DM Sans'; font-weight: 700; }

/* =========================================
   ADMIN PAGE
   ========================================= */
.admin-inline-panel { background: rgba(0,0,0,.25); border-radius: 8px; padding: 10px; margin-top: 8px; display: flex; flex-direction: column; gap: 8px; }
.admin-field-row { display: flex; align-items: center; gap: 8px; font-size: 11px; color: #d7ccc8; }
.admin-field-row label { flex: 0 0 120px; }
.admin-field-row input[type="text"], .admin-field-row input[type="number"] { flex: 1; }
.admin-field-row input[type="color"] { width: 36px; height: 28px; padding: 0; border: none; background: none; cursor: pointer; }
/* The CREATE tab renders on the light --parchment card, so its field text must
   be dark ink for contrast. The base .admin-field-row color above (#d7ccc8) is
   tuned for the DARK .admin-inline-panel used by the PLAYERS tab, so scope this
   override to the create pane only. */
#adminCreatePane .admin-field-row,
#adminCreatePane .admin-field-row label { color: var(--ink); }
#adminCreatePane input::placeholder { color: #8a6f4f; opacity: 1; }
#adminCreatePane input[type="file"] { color: var(--ink); }
.admin-chip { display: inline-flex; align-items: center; gap: 4px; background: rgba(0,0,0,.35); border-radius: 12px; padding: 3px 8px; font-size: 9px; margin: 2px 4px 2px 0; color: #eae0c8; }
.admin-chip button { background: none; border: none; color: #e74c3c; cursor: pointer; font-weight: bold; padding: 0 2px; font-size: 11px; line-height: 1; }
.admin-banned-tag { background: var(--marble-red); color: #fff; font-size: 8px; padding: 2px 6px; border-radius: 4px; font-weight: 700; letter-spacing: .5px; margin-left: 6px; }
.admin-custom-tag { background: var(--marble-amber); color: #fff; font-size: 8px; padding: 2px 6px; border-radius: 4px; font-weight: 700; }
.admin-builtin-tag { background: rgba(255,255,255,.25); color: #eae0c8; font-size: 8px; padding: 2px 6px; border-radius: 4px; font-weight: 700; }
.admin-manage-card { background: var(--parchment); border: 1px solid #e0c98f; border-radius: 10px; padding: 10px; display: flex; flex-direction: column; gap: 4px; align-items: center; text-align: center; }
.admin-manage-card .admin-manage-name { font-size: 11px; color: var(--ink); font-weight: 700; }
.admin-manage-card .admin-manage-actions { display: flex; gap: 6px; margin-top: 4px; }
.admin-manage-emote-thumb { width: 36px; height: 36px; object-fit: contain; background: rgba(0,0,0,.15); border-radius: 6px; }

/* CREATE tab's emote image field (admin.js's buildImageField) -- the one
   image-upload input on the Admin page besides the avatar uploads in
   auth.js, styled to sit inline in the same .admin-field-row as every other
   field's <input>. */
.admin-emote-image-preview { width: 40px; height: 40px; object-fit: contain; background: rgba(0,0,0,.2); border-radius: 6px; margin-left: 8px; }
.admin-emote-image-hint { font-size: 9px; color: #8d6e63; display: block; margin-top: 4px; }
