/* ========== GLOBAL UI SCALE ==========
   The HUD is authored at fixed pixel sizes and then scaled as a whole. The
   factors are recomputed by updateUIScale() in game.js on every resize, from
   the *measured* size of the panels — media-query steps used to do this, so a
   frame that fell between them (CrazyGames embeds at 1019x528) got the full
   desktop HUD and pushed the paint menu off the bottom of the screen.
     --ui-scale    HUD panels: left stats, paint menu, match banner, tutorial
     --ctl-scale   round corner buttons — floored so they stay tappable
     --card-scale  modal cards: overlay, shop, audio, private room, sitelock */
:root {
    --ui-scale: 1;
    --ctl-scale: 1;
    --card-scale: 1;
}

body {
    margin: 0;
    overflow: hidden;
    background-color: #1e272e;
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    color: white;
    /* Switched to a chunkier stack, adjust if you import a custom cartoon font! */
    font-family: 'Fredoka One', 'Comic Sans MS', 'Segoe UI', Tahoma, sans-serif;
    user-select: none;
    /* the page itself must never pan or zoom under a finger; scrollable
       panels opt back in with their own touch-action (see the bottom) */
    touch-action: none;
}

canvas {
    display: block;
    background: transparent;
}
/* ONLY the surfaces a finger plays or paints on swallow gestures. This used to
   be every canvas on the page, which quietly made album stickers and gallery
   thumbnails dead zones: a scroll starting on one could not pan its panel, and
   a gallery thumbnail fills its whole row — so most of the list did not scroll.
   (#me-canvas and #cpaint-canvas set their own touch-action: none below.) */
#gameCanvas { touch-action: none; }

/* ========== 🎬 LOADING SCREEN ==========
   Covers the first frame, which used to be the how-to-play panel over an
   empty canvas. Paints with nothing downloaded: the gradient, the title and
   the stickman are all CSS and inline SVG. #boot-art is the only part that
   needs the network and it is decorative — it fades in late or never, and
   the screen looks finished either way. */
#boot {
    position: fixed; inset: 0; z-index: 99999;
    display: flex; align-items: center; justify-content: center;
    background: radial-gradient(120% 120% at 50% 30%, #2b3a55 0%, #1a1f2b 55%, #12161f 100%);
    overflow: hidden;
    transition: opacity 0.45s ease;
}
#boot.gone { opacity: 0; pointer-events: none; }
#boot-art {
    position: absolute; inset: 0;
    background-image: url("images/cybernature.webp");
    background-size: cover; background-position: center;
    opacity: 0; transition: opacity 1.1s ease;
    filter: saturate(0.85) blur(1px);
}
#boot-art.in { opacity: 0.3; }
#boot-mid {
    position: relative; z-index: 1;
    display: flex; flex-direction: column; align-items: center; gap: 14px;
    padding: 0 20px; text-align: center;
}
#boot-guy { width: 92px; height: 115px; filter: drop-shadow(4px 5px 0 rgba(0,0,0,0.45)); }
/* the paint sweeps across, pauses, and goes again — the loop is the game */
#boot-paint { animation: boot-sweep 2.4s cubic-bezier(.5,0,.2,1) infinite; }
@keyframes boot-sweep {
    0%   { transform: translateX(0); }
    45%  { transform: translateX(120px); }
    55%  { transform: translateX(120px); }
    100% { transform: translateX(240px); }
}
#boot-title {
    margin: 0; font-size: clamp(26px, 6vw, 44px); line-height: 1.05;
    font-weight: 900; color: #fff; letter-spacing: 0.5px;
    text-shadow: 3px 3px 0 #000;
}
#boot-tag { margin: 0; font-size: clamp(12px, 2.6vw, 15px); color: #a9b7c9; font-weight: 600; }
#boot-bar {
    margin-top: 6px; width: min(240px, 60vw); height: 6px;
    background: rgba(255,255,255,0.14); border-radius: 999px; overflow: hidden;
}
#boot-bar i {
    display: block; width: 40%; height: 100%; border-radius: 999px;
    background: linear-gradient(90deg, #2ed573, #7bed9f);
    animation: boot-slide 1.3s ease-in-out infinite;
}
@keyframes boot-slide {
    0%   { transform: translateX(-110%); }
    100% { transform: translateX(260%); }
}
@media (prefers-reduced-motion: reduce) {
    #boot-paint, #boot-bar i { animation: none; }
    #boot-paint { transform: translateX(120px); }
}

/* ========== MAIN UI PANELS ========== */
#ui-container {
    position: absolute;
    top: 15px;
    left: 15px;
    pointer-events: none;
    background: #341f53;
    padding: 10px 12px; /* Slimmer padding */
    border-radius: 10px;
    border: 3px solid #000;
    box-shadow: 4px 4px 0px #000;
    width: 190px; /* Slimmed down from 220px */
    transform: scale(var(--ui-scale));
    transform-origin: top left;
    z-index: 10;
}

#how-to-play h2 {
    font-size: 18px;
    margin-bottom: 6px;
    color: #00d2d3;
    text-shadow: 2px 2px 0px #000;
}

#how-to-play p {
    font-size: 11px;
    margin: 3px 0;
    color: #fff;
    font-weight: bold;
}

h2 { 
    margin: 0 0 10px 0; 
    font-size: 22px; 
    color: #00d2d3; 
    text-shadow: 2px 2px 0px #000; /* Cartoon text drop shadow */
}
p { 
    margin: 5px 0; 
    color: #fff; 
    font-size: 13px; 
    font-weight: bold;
}

.ink-container { margin-top: 15px; }
.ink-title {
    font-weight: 900; 
    font-size: 14px; 
    color: #feca57;
    margin-bottom: 5px; 
    text-transform: uppercase; 
    letter-spacing: 1px;
    text-shadow: 1px 1px 0px #000;
}

/* CARTOON PROGRESS BARS */
#ink-bar-bg {
    width: 100%; 
    height: 16px; 
    background: #2d3436;
    border-radius: 8px; 
    overflow: hidden; 
    border: 3px solid #000; /* Thick outline */
    box-shadow: inset 0 3px 0 rgba(0,0,0,0.3); /* Inner shadow for depth */
}
#ink-bar-fill {
    width: 100%; 
    height: 100%; 
    background: #feca57; /* Flat bright color, no gradient */
    border-radius: 4px; 
    transition: width 0.1s linear, background-color 0.3s;
}

/* ========== PAINT MENU ========== */
#paintMenu {
    /* --paint-top keeps the menu clear of the round buttons in the corner */
    position: absolute; top: var(--paint-top, 20px); right: 20px;
    background: #205c6b; /* Solid teal/blue */
    padding: 20px; 
    border-radius: 16px; 
    display: flex; flex-direction: column; gap: 15px; align-items: center;
    transition: opacity 0.3s ease, transform 0.3s ease;
    opacity: 0; transform: scale(calc(var(--paint-scale, var(--ui-scale)) * 0.9)); pointer-events: none;
    transform-origin: top right;
    border: 4px solid #000;
    box-shadow: 6px 6px 0px #000; /* Hard offset shadow */
    box-sizing: border-box;
    /* Last resort on frames too short to scale the menu down and still read it:
       --ui-scale bottoms out and the rest of the list scrolls. updateUIScale()
       sets max-height, in the menu's own pre-scale pixels. */
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
    /* above the virtual JUMP button (z 20): while the menu is open it wins the
       overlap — painters are stationary, so JUMP being covered costs nothing */
    z-index: 25;
}
#paintMenu.visible { opacity: 1; transform: scale(var(--paint-scale, var(--ui-scale))); pointer-events: auto; }
.menu-title {
    font-weight: 900; font-size: 14px; text-transform: uppercase;
    letter-spacing: 1.5px; color: #fff;
    text-shadow: 2px 2px 0px #000;
}

input[type="color"] {
    -webkit-appearance: none; border: none; width: 46px; height: 46px;
    border-radius: 50%; cursor: pointer; padding: 0; background: none;
    transition: transform 0.2s ease;
}
input[type="color"]:hover { transform: scale(1.1); }
input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
input[type="color"]::-webkit-color-swatch {
    border: 3px solid #000; /* Black outline */
    border-radius: 50%; 
    box-shadow: 3px 3px 0px #000;
}

/* ---- paint menu tabs ---- */
.pm-tabs { display: flex; gap: 8px; width: 100%; }
.pm-tab {
    flex: 1;
    padding: 8px 6px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: 1px;
    color: #bfe3ea;
    background: #17454f;
    border: 3px solid #000;
    border-radius: 10px;
    box-shadow: 2px 2px 0 #000;
    cursor: pointer;
}
.pm-tab.active {
    color: #1e272e;
    background: #feca57;
    transform: translate(1px, 1px);
    box-shadow: 1px 1px 0 #000;
}
.pm-pane { display: flex; flex-direction: column; gap: 15px; align-items: center; width: 100%; }

/* ---- inline custom color picker ---- */
.cc-head { display: flex; align-items: center; justify-content: center; gap: 10px; }
#cc-eyedrop {
    width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    background: #17454f;
    border: 2px solid #000; border-radius: 8px;
    box-shadow: 2px 2px 0 #000;
    cursor: pointer; padding: 0;
}
#cc-eyedrop:active { transform: translate(1px, 1px); box-shadow: 1px 1px 0 #000; }
#cc-preview {
    display: inline-block;
    width: 26px; height: 26px;
    border-radius: 50%;
    background: #ffffff;
    border: 3px solid #000;
    box-shadow: 2px 2px 0 #000;
}
#custom-color-row { display: flex; flex-direction: column; gap: 8px; width: 100%; padding: 0 6px; box-sizing: border-box; }
#cc-hue, #cc-light { width: 100%; height: 18px; cursor: pointer; -webkit-appearance: none; appearance: none; border: 2px solid #000; border-radius: 9px; }
#cc-hue { background: linear-gradient(90deg, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00); }
#cc-light { background: linear-gradient(90deg, #000, #808080, #fff); }
#cc-hue::-webkit-slider-thumb, #cc-light::-webkit-slider-thumb {
    -webkit-appearance: none; appearance: none;
    width: 20px; height: 20px; border-radius: 50%;
    background: #fff; border: 3px solid #000;
}

/* HEXAGONAL PALETTE */
#hex-palette { display: flex; flex-direction: column; align-items: center; }
.hex-row { display: flex; justify-content: center; margin-top: -6px; }
.hex-row:first-child { margin-top: 0; }
.hex-cell {
    width: 24px; height: 27px; margin: 0 1px; cursor: pointer;
    clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
    transition: transform 0.1s ease;
    /* Note: CSS clip-path removes borders. If you want black borders on hexes, 
       consider using an SVG background image for the hexes instead. */
}
.hex-cell:hover { transform: scale(1.25); z-index: 2; }
.gray-row { margin-top: 6px; }
.custom-color-row { display: flex; align-items: center; gap: 10px; }
.menu-subtitle {
    font-size: 12px; font-weight: 900; text-transform: uppercase;
    letter-spacing: 1px; color: #feca57; text-shadow: 1px 1px 0px #000;
}

/* BRUSH BUTTONS */
.brush-container { display: flex; gap: 12px; align-items: flex-end; justify-content: center; height: 50px; }
.brush-btn {
    background: #636e72; 
    border: 3px solid #000;
    box-shadow: 3px 3px 0px #000;
    border-radius: 50%; cursor: pointer; display: flex; justify-content: center;
    align-items: center; color: white; transition: all 0.1s; font-weight: 900;
}
.brush-btn:hover { 
    background: #00d2d3;
    transform: translateY(-2px);
}
.brush-btn.active { 
    background: #feca57; 
    color: #000;
    transform: translate(3px, 3px); /* Press down effect */
    box-shadow: 0px 0px 0px #000; /* Remove shadow when pressed */
}

.brush-slider-row {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 0 4px;
    box-sizing: border-box;
}
#brush-slider {
    flex: 1;
    min-width: 0;
    accent-color: #feca57;
    cursor: pointer;
    height: 22px; /* generous touch target on phones */
}
.brush-preview {
    flex: 0 0 auto;
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #17323b;
    border: 2px solid #000;
    border-radius: 50%;
}
#brush-preview-dot {
    display: block;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #fff;
    border: 1px solid rgba(0,0,0,0.4);
}
#brush-size-num {
    flex: 0 0 auto;
    min-width: 22px;
    text-align: right;
    font-size: 12px;
    font-weight: 900;
    color: #feca57;
    font-variant-numeric: tabular-nums;
    text-shadow: 1px 1px 0 #000;
}

#btn-small { width: 32px; height: 32px; font-size: 12px; }
#btn-medium { width: 40px; height: 40px; font-size: 16px; }
#btn-large { width: 48px; height: 48px; font-size: 20px; }

/* VIRTUAL MOBILE CONTROLS */
#mobile-controls {
    display: none; 
    position: absolute; bottom: 30px; left: 0; width: 100%;
    pointer-events: none; z-index: 20;
}
#joystick-base {
    position: absolute; left: 40px; bottom: 0;
    width: 120px; height: 120px; 
    background: #fff; /* Solid white base */
    border: 4px solid #000; 
    border-radius: 50%;
    box-shadow: 4px 4px 0px #000;
    pointer-events: auto; 
    opacity: 0.8; /* Slight overall transparency instead of blur */
}
#joystick-stick {
    position: absolute; top: 35px; left: 35px;
    width: 50px; height: 50px; 
    background: #00d2d3;
    border: 3px solid #000;
    border-radius: 50%; 
    box-shadow: 3px 3px 0px #000;
    pointer-events: none;
}
#jump-btn {
    position: absolute; right: 40px; bottom: 10px;
    width: 90px; height: 90px; 
    background: #ff9f43;
    border: 4px solid #000; 
    border-radius: 50%;
    color: white; font-weight: 900; display: flex;
    justify-content: center; align-items: center;
    font-size: 24px; pointer-events: auto; 
    box-shadow: 5px 5px 0px #000;
    text-shadow: 2px 2px 0px #000;
}
#jump-btn:active {
    background: #feca57;
    transform: translate(5px, 5px);
    box-shadow: 0px 0px 0px #000;
}
/* 👊 seeker swing, stacked directly above JUMP so the same thumb reaches both.
   Only shown to a seeker on a touch device (see refreshTouchControls). */
#hit-btn {
    position: absolute; right: 40px; bottom: 118px;
    width: 84px; height: 84px;
    background: #ee5253;
    border: 4px solid #000;
    border-radius: 50%;
    color: #fff; font-weight: 900; display: none;
    justify-content: center; align-items: center;
    font-size: 22px; pointer-events: auto;
    box-shadow: 5px 5px 0px #000;
    text-shadow: 2px 2px 0px #000;
}
#hit-btn.visible { display: flex; }
#hit-btn:active {
    background: #ff6b6b;
    transform: translate(5px, 5px);
    box-shadow: 0px 0px 0px #000;
}
#hit-btn.spent { background: #6b4a4a; color: #c8b6b6; }

/* ========== MATCH HUD ========== */
#match-banner {
    position: absolute;
    top: 15px;
    left: 50%;
    transform: translateX(-50%) translateY(-100px) scale(var(--ui-scale));
    transform-origin: top center;
    display: flex;
    align-items: center;
    gap: 10px; /* Tighter gap */
    background: #341f53;
    border: 3px solid #000;
    box-shadow: 4px 4px 0px #000;
    padding: 6px 16px;
    border-radius: 10px; /* Slimmed padding */
    transition: transform 0.4s ease;
    z-index: 15;
    pointer-events: none;
}

    #match-banner.visible {
        transform: translateX(-50%) translateY(0) scale(var(--ui-scale));
    }

#match-role {
    font-weight: 900;
    font-size: 14px;
    letter-spacing: 1px;
    text-shadow: 1px 1px 0px #000;
}

    #match-role.hider {
        color: #feca57;
    }

    #match-role.seeker {
        color: #ff6b81;
    }

#match-phase {
    font-size: 13px;
    color: #fff;
    font-weight: bold;
}

#match-timer {
    font-size: 42px; /* SHRUNK FROM 72px! */
    font-weight: 900;
    color: #00d2d3;
    min-width: 65px;
    text-align: center;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    /* Scaled down the shadow to match the new font size */
    text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000, 4px 4px 0 #000;
}

/* ========== HIT CHARGES ========== */
#hit-ui {
    position: absolute;
    top: 0;
    left: 0; /* Reset anchor to top-left */
    display: none;
    align-items: center;
    gap: 8px;
    background: #341f53;
    border: 3px solid #000;
    box-shadow: 4px 4px 0px #000;
    padding: 6px 14px; /* Slimmed down slightly for better visibility */
    border-radius: 12px;
    transform-origin: 0 0; /* game.js does the scale math against this corner */
    z-index: 15;
    pointer-events: none;
    will-change: transform; /* Tells the browser to optimize for constant movement */
}
#hit-ui.visible { display: flex; }
.hit-label {
    font-size: 13px; font-weight: 900; text-transform: uppercase;
    letter-spacing: 1.5px; color: #fff; margin-right: 4px;
    text-shadow: 1px 1px 0px #000;
}
#hit-count-num {
    font-size: 26px;
    font-weight: 900;
    line-height: 1;
    min-width: 34px;
    text-align: center;
    color: #ff9f43;
    font-variant-numeric: tabular-nums;
    text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000;
}
#hit-count-num.low { color: #ff6b81; }
#hit-count-num.out { color: #ff4757; }

/* ========== OVERLAY (start / end screen) ========== */
#overlay-screen {
    position: absolute;
    inset: 0;
    z-index: 30;
    display: flex;
    justify-content: center;
    align-items: flex-start;
    background: transparent;
    padding-top: 30px;
    opacity: 0;
    visibility: hidden; /* <--- ADD THIS */
    pointer-events: none;
    transition: opacity 0.3s ease, visibility 0.3s; /* <--- UPDATE THIS */
}

    #overlay-screen.visible {
        opacity: 1;
        visibility: visible; /* <--- ADD THIS */
    }

.overlay-card {
    pointer-events: auto;
    background: #205c6b; 
    border: 4px solid #000;
    border-radius: 16px; padding: 24px 34px; max-width: 420px;
    box-sizing: border-box;
    zoom: max(0.85, var(--card-scale));
    /* max-height is read in the card's own (zoomed) pixels, hence the divide */
    max-height: calc((100vh - 56px) / max(0.85, var(--card-scale)));
    overflow-y: auto;
    text-align: center; 
    box-shadow: 8px 8px 0px #000;
}
#overlay-title { margin: 0 0 12px 0; font-size: 32px; font-weight: 900; color: #feca57; text-shadow: 3px 3px 0px #000; }
#overlay-text { font-size: 16px; font-weight: bold; color: #fff; line-height: 1.6; margin: 0 0 24px 0; }

#overlay-coins {
    margin: 0 0 14px 0;
    font-size: 30px;
    font-weight: 900;
    color: #feca57;
    text-shadow: 2px 2px 0 #000;
    font-variant-numeric: tabular-nums;
}
#overlay-coins .coin-ico { width: 0.85em; height: 0.85em; }
#overlay-coins-tag {
    display: block;
    font-size: 13px;
    color: #2ed573;
    text-shadow: 1px 1px 0 #000;
    min-height: 16px;
}
#double-coins-btn, #perk-trial-btn, #seeker-hits-btn, .gift-btn {
    margin: 0 0 14px 0;
    padding: 11px 20px;
    font-family: inherit;
    font-size: 15px;
    font-weight: bold;
    color: #1e272e;
    background: linear-gradient(90deg, #2ed573, #7bed9f);
    border: 3px solid #000;
    border-radius: 12px;
    box-shadow: 3px 3px 0 #000;
    cursor: pointer;
}
#double-coins-btn:active, #perk-trial-btn:active, .gift-btn:active { transform: translate(2px, 2px); box-shadow: 0 0 0 #000; }
#double-coins-btn:disabled, #perk-trial-btn:disabled, #seeker-hits-btn:disabled, .gift-btn:disabled { opacity: 0.55; cursor: default; transform: none; }
#perk-trial-btn { background: linear-gradient(90deg, #54a0ff, #7ec3ff); }
/* the seeker's setup-phase offer floats over the dark waiting veil */
/* 📐 ...BELOW the warm-up line, which it used to sit exactly on top of.
   Both are centred and both are placed as a fraction of the height, so
   they tracked each other down the screen and collided at every size a
   phone can be held at — this is not a small-screen case, it is all of
   them. The canvas draws (see the seekerWait block in game.js):

       countdown   min(240, h*0.32)px, centred on h*0.42
       warm-up     h*0.42 + min(150, h*0.22), which is h*0.64 on a phone

   so the band from h*0.26 to h*0.58 belongs to the number and h*0.64 to
   the line under it. bottom:26% put this button's top at h*0.74-49px,
   which lands on that line from 340px tall to 450px tall:

       915x412   text y=264   button 256..305    on top of it
       844x390   text y=250   button 240..289    on top of it
       740x340   text y=218   button 203..252    on top of it

   top:70% is the fix rather than a smaller bottom% because it scales
   with the same height the text does, so the gap stays proportional
   instead of closing again on the next screen size. */
#seeker-hits-btn { position: fixed; left: 50%; top: 70%; bottom: auto; transform: translateX(-50%); z-index: 30; margin: 0; }
#seeker-hits-btn:active { transform: translateX(-50%) translate(2px, 2px); box-shadow: 0 0 0 #000; }
/* 🎁 daily gift modal — same skeleton as achievements/quests */
#gift-screen { position: fixed; inset: 0; z-index: 96; display: none; align-items: center; justify-content: center;
    background: rgba(0,0,0,0.7); backdrop-filter: blur(3px); }
#gift-screen.visible { display: flex; }
.gift-card { width: min(360px, 92vw); text-align: center; }
.gift-card .gift-btn { width: 100%; box-sizing: border-box; margin-bottom: 10px; }
#gift-close { width: 38px; height: 38px; font-size: 18px; color: #fff; background: #576574; border: none; border-radius: 10px; cursor: pointer; }
.gift-day { font-size: 13px; color: #8395a7; margin: 2px 0 10px; letter-spacing: 1px; text-transform: uppercase; }
#gift-body { margin: 4px 0 16px; }
/* 🎁 the gift card's three days. The tiles are the return ladder's, so
   only the fit differs: a little tighter, because this row sits inside a
   modal instead of a full-width panel. (.gift-coins and .gift-item went
   with the single-number card they styled.) */
.gift-row { margin: 4px 0 12px; }
.gift-row .ladder-art { height: 58px; }
.gift-row .ladder-art canvas { width: 54px; height: 54px; }
.gift-swatch { width: 38px; height: 38px; border-radius: 50%; display: inline-block;
    border: 3px solid #0d1017; box-shadow: 0 0 0 2px rgba(255,255,255,0.25); }
.gift-note { font-size: 12px; color: #8395a7; margin: 0; }


#overlay-countdown {
    font-size: 24px; font-weight: 900; color: #00d2d3;
    margin: 0 0 20px 0; min-height: 24px;
    text-shadow: 2px 2px 0px #000;
}

/* ========== RANK / PROGRESS ========== */
.rank-container {
    margin-top: 5px;
}
/* Tighter top margin */
.rank-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin-bottom: 2px; /* Slimmed down from 5px */
}

#rank-name {
    font-weight: 900;
    font-size: 14px;
    color: #00d2d3; /* Scaled down slightly */
    text-transform: uppercase;
    letter-spacing: 1px;
    text-shadow: 1px 1px 0px #000;
}

#rank-points {
    font-size: 12px;
    font-weight: bold;
    color: #fff;
}

#rank-bar-bg {
    width: 100%;
    height: 10px;
    background: #2d3436; /* Thinner bar (was 16px) */
    border-radius: 5px;
    overflow: hidden;
    border: 3px solid #000;
}

#rank-bar-fill {
    width: 0%;
    height: 100%;
    background: #00d2d3;
    border-radius: 2px;
    transition: width 0.4s ease;
}

#rank-next {
    font-size: 10px;
    font-weight: bold;
    color: #feca57;
    margin-top: 2px;
}

#overlay-rank { margin: 0 0 18px 0; }
#overlay-rank .or-name {
    font-size: 24px; font-weight: 900; color: #00d2d3; margin-bottom: 6px;
    text-shadow: 2px 2px 0px #000;
}
#overlay-rank .or-bar-bg {
    width: 100%; height: 18px; background: #2d3436;
    border-radius: 9px; overflow: hidden; border: 3px solid #000;
}
#overlay-rank .or-bar-fill {
    height: 100%; background: #00d2d3;
    border-radius: 6px; transition: width 0.5s ease;
}
#overlay-rank .or-next { font-size: 13px; font-weight: bold; color: #fff; margin-top: 6px; }
#overlay-rank .or-up {
    font-size: 15px; font-weight: 900; color: #feca57; margin-top: 6px;
    text-shadow: 1px 1px 0px #000;
}

/* PAINT MENU HINTS */
.menu-hint {
    font-size: 10px; font-weight: bold; color: #feca57;
    letter-spacing: 0.5px; margin-left: 4px;
}
.menu-tip {
    font-size: 11px; color: #fff; text-align: center; font-weight: bold;
    margin-top: 4px;
}
.brush-btn { position: relative; }
.brush-btn .key {
    position: absolute; bottom: -6px; right: -6px;
    font-size: 10px; font-weight: 900; background: #ff9f43; color: #000;
    border-radius: 4px; padding: 2px 4px; line-height: 1;
    border: 2px solid #000;
}

/* ========== POSES (tab; buttons carry a thumbnail of the pose) ========== */
.pose-container {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
    margin-top: 5px;
    width: 100%;
}
.pose-btn canvas { width: 52px; height: 56px; }
.pose-name { display: block; font-size: 10px; margin-top: 2px; }

.pose-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 6px 4px 5px 4px;
    background: #636e72;
    border: 2px solid #000;
    border-radius: 8px;
    color: white;
    font-size: 10px;
    font-weight: 900;
    padding: 4px 8px;
    cursor: pointer;
    transition: all 0.1s;
    text-transform: uppercase;
    box-shadow: 2px 2px 0px #000;
}

    .pose-btn:hover {
        background: #00d2d3;
        transform: translateY(-2px);
    }

    .pose-btn.active {
        background: #ff9f43;
        color: #000;
        transform: translate(2px, 2px);
        box-shadow: 0px 0px 0px #000;
    }

/* ========== MOBILE & SMALL SCREEN SCALING ========== */
@media (max-width: 900px), (max-height: 500px) {
    /* Panel scaling lives in --ui-scale now; only the virtual controls, which
       are sized for thumbs rather than for the layout, are stepped here. */
    #joystick-base {
        width: 100px;
        height: 100px;
        left: 20px;
        bottom: 20px;
    }

    #joystick-stick {
        width: 40px;
        height: 40px;
        top: 30px;
        left: 30px;
    }

    #jump-btn {
        width: 70px;
        height: 70px;
        font-size: 18px;
        right: 20px;
        bottom: 20px;
    }
}

/* ========== TUTORIAL SCREEN ========== */
#tutorial-screen {
    position: absolute;
    inset: 0;
    z-index: 60;
    display: none;
    justify-content: center;
    align-items: center;
    background: rgba(0, 0, 0, 0.85); /* Darken the background */
    backdrop-filter: blur(4px);
}

    #tutorial-screen.visible {
        display: flex;
    }

.tutorial-card {
    max-width: 450px;
    text-align: left;
    box-sizing: border-box;
    zoom: max(0.85, var(--card-scale));
    max-height: calc(94vh / max(0.85, var(--card-scale)));
    overflow-y: auto;
}

.tut-role {
    font-size: 24px;
    font-weight: 900;
    margin-bottom: 12px;
    text-shadow: 2px 2px 0px #000;
}

    .tut-role.hider {
        color: #feca57;
    }

    .tut-role.seeker {
        color: #ff6b81;
    }

.tut-text {
    font-size: 15px;
    line-height: 1.6;
    color: #fff;
    font-weight: bold;
    margin-bottom: 15px;
}

    .tut-text b {
        color: #00d2d3;
    }

.tut-buttons {
    display: flex;
    justify-content: flex-end;
    margin-top: 25px;
}

.tut-btn {
    background: #00d2d3;
    border: 3px solid #000;
    padding: 10px 24px;
    font-size: 18px;
    font-weight: 900;
    color: #000;
    border-radius: 8px;
    cursor: pointer;
    box-shadow: 4px 4px 0px #000;
    transition: all 0.1s;
}

    .tut-btn:active {
        transform: translate(4px, 4px);
        box-shadow: 0px 0px 0px #000;
    }


/* ========== INTERACTIVE TUTORIAL ========== */
#tutorial-banner {
    display: none;
    position: absolute;
    top: 15%;
    left: 50%;
    transform: translateX(-50%) scale(var(--ui-scale));
    transform-origin: top center;
    background: rgba(32, 92, 107, 0.95);
    padding: 20px 30px;
    border: 4px solid #000;
    border-radius: 12px;
    box-shadow: 6px 6px 0px #000;
    z-index: 100;
    text-align: center;
    pointer-events: auto;
    width: 80%;
    /* border-box so --tut-max-w caps the visible width, padding included */
    box-sizing: border-box;
    max-width: min(500px, var(--tut-max-w, 500px));
}

    #tutorial-banner.visible {
        display: block;
    }

#tut-banner-text {
    font-size: 22px;
    font-weight: 900;
    color: #feca57;
    text-shadow: 2px 2px 0px #000;
    margin-bottom: 15px;
}

    #tut-banner-text span {
        font-size: 15px;
        color: #fff;
        font-weight: bold;
        text-shadow: 1px 1px 0px #000;
        display: block;
        margin-top: 6px;
    }

#tut-skip-btn {
    background: #ff4757;
    color: white;
    border: 3px solid #000;
    padding: 6px 12px;
    font-weight: bold;
    border-radius: 6px;
    cursor: pointer;
    box-shadow: 3px 3px 0px #000;
    transition: transform 0.1s;
}

/* the way out of the whole thing, deliberately quieter than 'Skip this step':
   a stuck player needs the step button, and only a player who does not want
   the tutorial at all needs this one */
#tut-skip-all-btn {
    background: transparent;
    color: #c8d6e5;
    border: 2px solid #55617a;
    padding: 5px 10px;
    font-weight: bold;
    border-radius: 6px;
    cursor: pointer;
    margin-left: 8px;
}
    #tut-skip-all-btn:active { transform: translateY(1px); }

    #tut-skip-btn:active {
        transform: translate(3px, 3px);
        box-shadow: 0px 0px 0px #000;
    }
/* Custom color row visibility is controlled by JS (the "Custom Color Picker"
   perk) via refreshPerkTools() — no static override here. */

/* Slim down and move the tutorial banner up */
#tutorial-banner {
    display: none;
    position: absolute;
    top: 10px; /* Moved from 15% to snug against the top */
    left: 50%;
    transform: translateX(-50%) scale(var(--ui-scale));
    transform-origin: top center;
    background: rgba(32, 92, 107, 0.95);
    padding: 10px 20px; /* Slimmer padding */
    border: 4px solid #000;
    border-radius: 12px;
    box-shadow: 6px 6px 0px #000;
    z-index: 100;
    text-align: center;
    pointer-events: auto;
    width: 80%;
    /* border-box so --tut-max-w caps the visible width, padding included */
    box-sizing: border-box;
    max-width: min(500px, var(--tut-max-w, 500px));
}

#tut-banner-text {
    margin-bottom: 10px;
    font-size: 18px;
}

/* CrazyGames login button — top-right, never a blocking CTA */
#cg-login-btn {
    position: fixed;
    top: 12px;
    right: 12px;
    z-index: 60;
    padding: 8px 14px;
    background: rgba(30, 39, 46, 0.85);
    color: #feca57;
    border: 2px solid #feca57;
    border-radius: 10px;
    font-family: inherit;
    font-size: 13px;
    cursor: pointer;
    backdrop-filter: blur(4px);
    transform: scale(var(--ctl-scale));
    transform-origin: top right;
}
#cg-login-btn:hover { background: #feca57; color: #1e272e; }

/* ============ COINS HUD ============ */
#coin-hud {
    position: fixed;
    top: 12px;
    left: 50%;
    transform: translateX(-50%) scale(var(--ctl-scale));
    transform-origin: top center;
    z-index: 55;
    display: none;
    padding: 6px 16px;
    background: rgba(30, 39, 46, 0.85);
    color: #feca57;
    border: 2px solid #feca57;
    border-radius: 20px;
    font-size: 18px;
    font-weight: bold;
    backdrop-filter: blur(4px);
}

/* ============ CONNECTION + BATTERY ============
   Top right, inboard of Settings, and small on purpose: it is a glance, not
   a readout. Each half hides itself when it has nothing true to say -- an
   offline round has no round trip to report and a desktop browser usually
   has no battery to report, and a dash where a number should be is worse
   than no pill at all. */
/* 📶🔋 A ROW OF THE STATS PANEL, not something floating over the game.

   It floated three times — top right, under the cart, top centre — and each
   spot was free only in the states I had looked at: the shop cart drew
   through the middle of it, then the match banner grew to two lines and took
   the centre, and at z-index 55 against the overlay's 30 it sat on top of
   the result card as well.

   In flow inside #ui-container it cannot overlap anything, and every card
   and sheet covers it exactly as they cover the rest of the stats, because
   it now shares their z-index instead of outranking them. */
#link-hud {
    display: none;
    gap: 8px;
    align-items: center;
    margin: 0 0 7px;
    font-size: 11px;
    font-weight: 900;
    color: #9fb0c4;
    letter-spacing: 0.3px;
    white-space: nowrap;
}
#link-hud.shown { display: flex; }
#link-hud span:empty { display: none; }
/* the colour IS the message — a number nobody can read at a glance still has
   to say good, worrying or bad */
#link-net.good { color: #2ed573; }
#link-net.fair { color: #feca57; }
#link-net.poor { color: #ff6b6b; }
#link-bat.low  { color: #ff6b6b; }
/* an ad owns the screen while it plays — see the note on .mm-actions */
body.ad-playing #link-hud { display: none !important; }

/* ============ PAINT / ZOOM-OUT BUTTONS (touch hiders) ============
   They share one spot and never show together: PAINT while zoomed out,
   the magnifier-minus while zoomed in. */
#paint-btn,
#zoom-out-btn {
    position: fixed;
    /* Bottom centre, in the gap between the joystick and JUMP. This used to be
       left:12px/top:45% — precisely the zoom panel's lane — so on every phone
       size the two sat on top of each other, and on short frames it fouled the
       joystick as well. */
    left: 50%;
    bottom: 18px;
    z-index: 55;
    display: none;
    width: 54px;
    height: 54px;
    align-items: center;
    justify-content: center;
    background: rgba(30, 39, 46, 0.85);
    border: 3px solid #feca57;
    border-radius: 50%;
    cursor: pointer;
    padding: 0;
    transform: translateX(-50%) scale(var(--ctl-scale));
    transform-origin: center bottom;
}

/* ============ MAGNIFIER BUTTON (seeker) ============ */
#magnifier-btn {
    position: fixed;
    bottom: 20px;
    left: 20px;
    z-index: 55;
    display: none;
    width: 60px;
    height: 60px;
    line-height: 60px;
    text-align: center;
    font-size: 28px;
    background: rgba(30, 39, 46, 0.8);
    border: 3px solid #54a0ff;
    border-radius: 50%;
    cursor: pointer;
    user-select: none;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom left;
}
#magnifier-btn.active { background: #54a0ff; }

/* ============ LOCK BUTTON (hider) ============
   The hider's counterpart to PEEK and deliberately the same object: same
   size, same place on a desktop, same word-under-glyph. They are never on
   screen together -- one is a seeker control and one is a hider control. */
#look-btn {
    position: fixed;
    bottom: 20px;
    left: 20px;
    z-index: 55;
    display: none;
    width: 60px;
    height: 60px;
    align-items: center;
    justify-content: center;
    text-align: center;
    background: rgba(30, 39, 46, 0.8);
    border: 3px solid #2ed573;
    border-radius: 50%;
    cursor: pointer;
    user-select: none;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom left;
}
#look-btn.shown { display: flex; }
#look-btn.active { background: #2ed573; }
#look-btn svg { vertical-align: middle; }

/* ============ FILL TOOL (hider) ============ */
.fill-row { margin-top: 6px; display: none; }
/* 🔒 a paid tool the player does not own yet: still there, still tappable,
   visibly not theirs. */
.fill-btn.locked, #cc-eyedrop.locked { filter: grayscale(1); opacity: 0.55; }
.fill-btn.locked::after { content: ' 🔒'; font-size: 12px; }
.fill-btn {
    width: 100%;
    padding: 8px;
    background: linear-gradient(90deg, #ff9f43, #feca57);
    color: #1e272e;
    border: none;
    border-radius: 8px;
    font-family: inherit;
    font-weight: bold;
    font-size: 14px;
    cursor: pointer;
}
.custom-color-locked {
    display: none;
    font-size: 12px;
    opacity: 0.6;
    padding: 4px 0;
}

/* ============ MAIN MENU ============ */
#main-menu {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: none;
    pointer-events: none;
    padding: 30px;
    box-sizing: border-box;
}
#main-menu.visible { display: block; }
.mm-top {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
}
.mm-title {
    margin: 0;
    font-size: clamp(28px, min(6vw, 9vh), 64px);
    line-height: 1.05;
    color: #fff;
    text-shadow: 0 4px 0 rgba(0,0,0,0.35), 0 0 30px rgba(84,160,255,0.4);
}
.mm-coins {
    pointer-events: auto;
    padding: 8px 18px;
    /* Clear of the FIXED BUTTON STRIP in the corner — Settings, and since the
       inbox landed to its left, that one too. It has to scale with the
       buttons: they shrink toward the top-right corner with --ctl-scale, so a
       fixed 40px (which cleared Settings alone) left the coin pill sitting
       under the envelope on a portrait phone. */
    /* = the inbox button's offset (64px, NOT scaled — it is a `right:` value)
       + its scaled width + a gap. Scaling the whole figure is the wrong sum
       and still left the pill under the envelope in landscape. */
    margin-right: calc(72px + 42px * var(--ctl-scale));
    background: rgba(0,0,0,0.4);
    border: 2px solid #feca57;
    border-radius: 20px;
    color: #feca57;
    font-size: 22px;
    font-weight: bold;
    transform: scale(var(--ctl-scale));
    transform-origin: top right;
}
.mm-actions {
    position: absolute;
    right: 30px;
    bottom: 30px;
    display: flex;
    flex-direction: column;
    /* stretch, not flex-end: every button then takes the column's width, so
       the stack reads as one block instead of a ragged edge */
    align-items: stretch;
    gap: 14px;
    /* --mm-fit is the last resort when even hiding the quest pills leaves the
       column taller than the space above PLAY: fitMenuPills measures what is
       left and shrinks by exactly that much. Bottom-right origin means
       shrinking only ever moves the top DOWN, so it cannot oscillate. */
    transform: scale(calc(var(--ui-scale) * var(--mm-fit, 1)));
    transform-origin: bottom right;
}
.mm-shop {
    pointer-events: auto;
    width: 100%;
    box-sizing: border-box;
    text-align: center;
    padding: 12px 26px;
    font-family: inherit;
    font-size: 18px;
    font-weight: bold;
    color: #fff;
    background: rgba(45, 52, 70, 0.9);
    border: 2px solid #576574;
    border-radius: 14px;
    cursor: pointer;
}
.mm-shop:hover { border-color: #c8d6e5; }
/* 👍/👎 strip for a just-played community map */
#map-like { display: flex; align-items: center; justify-content: center; gap: 10px; margin: 4px 0 8px; }
#map-like span { font-size: 13px; color: #dfe6ee; }
#map-like button { width: 40px; height: 34px; border-radius: 9px; border: 2px solid rgba(255,255,255,0.25);
    background: rgba(20,26,34,0.85); color: #aab8c8; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; }
#map-like button:hover { border-color: #c8d6e5; color: #fff; }
#ml-up.picked { background: #2ed573; color: #10141c; border-color: #2ed573; }
#ml-down.picked { background: #ff6b81; color: #10141c; border-color: #ff6b81; }

/* 🧟 the mode selector: a menu button with an accent, and a sheet of four.
   Reminder: anything interactive inside #main-menu needs pointer-events:auto
   (mm-shop already carries it). */
.mode-accent { border-color: #a55eea !important; color: #d3a8ff; }
/* 🎲 a rolled zombie round wears the horde's green, so the rotation reads as
   an offer rather than a setting the player forgot to change */
.mode-rolled { border-color: #2ed573 !important; color: #7bed9f; }
#mode-screen { position: fixed; inset: 0; z-index: 96; display: none; align-items: center; justify-content: center;
    background: rgba(0,0,0,0.7); backdrop-filter: blur(3px); }
#mode-screen.visible { display: flex; }
#mode-screen .mode-card { width: min(760px, 94vw); }
#mode-close { width: 38px; height: 38px; font-size: 18px; color: #fff; background: #576574; border: none; border-radius: 10px; cursor: pointer; }
#mode-list {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(226px, 1fr)); gap: 8px;
    /* .ach-card is a flex column with a max-height: without these the list
       simply overflowed it and the last modes were cut off the bottom */
    min-height: 0; overflow-y: auto; touch-action: pan-y;
}
.mode-row { text-align: left; padding: 11px 14px; border-radius: 12px; background: #1e2533; border: 2px solid #3a4560;
    color: #dfe6e9; font-family: inherit; cursor: pointer; }
.mode-row b { font-size: 16px; }
.mode-row span { display: block; font-size: 12px; color: #8395a7; margin-top: 3px; }
.mode-row:hover { border-color: #c8d6e5; }
.mode-row.active { border-color: #a55eea; }
.mode-row.active b { color: #d3a8ff; }
/* 🎭 the role row at the bottom of the mode sheet */
.mode-role { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.mr-label { font-size: 13px; color: #8395a7; font-weight: bold; letter-spacing: 0.5px; text-transform: uppercase; }
#role-pref { flex: 1; display: flex; border-radius: 10px; overflow: hidden; border: 2px solid rgba(255,255,255,0.25); }
.rp-btn { flex: 1; padding: 8px 4px; border: 0; background: #1e2533; color: #aab8c8;
    font-family: inherit; font-size: 13px; font-weight: 700; cursor: pointer; letter-spacing: 0.4px; }
.rp-btn + .rp-btn { border-left: 1px solid rgba(255,255,255,0.15); }
.rp-btn.active { background: #54a0ff; color: #10141c; }

/* 🗓️ today's quests, as small pills above the menu buttons; tap opens the panel */
#quest-pills { pointer-events: auto; display: flex; flex-direction: column; gap: 6px; }
.q-pill { display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 5px 13px; border-radius: 999px; background: rgba(20,26,34,0.85);
    border: 2px solid rgba(255,255,255,0.2); color: #c9d6e2; font-size: 12px;
    font-family: inherit; font-weight: 600; cursor: pointer; text-align: left; }
.q-pill:hover { border-color: #c8d6e5; }
.q-pill b { color: #feca57; font-variant-numeric: tabular-nums; white-space: nowrap; }
.q-pill.done { border-color: #2ed573; }
.q-pill.done, .q-pill.done b { color: #2ed573; }


.mm-play {
    pointer-events: auto;
    /* PLAY sets the width of the whole stack; the others stretch to match */
    width: 100%;
    box-sizing: border-box;
    padding: 22px 70px;
    font-family: inherit;
    font-size: 34px;
    font-weight: bold;
    color: #fff;
    background: linear-gradient(180deg, #2ed573, #1da355);
    border: none;
    border-radius: 20px;
    box-shadow: 0 8px 0 #158a44, 0 12px 24px rgba(0,0,0,0.4);
    cursor: pointer;
    transition: transform 0.08s, box-shadow 0.08s;
}
.mm-play:active {
    transform: translateY(6px);
    box-shadow: 0 2px 0 #158a44, 0 4px 12px rgba(0,0,0,0.4);
}

/* ============ RANK LADDER (main menu, left column) ============ */
/* 🧱 the whole left stack scales and is positioned as one; the ladder inside
   it is what gives up height when the frame is short. */
#mm-left {
    position: absolute;
    left: 30px;
    top: clamp(118px, 24vh, 170px);
    display: none;
    flex-direction: column;
    gap: 8px;
    align-items: stretch;
    transform: scale(max(0.85, var(--ui-scale)));
    transform-origin: top left;
    box-sizing: border-box;
    /* divided by the scale because max-height is measured before the transform */
    max-height: calc((100vh - clamp(118px, 24vh, 170px) - 14px) / max(0.85, var(--ui-scale)));
    pointer-events: auto;
}
#main-menu.visible #mm-left { display: flex; }
.mm-side {
    flex: none;
    background: rgba(30, 39, 46, 0.97);
    border: 2px solid #576574;
    border-radius: 12px;
    color: #dfe6e9;
    font-family: inherit;
    font-size: 13px;
    font-weight: bold;
    padding: 9px 12px;
    cursor: pointer;
    white-space: nowrap;
    backdrop-filter: blur(4px);
}
.mm-side:hover { border-color: #54a0ff; color: #fff; }
/* 🎖️🪪 the level and the player card on one line. The level is the number
   people look at; the card is a door, so it shrinks to its icon. */
.mm-line { display: flex; align-items: stretch; gap: 6px; pointer-events: auto; }
.mm-line #level-badge { flex: 1 1 auto; min-width: 0; margin-bottom: 0; }
.mm-side.mm-icon { flex: 0 0 auto; width: 46px; padding: 0; font-size: 20px;
    display: flex; align-items: center; justify-content: center; }
/* the reward text and the headcount both want room; on a phone the row
   wraps and the count drops onto its own line rather than squeezing the
   reward into four wrapped words */
#rank-screen .rl-row { flex-wrap: wrap; row-gap: 2px; padding: 5px 7px; }
#rank-screen .rl-gift { flex: 1 1 140px; min-width: 0; }
#rank-screen .rl-season { padding-left: 4px; overflow: visible; }
#rank-screen #rank-ladder { padding-left: 2px; }
@media (max-width: 560px) {
    #rank-screen .rl-name { min-width: 0; flex: 1 1 auto; }
    #rank-screen .rl-pts { flex: 0 0 auto; }
    #rank-screen .rl-gift { flex: 1 1 100%; padding-left: 34px; }
    #rank-screen .rl-who { flex: 1 1 100%; margin-left: 34px; text-align: left; }
}
#rank-screen .sheet-card {
    background: #12161f; border: 2px solid #2d3646; border-radius: 16px;
    width: min(560px, 94vw); padding: 12px;
    max-height: 92vh; overflow-y: auto; box-sizing: border-box; }
#rank-screen .sheet-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
#rank-screen .sheet-title { font-size: 18px; font-weight: bold; color: #dfe6e9; flex: 1; }
#rank-screen #rank-ladder { background: none; border: 0; padding: 0; backdrop-filter: none; }
/* 🪜 the ladder lives in its own full-screen sheet now. It had been eleven
   rows competing with the level, the quests and every menu button for a
   column that is shorter than they are, and it had shrunk to unreadable. */
#rank-screen { position: fixed; inset: 0; z-index: 96; display: none;
    align-items: center; justify-content: center; background: rgba(6,9,14,.78); }
#rank-screen.visible { display: flex; }
.rank-card { width: min(520px, 94vw); max-height: 92vh; overflow-y: auto; }
.rank-btn { display: flex; align-items: center; gap: 9px; text-align: left; }
.rank-btn .rkb-badge { width: 26px; height: 26px; flex: 0 0 auto; }
.rank-btn .rkb-text { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.rank-btn .rkb-text b { font-size: 14px; }
.rank-btn .rkb-text i { font-style: normal; font-size: 11px; color: #8395a7; font-variant-numeric: tabular-nums; }
.rl-who { font-size: 11px; color: #576574; margin-left: auto; white-space: nowrap; font-variant-numeric: tabular-nums; }
.rl-row.cur .rl-who { color: #feca57; }

#rank-ladder {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: 10px 12px;
    background: rgba(30, 39, 46, 0.97);
    border: 2px solid #576574;
    border-radius: 14px;
    backdrop-filter: blur(4px);
    box-sizing: border-box;
    overflow-y: auto;
    scrollbar-width: thin;
    /* #main-menu is pointer-events:none and its children opt back in — the
       ladder never did, so it LOOKED scrollable but no touch, drag or wheel
       ever reached it: the hit test landed on the canvas behind. */
    pointer-events: auto;
}
.rl-title {
    font-size: 12px;
    font-weight: 900;
    text-transform: uppercase;
    letter-spacing: 1.5px;
    color: #c8d6e5;
    margin-bottom: 4px;
}
.rl-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 2px 6px;
    border-radius: 8px;
    border: 2px solid transparent;
}
.rl-row canvas { width: 26px; height: 26px; flex: 0 0 auto; }
.rl-name { font-size: 15px; font-weight: bold; color: #dfe6e9; min-width: 104px; }
.rl-pts { font-size: 12px; color: #8395a7; font-variant-numeric: tabular-nums; }
.rl-row.locked .rl-name { color: #8395a7; }
.rl-row.locked canvas { opacity: 0.45; filter: grayscale(0.4); }
.rl-row.done .rl-pts::after { content: ' ✓'; color: #2ed573; }
.rl-row.cur {
    border-color: #feca57;
    background: rgba(254, 202, 87, 0.14);
}
.rl-row.cur .rl-name { color: #feca57; }
.rl-row.cur .rl-pts { color: #dfe6e9; }
/* 🗓️ the week's clock and last week's result, under the ladder title */
.rl-season { font-size: 11px; color: #8395a7; margin: -2px 0 6px; line-height: 1.35; }
.rl-season b { color: #feca57; font-weight: 800; }
.rl-gift { font-size: 11px; color: #feca57; margin-left: 4px; }
.rl-dbl { margin-left: 6px; padding: 1px 7px; border-radius: 7px; border: 1px solid #2ed573; background: rgba(46,213,115,.12); color: #2ed573; font: 800 10px/1.4 inherit; cursor: pointer; }
.rl-dbl:hover { background: rgba(46,213,115,.25); }
.mode-tag { font-style: normal; font-size: 10px; letter-spacing: .8px; text-transform: uppercase; color: #feca57; margin-left: 6px; }
#season-screen { position: fixed; inset: 0; z-index: 96; display: none; align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#season-screen.visible { display: flex; }
#season-body { text-align: center; padding: 6px 0 10px; }
#season-body .ss-class { font-size: 28px; font-weight: 900; color: #feca57; margin: 4px 0; }
#season-body .ss-pts { color: #c8d6e5; font-size: 13px; }
#season-body .ss-reward { margin-top: 12px; padding: 10px; border-radius: 12px; background: rgba(254,202,87,.12); border: 2px solid #feca57; color: #fff; font-weight: 800; }
#season-body canvas { width: 72px; height: 72px; display: block; margin: 0 auto 4px; }
#season-list .cp-row.me { border-color: #feca57; }
.cp-pts { font-weight: 900; color: #feca57; }

/* ============ ACHIEVEMENTS ============ */
#ach-screen {
    position: fixed;
    inset: 0;
    z-index: 95;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.7);
    backdrop-filter: blur(3px);
}
#ach-screen.visible { display: flex; }
/* 🗓️ daily quests — same modal skeleton and row look as achievements */
#quest-screen { position: fixed; inset: 0; z-index: 95; display: none; align-items: center; justify-content: center;
    background: rgba(0,0,0,0.7); backdrop-filter: blur(3px); }
#quest-screen.visible { display: flex; }
#quest-timer { color: #feca57; font-variant-numeric: tabular-nums; }
/* one modal, two tabs: Quests | Achievements (the merged menu button) */
.qa-tabs { display: flex; gap: 6px; }
.qa-tabs button { padding: 8px 14px; border-radius: 10px; border: 2px solid transparent; background: none;
    color: #8395a7; font-family: inherit; font-size: 17px; font-weight: bold; cursor: pointer; }
.qa-tabs button.active { color: #fff; background: #1e2533; border-color: #3a4560; cursor: default; }
.qa-tabs button:not(.active):hover { color: #c8d6e5; }

.quest-sub { font-size: 12px; color: #8395a7; margin: 2px 0 10px; }
#quest-list { overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding-right: 4px; scrollbar-width: thin; }
#quest-close { width: 38px; height: 38px; font-size: 18px; color: #fff; background: #576574; border: none; border-radius: 10px; cursor: pointer; }

.ach-card {
    width: min(460px, 92vw);
    background: #2d3446;
    border: 2px solid #576574;
    border-radius: 18px;
    padding: 18px 22px 22px 22px;
    box-sizing: border-box;
    zoom: max(0.85, var(--card-scale));
    max-height: calc(88vh / max(0.85, var(--card-scale)));
    display: flex;
    flex-direction: column;
}
#ach-close {
    width: 38px; height: 38px;
    font-size: 18px;
    color: #fff;
    background: #576574;
    border: none;
    border-radius: 10px;
    cursor: pointer;
}
#ach-list { overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding-right: 4px; scrollbar-width: thin; }
.ach-row {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #1e2533;
    border: 2px solid #3a4560;
    border-radius: 12px;
    padding: 8px 12px;
}
.ach-row.done { border-color: #2ed573; }
/* 📒 the album */
#album-list { overflow-y: auto; display: flex; flex-direction: column; gap: 8px; padding-right: 4px; scrollbar-width: thin; min-height: 0; }
.al-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.al-count { font-size: 15px; color: #dfe6e9; } .al-count b { color: #feca57; font-size: 20px; }
.al-note { font-size: 11.5px; color: #8b97ab; }
.al-pages { display: flex; gap: 5px; flex-wrap: wrap; }
.al-page { padding: 5px 9px; border-radius: 9px; border: 2px solid #3a4560; background: #1e2533; color: #8b97ab; font-family: inherit; font-size: 12px; font-weight: 700; cursor: pointer; }
.al-page i { font-style: normal; color: #5b6b82; font-weight: 600; margin-left: 3px; }
.al-page.on { color: #fff; border-color: #feca57; }
.al-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 8px; }
.al-cell { background: #1e2533; border: 2px solid #3a4560; border-radius: 12px; padding: 6px 4px 5px; text-align: center; font-family: inherit; color: #dfe6e9; cursor: default; }
.al-cell canvas { width: 64px; height: 64px; display: block; margin: 0 auto; }
/* art still in flight: a shimmering disc, not the drawn placeholder — that one
   is for stickers whose PNG does not exist yet, and reads as broken here */
.al-cell.loading { position: relative; }
.al-cell.loading canvas { opacity: 0; }
.al-cell.loading::before {
    content: ''; position: absolute; left: 50%; top: 6px; transform: translateX(-50%);
    width: 64px; height: 64px; border-radius: 50%;
    background: linear-gradient(100deg, #2b3543 34%, #3f4c60 50%, #2b3543 66%);
    background-size: 260% 100%;
    animation: al-shimmer 1.2s linear infinite;
}
@keyframes al-shimmer { from { background-position: 130% 0; } to { background-position: -130% 0; } }
@media (prefers-reduced-motion: reduce) { .al-cell.loading::before { animation: none; } }
.al-cell.have { cursor: pointer; }
.al-cell.have:hover { border-color: #54a0ff; }
.al-cell.placed { border-color: #feca57; background: rgba(254,202,87,.1); }
.al-cell.locked { opacity: .75; }
.al-name { font-size: 11.5px; font-weight: 800; margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.al-hint { font-size: 10px; color: #8b97ab; line-height: 1.2; margin-top: 2px; min-height: 2.4em; }
.ach-info { flex: 1; min-width: 0; }
.ach-name { font-size: 15px; font-weight: bold; color: #dfe6e9; }
.ach-row.done .ach-name { color: #2ed573; }
.ach-desc { font-size: 12px; color: #8395a7; }
.ach-bar { height: 6px; background: #12161f; border-radius: 3px; margin-top: 5px; overflow: hidden; }
.ach-fill { height: 100%; background: #54a0ff; border-radius: 3px; }
.ach-row.done .ach-fill { background: #2ed573; }
.ach-right { flex: 0 0 auto; text-align: right; font-size: 12px; color: #feca57; font-weight: bold; white-space: nowrap; }
.ach-prog { display: block; color: #8395a7; font-weight: normal; font-variant-numeric: tabular-nums; }
/* how many players hold this one. Sits on the description line so 28 rows do
   not each grow taller; it wraps to its own line on a narrow phone. */
.ach-rare { color: #6b7c8c; font-weight: 600; font-variant-numeric: tabular-nums; }
.ach-rare.rare { color: #feca57; }
.si-buy.si-locked { background: #2d3446; color: #8395a7; border: 2px solid #576574; font-size: 11px; }

/* ============ SHOP ============ */
#shop-screen {
    position: fixed;
    inset: 0;
    z-index: 90;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.7);
    backdrop-filter: blur(3px);
}
#shop-screen.visible { display: flex; }
/* the ✕ is pinned to this corner — see #shop-close */
.shop-card {
    position: relative;
    width: min(680px, 92vw);
    zoom: max(0.85, var(--card-scale));
    max-height: calc(88vh / max(0.85, var(--card-scale)));
    display: flex;
    flex-direction: column;
    background: #2d3446;
    border: 2px solid #576574;
    border-radius: 18px;
    padding: 18px;
    box-sizing: border-box;
}
.shop-head {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 12px;
    /* room for the ✕, which is now pinned to the card corner */
    padding-right: 48px;
}
.shop-head h2 { margin: 0; flex: 0 0 auto; font-size: 26px; }
.shop-coins {
    margin-left: auto;
    color: #feca57;
    font-size: 20px;
    font-weight: bold;
}
/* 🩹 OUT OF THE FLEX ROW. This used to be pinned right only by
   .shop-coins having margin-left:auto, which needs free space in the row
   to work. Open the shop during a round — where the rewarded-ad button is
   also showing — and the row fills, the auto margin collapses to zero,
   and the ✕ bunches leftward into the middle of the header. A close
   button belongs in the corner whatever else shares its row. */
#shop-close {
    position: absolute;
    top: 18px; right: 18px;
    z-index: 1;
    width: 38px; height: 38px;
    font-size: 18px;
    color: #fff;
    background: #576574;
    border: none;
    border-radius: 10px;
    cursor: pointer;
}
.shop-tabs { display: flex; gap: 8px; margin-bottom: 14px; flex-wrap: wrap; }
.shop-tab {
    flex: 1;
    min-width: 100px;
    padding: 10px;
    font-family: inherit;
    font-size: 14px;
    font-weight: bold;
    color: #c8d6e5;
    background: #1e2533;
    border: 2px solid transparent;
    border-radius: 10px;
    cursor: pointer;
}
.shop-tab.active { color: #fff; border-color: #54a0ff; background: #34405a; }
/* 🧭 the body-part sub-menu under the Customize tab. A second row of tabs
   would compete with the first, so these are chips: smaller, rounder, and
   scrolling sideways rather than wrapping into a second line that eats the
   little height a phone has. */
.shop-sub {
    display: flex;
    /* flex:none, or the card's column squeezes this row to nothing the moment
       the main tabs wrap to two lines on a phone — the chips came out sliced
       in half by the item grid. */
    flex: none;
    gap: 4px;
    /* 🏷️ ONE LINE. Two wrapped rows of chips ate a third of the shop card on a
       phone, above the items they are there to filter.
       The wrap was added for a real reason — nine chips on one line pushed Name
       FX and Auras off the right edge, "invisible unless you thought to drag a
       row that does not look draggable". The chips are slim enough now that
       most fit, and the mask below puts a fade on whichever edge still has
       more past it, so the row is visibly cut off rather than secretly. */
    flex-wrap: nowrap;
    margin: 0 0 8px;
    padding-bottom: 2px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* the scrollbar is noise on a row this short; the fade is the affordance */
    -ms-overflow-style: none;
    /* fades only where there is something to scroll to — at either end the
       gradient collapses, so a row that fits shows no fade at all */
    mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 14px, #000 calc(100% - 14px), transparent 100%);
}
.shop-sub::-webkit-scrollbar { height: 0; }
.shop-sub-tab {
    flex: none;
    /* slimmer on every axis: this row is a filter, not the content. Was
       7px 13px / 13px text / 2px border — roughly half the height now. */
    padding: 4px 9px;
    font-family: inherit;
    font-size: 12px;
    font-weight: bold;
    line-height: 1.25;
    white-space: nowrap;
    color: #8fa3bd;
    background: #161c27;
    border: 1px solid #2b3547;
    border-radius: 999px;
    cursor: pointer;
}
.shop-sub-tab:hover { color: #c8d6e5; border-color: #3f4d66; }
.shop-sub-tab.active { color: #fff; background: #34405a; border-color: #54a0ff; }
.shop-sub-tab:focus-visible { outline: 2px solid #4a9de0; outline-offset: 2px; }
#shop-items {
    overflow-y: auto;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 12px;
    padding: 4px;
}
.shop-item {
    background: #1e2533;
    border: 2px solid #3a4560;
    border-radius: 14px;
    padding: 14px;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.shop-item .si-icon { font-size: 40px; }
.shop-item .si-name { font-weight: bold; font-size: 15px; }
.shop-item .si-desc { font-size: 11px; opacity: 0.7; min-height: 30px; }
.shop-item button {
    margin-top: auto;
    padding: 9px;
    font-family: inherit;
    font-weight: bold;
    font-size: 14px;
    border: none;
    border-radius: 8px;
    cursor: pointer;
}
.si-buy { background: #feca57; color: #1e272e; }
.si-buy:disabled { background: #576574; color: #95a5a6; cursor: not-allowed; }
.si-equip { background: #54a0ff; color: #fff; }
.si-equipped { background: #2ed573; color: #fff; }

#overlay-shop-btn {
    margin-top: 12px;
    padding: 12px 28px;
    font-family: inherit;
    font-size: 16px;
    font-weight: bold;
    color: #1e272e;
    background: #feca57;
    border: none;
    border-radius: 12px;
    cursor: pointer;
}
#overlay-shop-btn:hover { background: #ffd97a; }

#overlay-menu-btn {
    margin: 12px 0 0 10px;
    padding: 12px 24px;
    font-family: inherit;
    font-size: 16px;
    font-weight: bold;
    color: #c8d6e5;
    background: #2d3446;
    border: 2px solid #576574;
    border-radius: 12px;
    cursor: pointer;
}
#overlay-menu-btn:hover { border-color: #c8d6e5; color: #fff; }
#overlay-menu-btn:disabled, #home-btn:disabled { opacity: 0.35; cursor: default; }
#overlay-menu-btn:disabled:hover { border-color: #576574; color: #c8d6e5; }

/* ============ AUDIO SETTINGS POPUP ============ */
#audio-screen {
    position: fixed;
    inset: 0;
    z-index: 95;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(3px);
}
#audio-screen.visible { display: flex; }
.audio-card {
    width: min(360px, 92vw);
    zoom: max(0.85, var(--card-scale));
    max-height: calc(94vh / max(0.85, var(--card-scale)));
    overflow-y: auto;
    background: #2d3446;
    border: 2px solid #576574;
    border-radius: 18px;
    padding: 18px 22px 22px 22px;
    box-sizing: border-box;
}
.audio-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 6px;
}
.audio-head h2 { margin: 0; }
#audio-close {
    width: 38px; height: 38px;
    font-size: 18px;
    color: #fff;
    background: #576574;
    border: none;
    border-radius: 10px;
    cursor: pointer;
}
.audio-track {
    text-align: center;
    color: #feca57;
    font-weight: bold;
    font-size: 16px;
    margin: 8px 0 14px 0;
    min-height: 20px;
}
.audio-controls {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 16px;
}
.audio-btn {
    padding: 9px 14px;
    font-family: inherit;
    font-weight: bold;
    font-size: 14px;
    color: #c8d6e5;
    background: #1e2533;
    border: 2px solid #3a4560;
    border-radius: 10px;
    cursor: pointer;
}
.audio-btn:hover { border-color: #54a0ff; color: #fff; }
.audio-btn.active { background: #54a0ff; border-color: #54a0ff; color: #fff; }
.audio-btn.off { background: #ff4757; border-color: #ff4757; color: #fff; }
.audio-play { min-width: 76px; }
.audio-label {
    display: block;
    font-size: 13px;
    font-weight: bold;
    color: #c8d6e5;
    margin: 12px 2px 6px 2px;
}
#audio-screen input[type="range"] {
    width: 100%;
    cursor: pointer;
    accent-color: #54a0ff;
}
.audio-mute-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid #3a4560;
    font-weight: bold;
    font-size: 15px;
}

/* ============ BOTTOM BANNER AD (320x50, stuck to the frame bottom) ============ */
#banner-holder {
    position: fixed;
    left: 50%;
    bottom: 0;
    transform: translateX(-50%);
    width: 320px;
    height: 50px;
    z-index: 40;
    display: none;
    align-items: center;
    justify-content: center;
}
#banner-container { width: 320px; height: 50px; }

/* the result card shrinks as a whole (zoom keeps the layout intact) on short
   frames and when the banner strip claims the bottom — see --card-scale. */

/* ============ 🏆 CHAMPIONSHIP ============ */
/* it sits right above PLAY and has to read as connected to it */
.champ-accent { border-color: #feca57 !important; color: #ffe9a8 !important; }
.champ-accent.champ-off { border-color: #576574 !important; color: #8b97ab !important; }
.champ-sub { display: block; font-size: 11px; font-weight: normal; opacity: 0.85; }


#champ-screen {
    position: fixed; inset: 0; z-index: 95; display: none;
    align-items: center; justify-content: center; background: rgba(8, 11, 18, 0.82);
}
#champ-screen.visible { display: flex; }
/* 🏆 ONE scroll region, not two. #champ-list used to be the only
   scrollable thing in this card — then the weekly points table was added
   below it, and everything past the championship top ten sat under the
   bottom edge with no way to reach it. The CARD scrolls now and both lists
   sit inside it at their natural height. */
#champ-screen .ach-card { overflow-y: auto; scrollbar-width: thin; overscroll-behavior: contain; }
#champ-list {
    display: flex; flex-direction: column; gap: 6px;
    flex: none;
    padding-right: 4px;
}
#season-list { flex: none; }
#champ-note { flex: none; }
.cp-row {
    display: flex; align-items: center; gap: 10px;
    background: #232c3d; border: 2px solid #000; border-radius: 9px; padding: 7px 10px;
}
.cp-row.me { border-color: #54a0ff; }
/* 🏆 the break between the top ten and the player's own corner of the table.
   Quiet on purpose: it is punctuation, not a row. */
.cp-gap {
    text-align: center; letter-spacing: 4px; color: #6b7a90;
    font-weight: 900; padding: 2px 0 1px; user-select: none;
}
/* 🤝 a friend on the weekend table. The left bar carries it as well as the
   colour, so it reads without relying on hue alone. */
.cp-row.friend { border-color: #4ecdc4; box-shadow: inset 3px 0 0 #4ecdc4; }
.cp-row.friend .cp-name::after {
    content: '🤝';
    font-size: 11px;
    margin-left: 6px;
    vertical-align: middle;
}
.cp-place { font-weight: 900; font-size: 15px; min-width: 30px; text-align: center; color: #8b97ab; }
.cp-row.p1 .cp-place { color: #ffd32a; }
.cp-row.p2 .cp-place { color: #dfe6e9; }
.cp-row.p3 .cp-place { color: #e58e26; }
.cp-name { flex: 1; font-weight: bold; color: #fff; overflow-wrap: anywhere; }
.cp-wins { font-weight: 900; color: #2ed573; }
/* what each place is worth, on the row itself */
.cp-prize { display: flex; align-items: center; gap: 3px; font-weight: 900; color: #ffd32a; min-width: 62px; justify-content: flex-end; }
.cp-prize .coin-ico { width: 13px; height: 13px; }
.cp-crown { font-size: 12px; }
.cp-prizes { margin-top: 6px; font-size: 12px; color: #c8d6e5; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.cp-prizes .coin-ico { width: 12px; height: 12px; }
.cp-note { font-size: 12px; color: #8b97ab; padding: 6px 2px; }
.cp-live { color: #2ed573; font-weight: 900; }

/* ============ SITELOCK SCREEN ============ */
#lock-screen {
    position: fixed;
    inset: 0;
    z-index: 500;
    display: none;
    align-items: center;
    justify-content: center;
    background: #161b26;
}
#lock-screen.visible { display: flex; }
.lock-card {
    text-align: center;
    zoom: max(0.85, var(--card-scale));
    background: #205c6b;
    border: 4px solid #000;
    border-radius: 16px;
    padding: 34px 44px;
    box-shadow: 8px 8px 0px #000;
    max-width: min(420px, 88vw);
}
.lock-card h1 {
    margin: 0 0 14px 0;
    font-size: 30px;
    color: #feca57;
    text-shadow: 3px 3px 0px #000;
}
.lock-card p {
    font-size: 16px;
    line-height: 1.5;
    margin: 0 0 24px 0;
}
.lock-btn {
    display: inline-block;
    padding: 14px 30px;
    font-size: 18px;
    font-weight: bold;
    color: #fff;
    text-decoration: none;
    background: linear-gradient(180deg, #2ed573, #1da355);
    border-radius: 14px;
    box-shadow: 0 6px 0 #158a44, 0 10px 20px rgba(0,0,0,0.4);
}
.lock-btn:active { transform: translateY(4px); box-shadow: 0 2px 0 #158a44; }

/* ============ HOME BUTTON (in-game, back to menu) ============ */
#home-btn {
    position: fixed;
    top: 12px;
    right: 64px;
    z-index: 60;
    width: 42px;
    height: 42px;
    background: rgba(30, 39, 46, 0.85);
    border: 2px solid #576574;
    border-radius: 50%;
    cursor: pointer;
    padding: 0;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(4px);
    transform: scale(var(--ctl-scale));
    transform-origin: top right;
}
#home-btn:hover { border-color: #c8d6e5; }
/* 🚪 armed: the first press asked, the second one goes. Amber rather than red
   — leaving is allowed, not a mistake, it just should not happen by accident. */
#home-btn.confirm { border-color: #e8a33d; background: rgba(232, 163, 61, 0.22); }
#home-btn.confirm svg path { fill: #ffce7a; }

/* ============ 😀 EMOTE REACTIONS ============ */
#emote-wrap {
    position: fixed;
    right: 14px;
    bottom: 14px;
    z-index: 60;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom right;
}
#emote-toggle {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    background: rgba(30, 39, 46, 0.85);
    border: 2px solid #576574;
    cursor: pointer;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(4px);
    transition: border-color 0.15s, transform 0.15s;
}
#emote-toggle:hover { border-color: #c8d6e5; }
#emote-toggle.open { border-color: #feca57; transform: scale(1.05); }
/* never sit under the banner ad (bottom-center) — hide the button while it shows */
body.banner-on #emote-wrap { display: none !important; }
/* on touch devices the bottom corners belong to the joystick + jump button, so
   move the reaction button to the top-right (below the mute button) and open the
   palette DOWNWARD (column-reverse keeps the toggle pinned in place) */
/* On touch the reactions move under the mute button. --ctl-scale bottoms out
   at 0.75, which shrank a 46px button to 35px — under the 44px floor and easy
   to miss entirely. Hold it at a real tap target. */
body.touch #emote-wrap { top: 58px; right: 10px; bottom: auto; flex-direction: column-reverse;
    transform-origin: top right; transform: scale(max(0.96, var(--ctl-scale))); }
#emote-palette {
    display: none;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    padding: 8px;
    background: rgba(30, 39, 46, 0.92);
    border: 2px solid #576574;
    border-radius: 16px;
    backdrop-filter: blur(4px);
    width: min(312px, 72vw);
    max-height: calc(46vh / var(--ctl-scale));
    overflow-y: auto;
}
#emote-palette.open { display: flex; }
.emote-btn {
    width: 44px;
    height: 44px;
    border-radius: 12px;
    background: rgba(72, 84, 96, 0.6);
    border: 2px solid transparent;
    cursor: pointer;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 26px;
    line-height: 1;
    transition: background 0.12s, transform 0.12s, border-color 0.12s;
}
.emote-btn:hover { background: rgba(102, 118, 138, 0.9); border-color: #feca57; transform: scale(1.08); }
.emote-btn:disabled { opacity: 0.4; cursor: default; transform: none; }
.emote-btn img { width: 32px; height: 32px; object-fit: contain; image-rendering: auto; pointer-events: none; }
.emote-btn.sel { border-color: #2ed573; background: rgba(46, 213, 115, 0.25); }
@media (max-height: 620px) {
    #emote-wrap { right: 8px; bottom: 8px; }
}

/* ---- shop "Reactions" tab: auto-reaction slot config + unlock grid ---- */
/* these rows sit inside the shop's item grid, so they must span every column */
.emoji-slots, .emoji-choose-hint, .emoji-chooser { grid-column: 1 / -1; }
.emoji-slots {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
    margin: 4px 0 10px;
}
.emoji-slot {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: rgba(72, 84, 96, 0.35);
    border: 2px solid #576574;
    border-radius: 12px;
    cursor: pointer;
    text-align: left;
    transition: border-color 0.12s, background 0.12s;
}
.emoji-slot:hover { border-color: #c8d6e5; }
.emoji-slot.active { border-color: #feca57; background: rgba(254, 202, 87, 0.16); }
.emoji-slot-face {
    flex: 0 0 auto;
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    font-size: 26px; line-height: 1;
}
.emoji-slot-face img { width: 30px; height: 30px; object-fit: contain; }
.emoji-slot-label { font-size: 13px; color: #dfe6e9; font-weight: 600; line-height: 1.2; }
.emoji-choose-hint { font-size: 13px; color: #feca57; margin: 2px 0 6px; font-weight: 600; }
.emoji-chooser {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 12px;
    padding-bottom: 12px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.emoji-ico {
    display: flex; align-items: center; justify-content: center;
    font-size: 34px; line-height: 1;
}
.emoji-ico img { width: 40px; height: 40px; object-fit: contain; }
@media (min-width: 620px) {
    .emoji-slots { grid-template-columns: repeat(4, 1fr); }
    .emoji-slot { flex-direction: column; text-align: center; gap: 5px; }
}

/* ============ PRIVATE ROOM MODAL ============ */
#private-screen {
    position: fixed;
    inset: 0;
    z-index: 95;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(3px);
}
#private-screen.visible { display: flex; }
.prv-card {
    width: min(380px, 92vw);
    zoom: max(0.85, var(--card-scale));
    max-height: calc(94vh / max(0.85, var(--card-scale)));
    overflow-y: auto;
    background: #2d3446;
    border: 2px solid #576574;
    border-radius: 18px;
    padding: 18px 22px 22px 22px;
    box-sizing: border-box;
}
.prv-text { font-size: 13px; line-height: 1.5; color: #c8d6e5; margin: 4px 0 16px 0; }
/* ⏱️ the two round-length sliders in the private-room modal */
.prv-time { margin: 0 2px 12px 2px; }
.prv-time label { display: block; font-size: 13px; font-weight: bold; color: #c8d6e5; margin-bottom: 8px; }
.prv-time b { color: #feca57; font-variant-numeric: tabular-nums; }
.prv-time input[type="range"] { width: 100%; margin-top: 4px; accent-color: #54a0ff; }
.prv-bots {
    display: block;
    font-size: 13px;
    font-weight: bold;
    color: #c8d6e5;
    margin: 0 2px 12px 2px;
    cursor: pointer;
}
.prv-bots input { accent-color: #54a0ff; margin-right: 6px; }
.prv-big {
    width: 100%;
    padding: 14px;
    font-family: inherit;
    font-size: 17px;
    font-weight: bold;
    color: #fff;
    background: linear-gradient(180deg, #2ed573, #1da355);
    border: none;
    border-radius: 12px;
    cursor: pointer;
    box-shadow: 0 4px 0 #158a44;
}
.prv-big:active { transform: translateY(3px); box-shadow: 0 1px 0 #158a44; }
.prv-divider {
    text-align: center;
    font-size: 12px;
    font-weight: bold;
    color: #95a5a6;
    margin: 16px 0 10px 0;
}
.prv-join-row { display: flex; gap: 8px; }
#prv-code {
    flex: 1;
    min-width: 0;
    padding: 12px;
    font-family: inherit;
    font-size: 16px;
    font-weight: bold;
    letter-spacing: 2px;
    text-transform: uppercase;
    text-align: center;
    color: #fff;
    background: #1e2533;
    border: 2px solid #3a4560;
    border-radius: 10px;
    outline: none;
}
#prv-code:focus { border-color: #54a0ff; }
#prv-join {
    padding: 12px 24px;
    font-family: inherit;
    font-size: 15px;
    font-weight: bold;
    color: #fff;
    background: #54a0ff;
    border: none;
    border-radius: 10px;
    cursor: pointer;
}

/* ============ PRIVATE ROOM BAR (menu, top-center) ============ */
#room-bar {
    position: fixed;
    top: 14px;
    left: 50%;
    transform: translateX(-50%) scale(var(--ctl-scale));
    transform-origin: top center;
    z-index: 85;
    display: none;
    align-items: center;
    gap: 10px;
    padding: 8px 10px 8px 18px;
    background: rgba(30, 39, 46, 0.9);
    border: 2px solid #feca57;
    border-radius: 16px;
    backdrop-filter: blur(4px);
}
#room-bar-code {
    color: #feca57;
    font-size: 16px;
    font-weight: bold;
    letter-spacing: 1px;
}
#room-bar button {
    padding: 7px 14px;
    font-family: inherit;
    font-size: 13px;
    font-weight: bold;
    border: none;
    border-radius: 9px;
    cursor: pointer;
}
#room-copy { background: #feca57; color: #1e272e; }
#room-copy:hover { background: #ffd97a; }
#room-leave { background: #2d3446; color: #c8d6e5; border: 2px solid #576574 !important; }
#room-leave:hover { color: #fff; border-color: #c8d6e5 !important; }

/* private-room members: PLAY is replaced by this pulsing wait pill */
#wait-host {
    pointer-events: none;
    padding: 20px 44px;
    font-size: 22px;
    font-weight: bold;
    color: #feca57;
    background: rgba(45, 52, 70, 0.92);
    border: 2px solid #feca57;
    border-radius: 18px;
    animation: wait-pulse 1.6s ease-in-out infinite;
}
@keyframes wait-pulse {
    0%, 100% { opacity: 0.6; }
    50% { opacity: 1; }
}

/* spectator attention bounce — any input while WATCHING pops the banner */
@keyframes spec-bounce {
    0%   { transform: translateX(-50%) translateY(0) scale(var(--ui-scale)); }
    30%  { transform: translateX(-50%) translateY(0) scale(calc(var(--ui-scale) * 1.18)); }
    60%  { transform: translateX(-50%) translateY(0) scale(calc(var(--ui-scale) * 0.94)); }
    100% { transform: translateX(-50%) translateY(0) scale(var(--ui-scale)); }
}
#match-banner.spec-bounce { animation: spec-bounce 0.5s ease; }

/* ============ IN-GAME ROOM CODE TAG (with Invite) ============ */
#room-code-tag {
    position: fixed;
    left: 20px;
    bottom: 90px;
    z-index: 55;
    display: none;
    align-items: center;
    gap: 8px;
    padding: 5px 6px 5px 14px;
    background: rgba(30, 39, 46, 0.85);
    color: #feca57;
    border: 2px solid #feca57;
    border-radius: 14px;
    font-size: 13px;
    font-weight: bold;
    letter-spacing: 1px;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom left;
}
#room-tag-invite {
    pointer-events: auto;
    padding: 5px 12px;
    font-family: inherit;
    font-size: 12px;
    font-weight: bold;
    color: #1e272e;
    background: #feca57;
    border: none;
    border-radius: 9px;
    cursor: pointer;
}
#room-tag-invite:hover { background: #ffd97a; }

/* disabled menu buttons (e.g. invite while still connecting) */
.mm-shop:disabled { opacity: 0.45; cursor: default; }
.mm-shop:disabled:hover { border-color: #576574; }

/* ============ CUSTOM SCROLLBARS (shop, overlay) ============ */
#shop-items::-webkit-scrollbar,
.overlay-card::-webkit-scrollbar,
.shop-card::-webkit-scrollbar {
    width: 10px;
}
#shop-items::-webkit-scrollbar-track,
.overlay-card::-webkit-scrollbar-track,
.shop-card::-webkit-scrollbar-track {
    background: #1e2533;
    border-radius: 5px;
}
#shop-items::-webkit-scrollbar-thumb,
.overlay-card::-webkit-scrollbar-thumb,
.shop-card::-webkit-scrollbar-thumb {
    background: #576574;
    border-radius: 5px;
    border: 2px solid #1e2533;
}
#shop-items::-webkit-scrollbar-thumb:hover,
.overlay-card::-webkit-scrollbar-thumb:hover,
.shop-card::-webkit-scrollbar-thumb:hover {
    background: #54a0ff;
}
#shop-items, .overlay-card { scrollbar-width: thin; scrollbar-color: #576574 #1e2533; }

/* Small frames (e.g. Chromebook 764x385): the banner stays centred and scales
   with the rest of the HUD, and --tut-max-w keeps it clear of the palette. */
@media (max-width: 900px), (max-height: 500px) {
    #tutorial-banner { padding: 8px 12px; }
}

/* Short frames: --ui-scale / --ctl-scale / --card-scale already shrink every
   panel continuously, so all that is left here is pulling things in off the
   edges so the smaller HUD does not float in the corners. */
@media (max-height: 620px) {
    #ui-container { top: 8px; left: 8px; }
    #paintMenu { right: 10px; }
    #mute-btn { right: 8px; }
    #home-btn { right: 48px; }
    #inbox-btn { right: 48px; }
    #cg-login-btn { top: 64px; right: 12px; }
    #room-code-tag { bottom: 74px; left: 12px; }
    #magnifier-btn { bottom: 12px; left: 12px; }
    #overlay-screen { padding-top: 10px; }
    .mm-actions { right: 14px; bottom: 12px; }
    #main-menu { padding: 14px; }
    #toast { bottom: 60px; }
}

/* ============ MUTE BUTTON ============ */
#mute-btn {
    position: fixed;
    top: 12px;
    right: 12px;
    /* ABOVE #main-menu (80). These are fixed controls: whatever the menu
       column grows into, Settings, the inbox and its unread badge have to
       stay both visible and tappable. The badge being painted over by a
       quest pill is exactly how a message arrived with no notification. */
    z-index: 90;
    width: 42px;
    height: 42px;
    font-size: 20px;
    line-height: 1;
    background: rgba(30, 39, 46, 0.85);
    border: 2px solid #576574;
    border-radius: 50%;
    cursor: pointer;
    backdrop-filter: blur(4px);
    transform: scale(var(--ctl-scale));
    transform-origin: top right;
}
#mute-btn:hover { border-color: #c8d6e5; }

/* ============ 📬 INBOX BUTTON (left of Settings) ============ */
#inbox-btn {
    position: fixed;
    top: 12px;
    right: 64px;          /* the slot left of #mute-btn; #home-btn shares it in-game */
    z-index: 90;          /* above the menu — see #mute-btn */
    width: 42px;
    height: 42px;
    padding: 0;
    align-items: center;
    justify-content: center;
    background: rgba(30, 39, 46, 0.85);
    border: 2px solid #576574;
    border-radius: 50%;
    cursor: pointer;
    backdrop-filter: blur(4px);
    transform: scale(var(--ctl-scale));
    transform-origin: top right;
}
#inbox-btn:hover { border-color: #c8d6e5; }
/* the unread count rides the rim; it is the only thing that makes the button
   worth looking at, so it stays legible at the smallest --ctl-scale */
#inbox-dot {
    position: absolute;
    top: -4px;
    right: -4px;
    min-width: 19px;
    height: 19px;
    padding: 0 4px;
    box-sizing: border-box;
    border-radius: 10px;
    background: #ff6b6b;
    color: #fff;
    font-size: 12px;
    font-weight: 700;
    line-height: 19px;
    text-align: center;
    border: 2px solid #1e272e;
}

/* ============ 📬 INBOX PANEL ============ */
#inbox-screen {
    position: fixed;
    inset: 0;
    z-index: 130;
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.72);
    padding: 16px;
}
#inbox-screen.visible { display: flex; }
/* A HEIGHT CAP, for the reason every centred panel in this game needs one:
   without it a short window pushes the header and the close button off screen
   and the whole thing reads as "it rendered in the background". */
/* Scaled by --card-scale like the shop and settings sheets, so on a phone it
   shrinks with the rest of the UI instead of sitting at desktop size inside
   a smaller window — that is what read as "cropped": a full-size card in a
   short frame, with the list squeezed to two rows. The height cap follows
   the same division so the whole thing still fits after the zoom.
   The 0.85 FLOOR was ignoring --card-scale exactly where it mattered: on a
   landscape phone the measured scale is 0.5 and the card still rendered at
   0.85, so two and a bit messages filled the frame. */
.inbox-card {
    width: min(560px, 94vw);
    zoom: max(0.68, min(1, var(--card-scale)));
    max-height: calc(92vh / max(0.68, min(1, var(--card-scale))));
    display: flex;
    flex-direction: column;
}
/* A wide, short frame has width to spare and no height at all. Spending the
   width buys height back: a wider row wraps to fewer lines. */
@media (max-height: 560px) and (orientation: landscape) {
    .inbox-card { width: min(820px, 96vw); }
    .inbox-msg { padding: 10px 12px; gap: 8px; }
    /* the card zooms to ~0.68 here, which took the 26px delete button down to
       18 rendered pixels — under a thumb. It stays deliberately modest (this
       is a destructive tap) but it has to be hittable. */
    .inbox-del { width: 34px; height: 34px; font-size: 17px; }
    .inbox-msg { padding-right: 46px; }
}
#inbox-list {
    overflow-y: auto;
    /* and never sideways. Setting overflow-y alone makes the browser compute
       overflow-x as auto, so any future stray pixel would put the scrollbar
       straight back. A message list has nothing to scroll to the right. */
    overflow-x: hidden;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    margin: 4px -4px 0;
    /* 📜 THE FOLD HAS TO LOOK LIKE A FOLD. A scrollable list ending flush
       against the card's edge slices its last row through the middle of a
       word, and that reads as broken layout rather than "there is more below"
       — it is what the owner kept calling cropped. The fade says scroll, and
       the bottom padding means the final message clears the rounded corner
       instead of being cut by it. */
    padding: 0 4px 2px;
    scroll-padding-bottom: 12px;
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 26px), transparent 100%);
}
/* ...but once the list is scrolled to the end there is nothing more to hint
   at, and a fade over the last message is just a smudge. */
#inbox-list.at-end {
    -webkit-mask-image: none;
    mask-image: none;
}
#inbox-list > :last-child { margin-bottom: 10px; }
.inbox-msg {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    width: 100%;
    /* 📏 width:100% PLUS 12+38px of padding and 2px of border, and this page
       has no global border-box — so every row came out 52px wider than the
       list holding it. overflow-y:auto forces overflow-x to auto, so that
       surplus became a horizontal scrollbar under a single welcome message,
       which is what kept reading as a broken panel. Arrived with the delete
       button, which is where the 38px came from. */
    box-sizing: border-box;
    text-align: left;
    background: rgba(255, 255, 255, 0.05);
    border: 2px solid #3d4852;
    border-radius: 10px;
    padding: 10px 12px;
    margin-bottom: 8px;
    cursor: pointer;
    color: #dfe6e9;
    font-family: inherit;
}
.inbox-msg:hover { border-color: #7f8fa6; background: rgba(255, 255, 255, 0.09); }
.inbox-msg { position: relative; padding-right: 38px; }
/* 🗑️ small and quiet in the corner: a message is deleted on purpose, not
   by a thumb grazing it — and it never covers the Claim button */
.inbox-del {
    position: absolute;
    top: 6px;
    right: 6px;
    width: 26px;
    height: 26px;
    /* quiet, but never invisible: on the dark card the first pass was so
       faint it did not show in a phone screenshot at all */
    border: 1px solid #6b7a8a;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    color: #c8d6e5;
    font-size: 14px;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}
.inbox-del:hover { color: #fff; border-color: #ff6b6b; background: rgba(255, 107, 107, 0.18); }
.inbox-del:focus-visible { outline: 2px solid #4a9de0; outline-offset: 1px; }
/* unread is carried by BOTH a colour and a dot — colour alone is not a signal
   every player can see */
/* 🔗 a link on a message — the push to the app store or Discord. Styled as
   a quiet secondary button so it never competes with Claim, which is the one
   that pays. */
/* 🖼️ the picture of what the message is about — a wing, a sticker, a face.
   Sits above the Claim button so the player sees the THING before the name. */
.inbox-art {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    margin: 8px 0 2px;
    min-height: 56px;
}
.inbox-art canvas, .inbox-art svg { width: auto; height: auto; max-width: 104px; max-height: 56px; display: block; }
.inbox-art-emoji { width: 52px; height: 52px; font-size: 40px; line-height: 1; }
.inbox-art-emoji img { width: 100%; height: 100%; object-fit: contain; }
.inbox-link {
    display: inline-block;
    margin-top: 8px;
    padding: 7px 14px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    color: #cfe6ff;
    background: rgba(74, 157, 224, 0.18);
    border: 2px solid #4a9de0;
    border-radius: 999px;
    cursor: pointer;
}
.inbox-link:hover { background: rgba(74, 157, 224, 0.3); color: #fff; }
.inbox-link:focus-visible { outline: 2px solid #8fd0ff; outline-offset: 2px; }
/* ⏳ and its deadline, quiet but present: a prize that will vanish has to say so */
.inbox-expiry { margin-top: 6px; font-size: 11px; color: #e8a33d; }
.inbox-msg.unread { border-color: #4a9de0; background: rgba(74, 157, 224, 0.12); }
.inbox-ico { font-size: 22px; line-height: 1.2; flex: none; }
.inbox-txt { flex: 1; min-width: 0; }
.inbox-title { font-weight: 700; color: #fff; font-size: 15px; margin-bottom: 2px; }
.inbox-msg.unread .inbox-title::after {
    content: '';
    display: inline-block;
    width: 8px; height: 8px;
    margin-left: 7px;
    border-radius: 50%;
    background: #4a9de0;
    vertical-align: middle;
}
.inbox-body { font-size: 13px; line-height: 1.45; color: #b2bec3; overflow-wrap: anywhere; }
.inbox-when { font-size: 11px; color: #7f8fa6; margin-top: 4px; }
/* The claim button. It is the only thing in the panel that pays, so it is the
   only thing that looks pressable. */
.inbox-claim {
    display: inline-block;
    margin-top: 8px;
    background: #f6b93b;
    color: #2d2000;
    font-family: inherit;
    font-weight: 700;
    font-size: 13px;
    border: 2px solid #c98f1d;
    border-radius: 999px;
    padding: 6px 14px;
    cursor: pointer;
}
.inbox-claim:hover { background: #ffd166; }
.inbox-claim:disabled { opacity: 0.55; cursor: default; }
.inbox-empty { text-align: center; color: #b2bec3; padding: 26px 10px; font-size: 14px; line-height: 1.6; }
.inbox-empty span { color: #7f8fa6; font-size: 12.5px; }
/* login button lives on the MENU only, below the coin pill so nothing overlaps */
#cg-login-btn { top: 84px; right: 30px; }

/* ============ EARLY-OUT OFFER (caught/disarmed with >60s left) ============ */
#requeue-panel {
    position: fixed;
    left: 50%;
    bottom: 96px;
    transform: translateX(-50%);
    z-index: 105;
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 14px 22px;
    background: rgba(32, 92, 107, 0.96);
    border: 3px solid #000;
    border-radius: 14px;
    box-shadow: 5px 5px 0 #000;
    text-align: center;
    zoom: max(0.85, var(--card-scale));
}
#requeue-panel.visible { display: flex; }
#rq-title { font-size: 18px; font-weight: 900; color: #feca57; text-shadow: 2px 2px 0 #000; }
.rq-text { font-size: 13px; font-weight: bold; color: #fff; }
.rq-btns { display: flex; gap: 10px; }
#rq-yes {
    padding: 9px 18px;
    font-family: inherit; font-size: 14px; font-weight: bold;
    color: #1e272e;
    background: linear-gradient(90deg, #2ed573, #7bed9f);
    border: 3px solid #000; border-radius: 10px;
    box-shadow: 3px 3px 0 #000; cursor: pointer;
}
#rq-yes:active { transform: translate(2px, 2px); box-shadow: 0 0 0 #000; }
#rq-no {
    padding: 9px 16px;
    font-family: inherit; font-size: 14px; font-weight: bold;
    color: #c8d6e5;
    background: #2d3446;
    border: 3px solid #000; border-radius: 10px;
    box-shadow: 3px 3px 0 #000; cursor: pointer;
}

/* ============ AFK WARNING ============ */
#idle-warning {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 115;
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 26px 44px;
    background: rgba(30, 39, 46, 0.96);
    border: 4px solid #ff4757;
    border-radius: 18px;
    box-shadow: 0 0 0 6px rgba(255, 71, 87, 0.25), 8px 8px 0 rgba(0,0,0,0.6);
    text-align: center;
    pointer-events: none;
    zoom: max(0.85, var(--card-scale));
    animation: iw-pulse 1s ease-in-out infinite;
}
#idle-warning.visible { display: flex; }
@keyframes iw-pulse {
    0%, 100% { border-color: #ff4757; }
    50% { border-color: #feca57; }
}
.iw-title {
    font-size: 30px;
    font-weight: 900;
    color: #ff6b81;
    text-shadow: 2px 2px 0 #000;
}
.iw-text { font-size: 14px; font-weight: bold; color: #dfe6e9; }
#iw-count {
    font-size: 56px;
    font-weight: 900;
    color: #feca57;
    line-height: 1;
    font-variant-numeric: tabular-nums;
    text-shadow: -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000, 2px 2px 0 #000;
}

/* ============ TOAST ============ */
#toast {
    position: fixed;
    left: 50%;
    bottom: 40px;
    transform: translateX(-50%) translateY(20px) scale(var(--ctl-scale));
    transform-origin: bottom center;
    z-index: 120;
    max-width: min(520px, 86vw);
    padding: 12px 22px;
    background: rgba(30, 39, 46, 0.95);
    color: #fff;
    font-size: 15px;
    font-weight: bold;
    border: 2px solid #feca57;
    border-radius: 14px;
    text-align: center;
    word-break: break-all;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s ease, transform 0.25s ease;
}
#toast.visible { opacity: 1; transform: translateX(-50%) translateY(0) scale(var(--ctl-scale)); }

/* ============ REWARDED AD BUTTON (Shop) ============ */
#reward-ad-btn {
    /* It used to be a full-width bar under the tabs, which pushed the things
       people came to look at off the bottom of a phone. It is a pill beside
       the coin count now — same offer, a twentieth of the room. */
    margin-left: auto;
    padding: 6px 11px;
    font-family: inherit;
    font-size: 14px;
    font-weight: bold;
    white-space: nowrap;
    color: #1e272e;
    background: linear-gradient(90deg, #2ed573, #7bed9f);
    border: none;
    border-radius: 999px;
    cursor: pointer;
}
.shop-head #reward-ad-btn + .shop-coins { margin-left: 10px; }
#reward-ad-btn:disabled { opacity: 0.5; cursor: wait; }

/* ============ EMOJI-FREE ICONOGRAPHY ============ */
/* Gold coin icon used everywhere a coin amount is shown */
.coin-ico {
    display: inline-block;
    width: 0.9em;
    height: 0.9em;
    border-radius: 50%;
    background: radial-gradient(circle at 35% 30%, #ffe9a8, #feca57 55%, #d99a2b);
    border: 0.12em solid #8a5f16;
    box-sizing: border-box;
    vertical-align: -0.08em;
    margin-right: 0.32em;
}

/* Online/offline status dot on the start overlay */
#net-status.online::before,
#net-status.offline::before {
    content: '';
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    margin-right: 7px;
    border: 2px solid rgba(0, 0, 0, 0.35);
    box-sizing: content-box;
    vertical-align: 0;
}
#net-status.online::before { background: #2ed573; }
#net-status.offline::before { background: #ff4757; }

/* Full-width hint line at the top of the Customize tab */
.shop-note {
    grid-column: 1 / -1;
    font-size: 12px;
    font-weight: bold;
    color: #c8d6e5;
    opacity: 0.75;
    text-align: center;
    padding: 2px 8px 6px 8px;
}

/* Shop item SVG icons */
.shop-item .si-icon { font-size: 0; line-height: 0; }
.shop-item .si-icon svg { width: 44px; height: 44px; }
/* the canvas carries the art's own aspect (see seasonItemIcon), so cap the
   sides rather than forcing a square — wide wings get width, a tall hat gets
   height, and the placeholder canvas is square and lands at 52x52 either way */
.shop-item .si-icon canvas { width: auto; height: auto; max-width: 104px; max-height: 52px; display: block; margin: 0 auto; }
/* 🎭 ...AND THE MASKS, WHICH ARE NEITHER. paintEmojiInto puts an <img> in the
   host, and there was no rule for one — so a mask drew at its natural size,
   around 140px, while every other icon on the shelf was 44. On a phone the
   theme caps it; everywhere else nothing did, which is why a row of holiday
   masks measured 399px tall against 256 for the row above it and the Masks
   group scrolled to nearly three thousand pixels. */
.shop-item .si-icon img { width: auto; height: 44px; max-width: 104px; display: block; margin: 0 auto; }
/* 💬 a phrase pack is words, not a picture: the four it carries ARE the art */
.shop-item .si-icon.phrase-ico { font-size: 30px; line-height: 1.1; }
.phrase-item .phrase-list { font-size: 11px; line-height: 1.45; color: #c8d6e5; min-height: 44px; }
/* ...but a reaction with no art file falls back to its GLYPH, which the
   font-size:0 above made invisible — the icon box measured 0px tall. */
.shop-item .si-icon.emoji-ico { font-size: 34px; line-height: 1; }

/* Mute + magnifier buttons center their SVGs */
#mute-btn { display: flex; align-items: center; justify-content: center; padding: 0; }
#magnifier-btn svg { vertical-align: middle; }

/* UI panels fade out when the player's stickman is behind them (JS toggles
   .ui-peek); hovering restores them so they stay usable. */
#ui-container { transition: opacity 0.25s ease; }
#match-banner { transition: transform 0.4s ease, opacity 0.25s ease; }
/* 📺 while an ad is up, the menu is not. The skip button is in the ad's
   top-right corner and this column was sitting on top of it. */
body.ad-playing .mm-actions,
body.ad-playing .mm-side,
body.ad-playing #mobile-controls,
body.ad-playing #ui-container { display: none !important; }

.ui-peek { opacity: 0.12 !important; }
.ui-peek:hover { opacity: 1 !important; }
/* 🕹️ the same idea for the touch controls, but nowhere near as far down.
   A panel at 0.12 is out of the way; a joystick at 0.12 is a control the
   player cannot find with their thumb, and there is no hover on a phone to
   bring it back. Enough to see the map through, enough to still aim at. */
.ctl-peek { opacity: 0.34 !important; }
.ctl-peek:hover { opacity: 1 !important; }

/* CSS-drawn play arrow on the PLAY button */
.mm-play::after {
    content: '';
    display: inline-block;
    margin-left: 16px;
    border-style: solid;
    border-width: 12px 0 12px 20px;
    border-color: transparent transparent transparent #fff;
    vertical-align: -1px;
}

/* ---- team-count pills in the match banner ---- */
.mb-count { padding: 3px 10px; border-radius: 10px; font-weight: bold; font-size: 14px; color: #fff; white-space: nowrap; }
.mb-count:empty { display: none; }
.mbh { background: #3867d6; }
.mbs { background: #e84118; }

/* ---- hider "zoom in & paint" nudge ---- */
#paint-hint { position: fixed; left: 50%; bottom: 23%; background: rgba(18, 23, 33, 0.93); color: #fff; padding: 10px 18px; border-radius: 12px; border: 2px solid #2ed573; font-size: 15px; display: none; z-index: 40; pointer-events: none; text-align: center; animation: ph-pulse 1.6s ease-in-out infinite; max-width: 86vw; }
#paint-hint.visible { display: block; }
/* 🎯 the seeker hint. Same shape as the hider one above so the two read as a
   pair, in the seekers' red rather than the painters' green, and higher up
   the screen because the seeker's thumb lives at the bottom. */
#seek-hint { position: fixed; left: 50%; transform: translateX(-50%); bottom: 30%; background: rgba(18, 23, 33, 0.95); color: #fff; padding: 12px 20px; border-radius: 12px; border: 2px solid #ff6b6b; font-size: 15px; display: none; z-index: 41; pointer-events: none; text-align: center; max-width: 86vw; animation: ph-pulse 1.6s ease-in-out infinite; }
#seek-hint.visible { display: block; }
#seek-hint b { display: block; color: #ff6b6b; font-size: 17px; letter-spacing: 0.5px; margin-bottom: 3px; }
#paint-hint b { color: #2ed573; }
@keyframes ph-pulse { 0%, 100% { transform: translateX(-50%) scale(1); } 50% { transform: translateX(-50%) scale(1.045); } }

/* ---- round-end scoreboard ---- */
#overlay-board { margin: 10px auto 2px; max-width: 350px; max-height: 176px; overflow-y: auto; text-align: left; }
.ob-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; background: rgba(255, 255, 255, 0.07); border-radius: 8px; padding: 5px 10px; margin-bottom: 4px; font-size: 14px; }
.ob-row.me { outline: 2px solid #f1c40f; }
.ob-name { display: flex; align-items: center; gap: 7px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ob-rank { font-style: normal; font-size: 10px; font-weight: bold; padding: 1px 6px; border-radius: 6px; color: #10141c; flex: none; }
.ob-stat { flex: none; font-weight: bold; }
.ob-stat.win { color: #2ed573; }
.ob-stat.lose { color: #ff4757; }
.ob-stat.seek { color: #ff9f43; }

/* ---- quick-chat row in the reaction palette ---- */
.qc-row { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; max-width: 264px; }
.qc-btn { background: #2f3640; color: #fff; border: 2px solid #000; border-radius: 14px; padding: 5px 11px; font-family: inherit; font-size: 13px; font-weight: bold; cursor: pointer; }
.qc-btn:hover { background: #3d4654; }
.qc-btn:disabled { opacity: 0.45; cursor: default; }

/* ---- 👻 warm-up ghost badge ---- */
#match-role.ghost { color: #10141c; background: #a4b0be; }

/* the badge is one word, never two lines */
#match-role { white-space: nowrap; }

/* ---- Discord link in the settings panel ---- */
#discord-link { display: flex; align-items: center; justify-content: center; gap: 9px; margin-top: 14px; padding: 10px 14px; background: #5865f2; color: #fff; border: 2px solid #000; border-radius: 12px; font-family: inherit; font-size: 15px; font-weight: bold; text-decoration: none; cursor: pointer; }
#discord-link:hover { background: #6c78f5; }
#discord-link svg { flex: none; }


/* ---- touch scrolling for panels ----
   body carries touch-action:none, which the spec applies to touches STARTING
   on any descendant, so a scrollable panel must opt back in explicitly or a
   finger cannot pan it. (The class ladder ALSO needed pointer-events:auto —
   see #rank-ladder — without which no gesture reached it at all.) */
#mobile-controls, #joystick-base, #joystick-stick, #jump-btn { touch-action: none; }
#rank-ladder, #ach-list, #album-list, #overlay-board, .shop-items, .shop-card, .audio-card,
.overlay-card, #paintMenu, #shop-screen, #ach-screen {
    touch-action: pan-y;
    -webkit-overflow-scrolling: touch;
}

/* ---- 🗺️ next-map vote (beside the result card) ---- */
#overlay-screen { flex-wrap: wrap; gap: 12px; align-content: flex-start; }
/* PORTRAIT PHONES: there is no second column, so "beside the result card"
   becomes "wrapped underneath, stretched to a 59px sliver" — the ballot was
   there but unreadable, which reads as the map choice being missing. Stack it
   properly at full width and let the overlay scroll to reach it. */
@media (orientation: portrait) and (max-width: 820px) {
    #overlay-screen {
        flex-direction: column; flex-wrap: nowrap;
        align-items: center; align-content: normal;
        overflow-y: auto;
        padding-bottom: calc(24px + env(safe-area-inset-bottom));
    }
    #map-vote {
        align-self: auto; flex: 0 0 auto;
        width: min(440px, 94vw);
    }
}
#map-vote {
    pointer-events: auto;
    /* fill the column beside the result card instead of hugging six tiny
       thumbnails — the ballot is a headline feature, not a footnote */
    align-self: stretch;
    width: min(390px, 38vw);
    display: flex;
    flex-direction: column;
    background: #205c6b;
    border: 4px solid #000;
    border-radius: 16px;
    padding: 14px 16px;
    box-shadow: 8px 8px 0 #000;
    box-sizing: border-box;
    zoom: max(0.85, var(--card-scale));
    max-height: calc((100vh - 56px) / max(0.85, var(--card-scale)));
    overflow-y: auto;
    touch-action: pan-y;
}
.mv-title { font-size: 17px; font-weight: 900; color: #feca57; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 10px; text-align: center; flex: none; }
/* the six tiles share the leftover height evenly, so the ballot always fills
   the card's column whatever the frame size */
#mv-list { flex: 1; min-height: 0; display: grid; grid-template-columns: repeat(2, 1fr); grid-auto-rows: 1fr; gap: 10px; }
.mv-item {
    position: relative;
    width: 100%; height: 100%; min-height: 62px;
    padding: 0; margin: 0;
    border: 3px solid #000; border-radius: 11px;
    overflow: hidden; cursor: pointer; background: #123;
    font-family: inherit;
}
.mv-item img { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
/* 🔍 the closer look, on the EDITOR's community ballot. It used to be on the
   end-of-match ballot too and was removed from there: that one runs on a
   timer across six small cards and had no room for a second control. Top-right,
   away from the vote count in the other
   corner, and big enough to hit with a thumb without covering the art it is
   offering to show. pointer-events ON — it is the one child of the card that
   has its own job. */
.mv-peek {
    position: absolute; top: 4px; right: 4px; z-index: 2;
    width: 30px; height: 30px; line-height: 30px; text-align: center;
    font-size: 15px; border-radius: 9px;
    background: rgba(8, 12, 20, .74); color: #e8eefc;
    pointer-events: auto; cursor: zoom-in;
}
.mv-peek:hover { background: rgba(8, 12, 20, .92); }
.mv-item:hover { border-color: #c8d6e5; }
.mv-item.mine { border-color: #2ed573; box-shadow: 0 0 0 3px #2ed573; }
.mv-name {
    position: absolute; left: 0; right: 0; bottom: 0;
    background: rgba(0,0,0,0.66); color: #fff;
    font-size: 13px; font-weight: bold; line-height: 1.7;
    text-transform: capitalize; text-align: center;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    pointer-events: none;
}
.mv-count {
    position: absolute; top: 4px; right: 4px;
    min-width: 24px; padding: 0 6px;
    background: #feca57; color: #10141c;
    font-size: 15px; font-weight: 900; line-height: 24px;
    border: 2px solid #000;
    border-radius: 12px; text-align: center;
    pointer-events: none;
}
/* narrow frames: the ballot goes under the card as one wide row */
/* Only PORTRAIT phones wrap the ballot under the card — a short landscape
   frame has no vertical room for a second row, so there it stays beside the
   card and simply narrows. */
@media (max-width: 560px) {
    #map-vote { align-self: auto; width: min(420px, 94vw); }
    #mv-list { grid-template-columns: repeat(3, 1fr); grid-auto-rows: 62px; }
    .mv-title { font-size: 14px; margin-bottom: 7px; }
    .mv-name { font-size: 11px; line-height: 1.6; }
}

/* ---- warm-up notice: its own full-width line under the banner row ---- */
#match-banner { flex-wrap: wrap; justify-content: center; }
/* the pills keep their size and the phase text gives way instead, so
   "2 seekers" can never be pushed onto a second line */
#match-banner > span { flex: none; }
#match-phase { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* narrow frames: tighter spacing, and the pills carry bare counts (set in
   updateMatchHud) so the row still fits on one line */
/* Narrow OR short: a landscape phone is 844 wide and 390 tall, so a
   width-only breakpoint gave it the roomy desktop banner and it wrapped to
   three lines. updateMatchHud() gates its bare-count pills on the same pair. */
@media (max-width: 700px), (max-height: 520px) {
    /* NOT nowrap. #mb-note is a full-width child that needs its own row, and
       forcing one line pushed it straight out of the banner background — the
       "banner does not cover all the text" report. The pill wording is what
       keeps the main row to one line (see `tight` in updateMatchHud). */
    #match-banner {
        gap: 6px; padding: 5px 10px;
        /* left:50% means the layout box only gets the RIGHT HALF of the screen
           to size itself in, so the seeker pill wrapped to a second row on
           every portrait phone. max-content ignores that squeeze; the cap
           keeps it inside the frame, and divides by the scale because the
           banner is transform-scaled after layout. */
        width: max-content;
        max-width: calc(94vw / var(--ui-scale, 1));
    }
    #mb-note { white-space: normal; overflow-wrap: anywhere; }
    #match-timer { font-size: 34px; min-width: 44px; }
    .mb-count { padding: 3px 8px; }
}
#mb-note {
    display: none;
    flex-basis: 100%;
    width: 100%;
    text-align: center;
    font-size: 12px;
    font-weight: bold;
    color: #dfe4ea;
    border-top: 1px solid rgba(255,255,255,0.18);
    padding-top: 4px;
    margin-top: 2px;
}
#mb-note.visible { display: block; }

/* Very narrow frames (portrait phones): the flavour line is the first thing
   to go — the role badge, the two count pills and the timer already say
   everything it does, and keeping it forced the row onto two lines. */
@media (max-width: 430px) {
    #match-phase { display: none; }
    #match-banner { gap: 5px; padding: 5px 9px; }
}

/* ---- 🔍 zoom panel (left edge, opposite the paint menu) ---- */
#zoom-panel {
    position: absolute;
    left: 14px;
    top: 50%;
    transform: translateY(-50%) scale(max(0.95, var(--ui-scale)));
    transform-origin: left center;
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 9px;
    width: 176px;
    padding: 14px 14px 13px;
    background: rgba(30, 39, 46, 0.9);
    border: 2px solid #576574;
    border-radius: 14px;
    z-index: 16;
    /* The panel sits at top:50% — right in the left playfield. A solid
       pointer-events slab there ATE a seeker's hits on any hider behind it
       (the click never reached the canvas, so no swing happened at all) and
       :hover flicked it back to full opacity mid-swing. Same convention as
       #ui-container and #main-menu: the panel is inert, controls opt back in. */
    pointer-events: none;
    box-sizing: border-box;
}
#zoom-panel.visible { display: flex; }
.zp-title { font-size: 15px; font-weight: 900; color: #feca57; text-transform: uppercase; letter-spacing: 0.5px; }
#zoom-slider { width: 100%; margin: 0; accent-color: #54a0ff; height: 22px; pointer-events: auto; }
#zoom-val { font-size: 22px; font-weight: 900; color: #fff; font-variant-numeric: tabular-nums; }
#zoom-auto-row { pointer-events: auto; display: flex; align-items: center; gap: 8px; font-size: 12px; line-height: 1.3; color: #dfe4ea; cursor: pointer; }
#zoom-auto-row input { margin: 0; flex: none; accent-color: #2ed573; width: 18px; height: 18px; }
/* Short frames (any landscape phone): park it at the top-left, and make it
   COMPACT. At the old size it still reached down into the joystick — there is
   only ~145px of clear left edge between the HUD and the thumb zone on a
   844x390 screen, so the caption goes and the rows tighten. */
@media (max-height: 500px) {
    #zoom-panel { top: 94px; transform: translateY(0) scale(max(0.82, var(--ui-scale))); transform-origin: top left; width: 150px; padding: 8px; gap: 4px; }
    #zoom-panel .zp-title { display: none; }
    #zoom-panel #zoom-val { font-size: 16px; }
    #zoom-panel #zoom-auto-row { font-size: 10px; line-height: 1.2; }
    #zoom-panel #zoom-auto-row input { width: 16px; height: 16px; }
}

/* ---- 🎨 map painter ---- */
#map-editor {
    position: fixed; inset: 0; z-index: 120; display: none;
    flex-direction: column; background: #10141c;
}
#map-editor.visible { display: flex; }
.me-bar {
    display: flex; align-items: center; gap: 10px;
    padding: 8px 14px; background: #1e272e; border-bottom: 2px solid #576574; flex: none;
}
/* A phone has nowhere near the width for eight controls, so the row ran off
   the right edge — taking DONE, the only way back to the menu, with it. The
   bar scrolls sideways now and Done is pinned to the left of it. */
@media (max-width: 900px), (max-height: 560px) {
    .me-bar { flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; touch-action: pan-x; padding: 8px 10px; }
    .me-bar > * { flex: none; }
    .me-title, .me-spacer { display: none; }
    #me-close { order: -1; position: sticky; left: 0; z-index: 2; background: #576574; color: #fff; box-shadow: 6px 0 8px -4px #1e272e; }
}
.me-title { font-size: 18px; font-weight: 900; color: #feca57; }
.me-status { font-size: 12px; color: #7f8fa6; position: relative; display: inline-flex; align-items: center; gap: 6px; cursor: help; }
/* 📐 the complexity meter: a bar and a number, no words. The words live in a
   popover: hover on desktop, tap on touch (the .show-tip class). */
.me-cx-bar { width: 74px; height: 8px; border-radius: 4px; background: rgba(255,255,255,0.14); overflow: hidden; display: inline-block; }
.me-cx-bar i { display: block; height: 100%; border-radius: 4px; transition: width 0.25s; }
.me-status b { font-size: 13px; }
.me-cx-tip { display: none; position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
    width: 230px; padding: 9px 11px; border-radius: 10px; background: #1e272e; color: #dfe6ee;
    border: 1px solid rgba(255,255,255,0.15); box-shadow: 0 6px 18px rgba(0,0,0,0.45);
    font-size: 12px; line-height: 1.35; cursor: default; }
.me-cx-tip span { display: block; }
/* 📋 the breakdown above the tip: one line per rule, ✓ or ✗ */
.me-cx-rows { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 12px; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid rgba(255,255,255,0.12); }
.me-cx-rows em { font-style: normal; font-size: 12px; color: #8395a7; white-space: nowrap; }
.me-cx-rows em b { color: #dfe6e9; font-weight: bold; }
.me-cx-rows em.ok { color: #2ed573; }
.me-cx-rows em.bad { color: #ff4757; }
#galonly-screen { position: fixed; inset: 0; z-index: 97; display: none; align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#galonly-screen.visible { display: flex; }
.galonly-text { font-size: 14px; color: #dfe6e9; line-height: 1.45; margin: 4px 0 14px; text-align: left; }
.me-cx-tip span + span { margin-top: 6px; }
.me-status:hover .me-cx-tip, .me-status.show-tip .me-cx-tip { display: block; }
.me-spacer { flex: 1; }
.me-btn {
    font-family: inherit; font-size: 13px; font-weight: bold; color: #fff;
    background: #2f3640; border: 2px solid #000; border-radius: 9px;
    padding: 7px 13px; cursor: pointer;
}
.me-btn:hover { background: #3d4654; }
.me-btn.me-go { background: #2ed573; color: #10141c; }
.me-tool.active { background: #54a0ff; color: #10141c; }
.me-body { flex: 1; display: flex; min-height: 0; }
.me-tools {
    width: 190px; flex: none; padding: 12px; box-sizing: border-box;
    background: #1a222c; border-right: 2px solid #576574;
    overflow-y: auto; touch-action: pan-y;
    display: flex; flex-direction: column; gap: 14px;
}
.me-group { display: flex; flex-direction: column; gap: 6px; }
.me-label { font-size: 11px; font-weight: 900; color: #feca57; text-transform: uppercase; letter-spacing: .5px; }
#me-palette { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; }
.me-swatch { width: 100%; aspect-ratio: 1; border: 2px solid #000; border-radius: 5px; cursor: pointer; padding: 0; }
.me-swatch.sel { outline: 2px solid #fff; outline-offset: 1px; }
#me-size { width: 100%; accent-color: #54a0ff; }
.me-toolrow { display: flex; gap: 6px; flex-wrap: wrap; }
.me-toolrow .me-btn { flex: 1; padding: 7px 6px; }
.me-hint { font-size: 11px; color: #7f8fa6; line-height: 1.4; margin-top: auto; }
/* NO justify-content/align-items centring here. With overflow:auto, centring
   an oversized flex item spills it equally both ways and the start side
   becomes unreachable — you can scroll right and down, never left and up, so
   a zoomed-in painter simply could not reach two edges of their own map.
   margin:auto on the canvas centres it while it fits and stops clipping once
   it does not. */
.me-stage { flex: 1; min-width: 0; display: flex; padding: 12px; }
#me-canvas {
    background: #000; border: 2px solid #576574; border-radius: 8px;
    max-width: 100%; max-height: 100%; image-rendering: auto;
    cursor: crosshair; touch-action: none;
    /* the stage is a flexbox: without these, a zoomed canvas gets shrunk
       right back to fit. margin:auto centres it without clipping the start
       edges once it overflows (see .me-stage) */
    margin: auto;
    flex: none;
    margin: auto;
}
@media (max-width: 700px) {
    .me-body { flex-direction: column-reverse; }
    .me-tools { width: 100%; border-right: none; border-top: 2px solid #576574; flex-direction: row; flex-wrap: wrap; gap: 10px; max-height: 42vh; }
    .me-group { flex: 1 1 140px; }
    .me-hint { display: none; }
}

/* ---- map test bar: a second row INSIDE the match banner ---- */
#map-test-bar {
    display: none;
    flex-basis: 100%;
    align-items: center;
    justify-content: center;
    gap: 10px;
    border-top: 1px solid rgba(255,255,255,0.18);
    padding-top: 5px;
    margin-top: 3px;
    font-size: 13px;
    font-weight: bold;
    color: #9fd0ff;
    pointer-events: none; /* the banner is inert — only the button acts */
}
#map-test-bar.visible { display: flex; }
#map-test-bar button { pointer-events: auto; }
#mt-back {
    font-family: inherit; font-size: 13px; font-weight: bold;
    background: #10141c; color: #fff; border: 2px solid #000;
    border-radius: 8px; padding: 5px 11px; cursor: pointer;
}

/* ---- submissions gallery inside the painter ---- */
#me-gallery-pane {
    display: none; width: 260px; flex: none; box-sizing: border-box;
    padding: 12px; background: #1a222c; border-left: 2px solid #576574;
    overflow-y: auto; touch-action: pan-y;
}
#me-gallery-pane.open { display: block; }
#me-gallery-list { display: flex; flex-direction: column; gap: 8px; margin-top: 8px; }
.mg-row { background: #232c3d; border: 2px solid #000; border-radius: 9px; padding: 8px 10px; }
.mg-row.approved { border-color: #2ed573; }
.mg-row.gallery { border-color: #b4791f; }
.mg-row.rejected { border-color: #ffa502; /* amber, not alarm-red: these notes go to children */ }
.mg-title { font-size: 13px; font-weight: bold; color: #fff; }
.mg-status { font-size: 11px; color: #b8c2cf; line-height: 1.4; margin-top: 3px; }
.mg-redo { margin-top: 7px; width: 100%; padding: 6px; font-size: 12px; }
@media (max-width: 700px) {
    #me-gallery-pane { width: 100%; border-left: none; border-top: 2px solid #576574; max-height: 40vh; }
}

/* ---- painter: custom colour mixer, brush shapes, pixel mode ---- */
.me-custom { display: flex; flex-direction: column; gap: 5px; margin-top: 7px; }
.me-custom input[type=range] { width: 100%; margin: 0; height: 18px; }
#me-hue { accent-color: #ff7675; }
#me-light { accent-color: #dfe6e9; }
#me-swatch-preview {
    display: block; height: 22px; border: 2px solid #000; border-radius: 6px; background: #1d6f85;
}
.me-shape.active { background: #54a0ff; color: #10141c; }
.me-check {
    display: flex; align-items: center; gap: 7px; font-size: 11px;
    color: #cfd8e6; cursor: pointer; margin-top: 6px; line-height: 1.3;
}
.me-check input { flex: none; width: 15px; height: 15px; margin: 0; accent-color: #2ed573; }
#me-pixel-size { width: 100%; margin: 4px 0 0; accent-color: #54a0ff; }

/* ---- editor v2: zoom, collapsible sections, brush ring ---- */
.me-zoom-wrap { display: flex; align-items: center; gap: 6px; font-size: 14px; color: #cfd8e6; }
.me-zoom-wrap input { width: 110px; accent-color: #54a0ff; margin: 0; }
#me-zoom-val { font-size: 12px; font-weight: bold; min-width: 34px; }
.me-stage { overflow: auto; }
.me-group > .me-label { cursor: pointer; user-select: none; }
.me-group > .me-label::after { content: ' ▾'; opacity: 0.55; }
.me-group.collapsed > .me-label::after { content: ' ▸'; }
/* !important: children like #me-palette carry id-specificity that would
   otherwise beat this class rule and refuse to collapse */
#map-editor .me-group.collapsed > :not(.me-label) { display: none !important; }
#me-ring {
    position: fixed; display: none; pointer-events: none; z-index: 130;
    border: 2px solid #fff; box-shadow: 0 0 0 1.5px rgba(0,0,0,0.75), inset 0 0 0 1.5px rgba(0,0,0,0.75);
    box-sizing: border-box;
}

/* ---- 🪙 extra review slot offer ---- */
#map-sent {
    position: absolute;
    inset: 0;
    z-index: 30;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0,0,0,0.72);
}
#map-sent .mp-card { text-align: center; }
#ms-discord {
    display: block;
    text-decoration: none;
    background: #5865F2;   /* Discord blurple — it reads as "that button" */
    color: #fff;
}
#me-pay {

    position: absolute; inset: 0; z-index: 10;
    display: none; align-items: center; justify-content: center;
    background: rgba(10, 13, 20, 0.75);
}
.mp-card {
    display: flex; flex-direction: column; gap: 10px;
    width: min(320px, 88vw); padding: 20px 22px;
    background: #1e2734; border: 3px solid #000; border-radius: 16px;
    box-shadow: 8px 8px 0 rgba(0,0,0,0.45); text-align: center;
}
.mp-big { font-size: 30px; font-weight: 900; color: #feca57; }
.mp-text { font-size: 13px; color: #cfd8e6; line-height: 1.5; }
.mp-card .me-btn { padding: 11px; font-size: 14px; }
.mp-card .me-btn:disabled { opacity: 0.4; cursor: default; }
.mp-balance { font-size: 12px; color: #8b97ab; }

/* ---- 🗳️ community ballot pane + next-map art thumbs ---- */
#me-vote-pane {
    display: none; width: 300px; flex: none; box-sizing: border-box;
    padding: 12px; background: #1a222c; border-left: 2px solid #576574;
    overflow-y: auto; touch-action: pan-y;
}
#me-vote-pane.open { display: block; }
.mvp-title { font-size: 15px; font-weight: 900; color: #feca57; }
.mvp-sub { font-size: 11px; color: #8b97ab; margin: 4px 0 10px; }
#me-vote-list { display: flex; flex-direction: column; gap: 10px; }
/* position: relative so the magnifier badge can sit on the thumbnail — this
   ballot is where a closer look belongs: no timer, one map per row, and the
   question is whether a map belongs in the game at all. */
.mvp-row { position: relative; background: #232c3d; border: 2px solid #000; border-radius: 10px; overflow: hidden; }
.mvp-thumb { width: 100%; display: block; image-rendering: auto; }
.mvp-meta { font-size: 12px; font-weight: bold; color: #fff; padding: 6px 9px 0; }
.mvp-btns { display: flex; gap: 8px; padding: 7px 9px 9px; }
.mvp-btns button {
    flex: 1; font-size: 20px; padding: 7px 0; cursor: pointer;
    background: #2f3640; border: 2px solid #000; border-radius: 9px;
}
.mvp-btns button.on { background: #2ed573; }
.mvp-btns button:last-child.on { background: #ff4757; }
.mv-art { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
@media (max-width: 700px) {
    #me-vote-pane { width: 100%; border-left: none; border-top: 2px solid #576574; max-height: 45vh; }
    /* 🔍 ...and with the pane across the whole width, the toolbar's CANVAS
       zoom is a control for something you are not looking at — sitting
       directly above a list of maps, where it reads as part of the voting.
       Above 700px the pane is a sidebar, the canvas is still beside it, and
       the zoom still does its job, so it stays. */
    #map-editor.pane-open .me-zoom-wrap { display: none; }
}

/* ---- 🖼️ the gallery, in the editor's right-hand column ---- */
#me-wall-pane {
    display: none; width: 300px; flex: none; box-sizing: border-box;
    padding: 12px; background: #1a222c; border-left: 2px solid #576574;
    overflow-y: auto; touch-action: pan-y;
}
#me-wall-pane.open { display: block; }
#me-wall-list { display: flex; flex-direction: column; gap: 10px; }
.mw-row {
    background: #232c3d; border: 2px solid #000; border-radius: 10px;
    overflow: hidden; cursor: pointer;
}
.mw-thumb { width: 100%; display: block; image-rendering: auto; }
.mw-meta { font-size: 12px; font-weight: bold; color: #fff; padding: 6px 9px 8px; }
.mw-by { font-size: 11px; font-weight: normal; color: #8b97ab; }
#me-wall-view {
    position: absolute; inset: 0; z-index: 31;
    align-items: center; justify-content: center;
    background: rgba(0, 0, 0, 0.82);
}
/* the picture is the point, so it takes the room and the chrome shrinks
   around it — the same trap the player card fell into, capped up front */
/* .mp-card is 320px wide by design — a picture is the one thing in this
   game that deserves the whole window, so this one overrides that outright */
.mwv-card { text-align: center; width: min(1100px, 94vw); max-height: 92vh;
    display: flex; flex-direction: column; gap: 8px; }
.mwv-canvas {
    align-self: center;
    width: auto; height: auto; max-width: 100%; max-height: 66vh;
    border: 3px solid #000; border-radius: 8px; background: #0d1017;
}
.mwv-name { font-size: 17px; font-weight: 900; color: #fff; }
.mwv-by { font-size: 13px; color: #8b97ab; }
@media (max-width: 700px) {
    #me-wall-pane { width: 100%; border-left: none; border-top: 2px solid #576574; max-height: 45vh; }
    .mwv-canvas { max-height: 52vh; }
}

/* ---- 🕷️ Spider Sense button ---- */
#spider-btn {
    position: absolute; right: 20px; bottom: 210px;
    width: 62px; height: 62px; display: none;
    align-items: center; justify-content: center;
    font-size: 26px; font-family: inherit; font-weight: 900;
    background: #2f3640; color: #fff;
    border: 3px solid #000; border-radius: 50%;
    box-shadow: 3px 3px 0 rgba(0,0,0,0.4);
    transform: scale(var(--ctl-scale)); transform-origin: bottom right;
    z-index: 18; cursor: pointer; pointer-events: auto;
}
#spider-btn.ready { background: #8e44ad; animation: sp-pulse 1.3s ease-in-out infinite; }
@keyframes sp-pulse {
    0%, 100% { box-shadow: 3px 3px 0 rgba(0,0,0,0.4), 0 0 0 0 rgba(142,68,173,0.6); }
    50% { box-shadow: 3px 3px 0 rgba(0,0,0,0.4), 0 0 0 12px rgba(142,68,173,0); }
}

/* ---- 📊 camo score on the result card ---- */
#overlay-camo { margin: 6px 0 12px; line-height: 1; }
.oc-label { display: block; font-size: 13px; font-weight: 900; letter-spacing: 2px; color: #8fa4bb; }
.oc-num { display: block; font-size: 54px; font-weight: 900; color: #fff; text-shadow: 3px 3px 0 #000; }
.oc-pct { font-size: 26px; margin-left: 2px; }
.oc-grade { display: block; font-size: 13px; font-weight: bold; color: #c8d6e5; margin-top: 2px; }
#overlay-camo.great .oc-num { color: #2ed573; }
#overlay-camo.perfect .oc-num { color: #feca57; text-shadow: 3px 3px 0 #000, 0 0 22px rgba(254,202,87,0.75); }
#overlay-camo.slam { animation: oc-slam 0.42s cubic-bezier(.2,1.4,.4,1); }
@keyframes oc-slam {
    0% { transform: scale(2.1); opacity: 0; }
    60% { transform: scale(0.94); opacity: 1; }
    100% { transform: scale(1); }
}

/* camo readout docked in the map-vote panel (it has the spare room) */
#map-vote #overlay-camo {
    margin: 0 0 10px; padding-bottom: 9px;
    border-bottom: 1px solid rgba(255,255,255,0.18);
}
#overlay-camo-slot:empty { display: none; }

/* On touch the bottom half of the screen belongs to the joystick, the zoom
   panel and the paint button, so the nudge cannot live down there — it landed
   on top of all three on small frames. Put it in the clear band under the
   match banner instead, and keep it narrow enough to miss the left lane. */
/* On a phone there is no free band for a floating nudge — every candidate
   spot belonged to the joystick, the zoom panel, the paint button or the
   palette, at one size or another. So on touch it stops floating and becomes
   the first line of the paint menu, which is exactly where the tools it talks
   about live. moveHintIntoMenu() does the reparenting. */
#paint-hint.in-menu {
    position: static;
    max-width: none; width: 100%;
    margin: 0; padding: 7px 9px;
    font-size: 12px; line-height: 1.35;
    border-width: 2px;
    animation: none;
    order: -1;
}

/* Finger-sized paint controls. These bases are chosen so that AFTER the touch
   --ui-scale floor (UI_TOUCH_ACROSS_FLOOR) they land back at roughly the
   desktop sizes — a 24px hex and a ~22px slider — instead of the 9px hexes and
   8px sliders a portrait phone was getting. */
body.touch .hex-cell { width: 30px; height: 34px; margin: 0 2px; }
body.touch .hex-row { margin-top: -7px; }
body.touch #cc-hue,
body.touch #cc-light,
body.touch #brush-slider { height: 28px; border-radius: 14px; }
body.touch #cc-hue::-webkit-slider-thumb,
body.touch #cc-light::-webkit-slider-thumb,
body.touch #brush-slider::-webkit-slider-thumb { width: 28px; height: 28px; }

/* 🏁 TIME'S UP — the 2s beat over the map before the result card appears.
   pointer-events:none so it never eats a tap, and it sits under the overlay's
   z-index so the card can come up on top of it if the two ever race. */
#time-up {
    position: fixed; inset: 0; z-index: 28;
    display: none; flex-direction: column;
    align-items: center; justify-content: center;
    pointer-events: none; text-align: center;
}
#time-up.visible { display: flex; }
#time-up span {
    font-size: clamp(38px, 11vw, 120px);
    font-weight: 900; letter-spacing: 2px; color: #feca57;
    -webkit-text-stroke: 3px #000;
    text-shadow: 6px 6px 0 #000;
    animation: tu-pop 0.45s cubic-bezier(.2,1.6,.4,1) both;
}
#time-up small {
    margin-top: 10px; font-size: clamp(13px, 3.2vw, 20px);
    font-weight: 800; color: #fff;
    background: rgba(18, 23, 33, 0.85);
    border: 2px solid #000; border-radius: 10px; padding: 5px 14px;
    animation: tu-pop 0.45s 0.12s cubic-bezier(.2,1.6,.4,1) both;
}
@keyframes tu-pop {
    from { opacity: 0; transform: scale(0.55) translateY(18px); }
    to   { opacity: 1; transform: scale(1) translateY(0); }
}

/* 🏅 RANK UP — the loudest moment in the game. Sits above the result card but
   pointer-events:none, so it can never swallow a tap on the buttons beneath. */
#rank-up {
    position: fixed; left: 0; right: 0; top: 16%;
    z-index: 45; display: none;
    flex-direction: column; align-items: center;
    pointer-events: none; text-align: center;
}
#rank-up.visible { display: flex; }
.ru-kicker {
    font-size: clamp(14px, 3.4vw, 22px); font-weight: 900; letter-spacing: 6px;
    color: #fff; background: #ee5253;
    border: 3px solid #000; border-radius: 8px; padding: 3px 16px;
    box-shadow: 4px 4px 0 #000;
    animation: ru-in 0.4s cubic-bezier(.2,1.7,.4,1) both;
}
.ru-name {
    margin-top: 8px;
    font-size: clamp(34px, 9vw, 86px); font-weight: 900; letter-spacing: 1px;
    color: #feca57; -webkit-text-stroke: 3px #000;
    text-shadow: 6px 6px 0 #000;
    animation: ru-in 0.5s 0.08s cubic-bezier(.2,1.7,.4,1) both, ru-glow 1.1s 0.55s ease-in-out infinite alternate;
}
.ru-sub {
    margin-top: 6px; font-size: clamp(12px, 2.8vw, 17px); font-weight: 800;
    color: #fff; background: rgba(18,23,33,.85);
    border: 2px solid #000; border-radius: 9px; padding: 4px 13px;
    animation: ru-in 0.45s 0.18s cubic-bezier(.2,1.7,.4,1) both;
}
@keyframes ru-in {
    from { opacity: 0; transform: scale(0.5) translateY(24px) rotate(-4deg); }
    to   { opacity: 1; transform: scale(1) translateY(0) rotate(0); }
}
@keyframes ru-glow {
    from { filter: drop-shadow(0 0 0 rgba(254,202,87,0)); }
    to   { filter: drop-shadow(0 0 18px rgba(254,202,87,.85)); }
}
/* the camo grade word lands only once the number has finished climbing */
.oc-grade { transition: opacity 0.25s ease; }
.oc-grade.pop { animation: ru-in 0.35s cubic-bezier(.2,1.7,.4,1) both; }

/* the card's own promotion chip — the banner is gone by the time this shows,
   so this is what the player actually reads */
.or-up {
    display: inline-block; margin-top: 7px;
    background: #ee5253; color: #fff;
    border: 3px solid #000; border-radius: 9px;
    padding: 4px 14px; font-weight: 900; letter-spacing: .5px;
    box-shadow: 3px 3px 0 #000;
    animation: ru-in .4s cubic-bezier(.2,1.7,.4,1) both;
}

/* ==========================================================================
   📱 TOUCH LAYOUT
   The palette used to be a floating box scaled to fit BESIDE everything else.
   On a landscape phone that meant a 361x190 window holding 704px of content —
   two thirds of the colours unreachable, and every control finger-hostile.
   It is a full-height drawer on the right now, at natural size, opened by the
   PAINT button. Nothing has to share that space, so nothing has to shrink.
   ========================================================================== */
body.touch #paintMenu {
    position: fixed; top: 0; right: 0; bottom: 0; left: auto;
    width: min(330px, 76vw);
    height: 100%;
    max-height: none !important;
    border-radius: 16px 0 0 16px;
    border-width: 0 0 0 4px;
    padding: 12px 14px calc(14px + env(safe-area-inset-bottom));
    gap: 10px;
    overflow-y: auto;
    z-index: 58;
    opacity: 1;
    transform: translateX(102%);          /* closed */
    transition: transform 0.22s ease;
    box-shadow: -6px 0 0 rgba(0,0,0,.45);
    pointer-events: auto;
}
body.touch #paintMenu.visible { transform: translateX(0); }
/* inside the drawer nothing is scaled, so the hit targets are the real ones */
body.touch #paintMenu .menu-tip { display: none; }

/* ---- the right-hand column: JUMP and HIT live in the thumb arc, the panel
   button sits above them so opening the drawer is never a mis-tap on JUMP --- */
#paint-btn, #zoom-out-btn, body.touch #magnifier-btn {
    position: fixed;
    right: 16px;
    left: auto;
    bottom: 150px;                         /* clears JUMP */
    top: auto;
    z-index: 57;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom right;
}
/* seekers keep HIT under it, so the panel button steps up a slot */
#paint-btn.above-hit, #zoom-out-btn.above-hit, body.touch #magnifier-btn.above-hit {
    bottom: 258px;
}
body.touch #magnifier-btn {
    width: 74px; height: 74px; border-radius: 50%;
    display: none; align-items: center; justify-content: center;
    background: rgba(30, 39, 46, 0.9); border: 3px solid #54a0ff;
    box-shadow: 4px 4px 0 #000;
}
body.touch #magnifier-btn.shown { display: flex; }
body.touch #magnifier-btn.active { background: #54a0ff; }
/* 🔒 on a phone it takes the slot ABOVE the panel button. 150px is PAINT's
   lane; a seeker's button steps up to 258 when HIT is under it, and a hider
   has no HIT, so that slot is free and LOCK can simply live there. */
body.touch #look-btn {
    /* 🧩 THE LEFT, above the joystick — not the right-hand column.

       On the right it sat between the emote button (anchored to the TOP at
       58px) and PAINT (anchored to the BOTTOM at 150px). A control wedged
       between a top-anchored and a bottom-anchored neighbour has a different
       gap on every screen height, and on a 400px-tall phone there was none:
       it drew underneath the emote button.

       Here it sits above the joystick and both are bottom-anchored, so the
       gap is the same on every phone. It also reads better than it sounds:
       pressing LOCK hides the joystick, so the thumb that was steering the
       character is already where it needs to be to steer the camera. */
    left: 16px; right: auto; bottom: 190px; top: auto;
    width: 74px; height: 74px; border-radius: 50%;
    z-index: 57;
    display: none; align-items: center; justify-content: center;
    flex-direction: column; gap: 1px; padding-top: 4px;
    background: rgba(30, 39, 46, 0.9); border: 3px solid #2ed573;
    box-shadow: 4px 4px 0 #000;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom left;
}
body.touch #look-btn.shown { display: flex; }
body.touch #look-btn.active { background: #2ed573; }
body.touch #look-btn svg { width: 24px; height: 24px; }
/* 🔒 while the camera is the player's, the thumb controls are not a thing
   they can use -- the same reason Statue takes them away. The canvas needs
   the whole screen for the drag, too. */
body.look-on #mobile-controls, body.look-on #joystick-base, body.look-on #jump-btn { display: none !important; }
/* a word under the glyph — "PEEK"/"PAINT" is clearer than an icon alone */
.rc-label {
    position: absolute; left: 50%; bottom: -4px; transform: translateX(-50%);
    font-size: 11px; font-weight: 900; letter-spacing: 1px; color: #fff;
    text-shadow: 2px 2px 0 #000; pointer-events: none;
}

/* the panel buttons are real tap targets, and DONE floats ABOVE the open
   drawer — it is the only way back out, so it cannot sit behind it */
body.touch #paint-btn, body.touch #zoom-out-btn {
    width: 74px; height: 74px;
}
body.touch #zoom-out-btn { z-index: 59; border-color: #2ed573; }
body.touch #paint-btn { border-color: #feca57; }

/* DONE steps out of the drawer's way — floating it on top of the palette put
   it straight over the swatches it is meant to let you pick from */
/* 🧩 JUMP IS UNDERNEATH THE OPEN DRAWER, and has been for as long as both
   have existed: the drawer takes 330px of the right edge and the button sits
   beneath it, so the tap has always gone to the palette. This changes nothing
   a player can do — it stops drawing a control that cannot be pressed, which
   is the same rule the Statue block follows for the rooted hider. DONE is the
   way out of the close-up, and it floats above the drawer on purpose. */
body.touch #paintMenu.visible ~ #mobile-controls #jump-btn { display: none; }

body.touch #paintMenu.visible ~ #zoom-out-btn,
body.touch #zoom-out-btn { right: calc(min(330px, 76vw) + 16px); }

/* the left panel has room for its caption again now that the palette moved
   off to the right — an unlabelled slider and a checkbox told nobody anything */
@media (max-height: 500px) {
    body.touch #zoom-panel .zp-title { display: block; font-size: 12px; }
    body.touch #zoom-panel { gap: 6px; padding: 9px 10px; }
    body.touch #zoom-panel #zoom-auto-row { font-size: 11px; }
}

/* 🧩 THE ZOOM PANEL SITS ABOVE THE JOYSTICK, anchored to the same edge.

   Its own rule puts it at top:50% — the middle of the screen — while the
   joystick is anchored to the bottom. Two different anchors means the gap
   between them is a function of viewport height, and on a 340px-tall phone
   (a 720p handset held sideways) it had closed to minus 16px. Sharing the
   bottom edge makes that gap a constant instead.

   The hider's LOCK button is on this same edge, and the two never appear
   together: the panel shows only inside the paint close-up, and canLook()
   excludes it. */
body.touch #zoom-panel {
    top: auto;
    bottom: 176px;
    transform: scale(max(0.9, var(--ui-scale)));
    transform-origin: bottom left;
}

/* 🧩 the shortest phones cannot have all three. Rank, zoom panel and
   joystick want 292px of a 340px column, so while the paint drawer is open
   the CLASS bar stands down and the ink bar stays — ink is what is being
   spent right now, and the class ladder is still there afterwards. */
@media (max-height: 420px) {
    body.touch.painting #ui-container .rank-container { display: none; }
    /* ...and the panel drops the last 8px into the gap the joystick leaves,
       which is 18px at this height. */
    body.touch #zoom-panel { bottom: 168px; }
}

/* icon over word, not word across icon */
body.touch #paint-btn, body.touch #zoom-out-btn, body.touch #magnifier-btn {
    flex-direction: column; gap: 1px; padding-top: 4px;
}
body.touch .rc-label { position: static; transform: none; bottom: auto; left: auto; }
body.touch #magnifier-btn svg, body.touch #paint-btn svg, body.touch #zoom-out-btn svg {
    width: 24px; height: 24px;
}

/* 🎯 the eyedropper, armed. It is a mode, so it has to look like one — and
   the whole canvas gets a crosshair so it is obvious what to do next. */
#cc-eyedrop.armed {
    background: #feca57; border-color: #feca57;
    animation: ed-pulse 1s ease-in-out infinite;
}
#cc-eyedrop.armed svg path { fill: #1a1408; stroke: #1a1408; }
body.eyedropping #gameCanvas { cursor: crosshair; }
@keyframes ed-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(254,202,87,.7); }
    50%      { box-shadow: 0 0 0 9px rgba(254,202,87,0); }
}
body.touch #cc-eyedrop { width: 44px; height: 44px; }

/* ---- seeker's secondary column ----
   Five controls do not fit in one right-hand column on a 360px-tall frame:
   measured, spider overlapped HIT at every size, and PEEK ran into the
   reaction button and the mute button on short ones. PEEK and Spider Sense
   move inboard into their own column instead of stacking higher. */
body.touch #magnifier-btn,
body.touch #magnifier-btn.above-hit {
    right: 150px; bottom: 50px; top: auto;
    width: 62px; height: 62px;
}
body.touch #spider-btn {
    position: fixed;
    right: 150px; bottom: 122px; top: auto;
    transform: scale(var(--ctl-scale));
    transform-origin: bottom right;
}

/* DONE goes to the top: at mid-height it sat right on the character you are
   trying to paint, which is the one thing that must stay visible */
body.touch #zoom-out-btn, body.touch #zoom-out-btn.above-hit {
    top: 16px; bottom: auto;
    transform-origin: top right;
}

/* DONE as the drawer's own header button — full width, first thing in it */
body.touch #zoom-out-btn.in-menu {
    position: static; top: auto; right: auto; bottom: auto;
    width: 100%; height: 46px;
    flex: none; flex-direction: row; gap: 8px;
    border-radius: 10px; border: 3px solid #000;
    background: #2ed573; transform: none;
    order: -2; margin-bottom: 2px;
}
body.touch #zoom-out-btn.in-menu .rc-label { font-size: 15px; letter-spacing: 2px; }
body.touch #zoom-out-btn.in-menu svg { width: 20px; height: 20px; }

/* 🤏 A 300px-TALL SCREEN HAS NO ROOM ABOVE HIT.

   Measured at 727x300, which is a 720p handset sideways with the browser
   bar still showing — the shape qa-rally has always used for the menu and
   that no suite had ever checked during a ROUND:

       corner strip (shop/home/settings)   ends at y44
       #hit-btn                            starts at y60

   Sixteen pixels, for a 44px button. There is no top value that fits, so
   every attempt to place #emote-wrap on the right edge at this height
   lands on one or the other — it was 42px into HIT on the live build and
   32px in on the themed one, and on a hider it was 7px into PAINT.

   So it stops sharing the edge and moves inboard to the column PEEK and
   Spider Sense use, which is empty at the top on both builds (PEEK sits
   at y97 themed, y196 live). Reactions are a cosmetic control — the one
   thing in that corner that can afford to move. */
@media (max-height: 320px) {
    /* ...and inboard is not far enough either. Measured at 727x300 the
       column PEEK uses has the match banner ending at y59 and PEEK
       starting at y97 — a 38px gap for a 44px button, so it simply
       does not fit anywhere on this screen.

       Reactions stand down instead. That is not a new idea here: the
       drawer-open rule a few lines below already hides this exact
       button whenever the paint drawer needs the room. A 300px-tall
       screen needs the room all the time, and of everything in that
       corner the cosmetic one is the one that can go. */
    body.touch #emote-wrap { display: none !important; }

    /* ...and the class bar goes with it, which is the same trade the
       two rules further down already make at 420px while painting, and
       that the theme makes at 360px: ink is what is being spent right
       now, the class ladder is still there after the round.

       Doing it unconditionally at 300px tall is what gets the corner
       panel out of its neighbours' way. Measured at 727x300 before:

           #look-btn 47..110   ×  #ui-container 8..64    17px
           #zoom-panel         ×  #ui-container           9px

       both of which are the panel standing 56px tall on a screen with
       300px to share between the rank, the zoom panel and the
       joystick. */
    body.touch #ui-container .rank-container { display: none; }

    /* ...and the zoom panel gives up its caption. It is wedged between
       the corner panel above and the joystick below, and at 300px tall
       there is no room to move it: bottom:168px is already tuned to the
       18px gap the joystick leaves, and dropping it further puts it on
       the joystick instead. So it gets shorter rather than moving. The
       caption is shown at 500px tall and under because there was room
       for it there; at 300 there is not. */
    body.touch #zoom-panel .zp-title { display: none; }
}

/* corner buttons stand down while the paint drawer is open */
body.touch.drawer-open #emote-wrap,
body.touch.drawer-open #mute-btn,
body.touch.drawer-open #home-btn { display: none !important; }

/* the paint phase is timed, so the countdown must survive the drawer opening.
   The banner is small once scaled, so re-centre it in the strip the drawer
   leaves rather than letting it disappear underneath. */
@media (orientation: landscape) {
    /* biased right of centre in the strip: dead-centre put it on the rank and
       ink panel in the top-left corner. Portrait has no strip worth using —
       the drawer takes 76vw there and the banner stays where it is. */
    body.touch.drawer-open #match-banner {
        left: calc((100vw - min(330px, 76vw)) * 0.72);
    }
}

/* 📬 map news on the main menu — the verdict on a map you sent in. It waits
   for you rather than arriving while you are mid-round, and it is the only
   place a player ever finds out. */
#map-news {
    position: fixed; left: 50%; top: 14px;
    transform: translateX(-50%);
    z-index: 118; display: none;
    width: min(430px, 92vw);
    background: #1b2a3a; color: #e6ebf2;
    border: 4px solid #000; border-radius: 14px;
    box-shadow: 6px 6px 0 #000;
    padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
    pointer-events: auto;
    max-height: 70vh; overflow-y: auto;
}
#map-news.visible { display: block; }
.mn-head {
    font-weight: 900; font-size: 15px; letter-spacing: .5px;
    color: #feca57; margin-bottom: 8px;
}
.mn-row {
    background: #223448; border: 2px solid #000; border-radius: 10px;
    padding: 8px 10px; margin-bottom: 8px;
}
.mn-row b { display: block; font-size: 14px; margin-bottom: 2px; }
.mn-row span { font-size: 12.5px; line-height: 1.4; color: #cfd8e6; }
.mn-live { color: #7ee59a; }
.mn-vote { color: #8ec3ff; }
.mn-back { color: #ffc978; }
#mn-ok {
    width: 100%; min-height: 44px;
    background: #feca57; color: #1a1408;
    border: 3px solid #000; border-radius: 10px;
    font-weight: 900; font-size: 15px; cursor: pointer;
    box-shadow: 3px 3px 0 #000;
}

/* 🌍 language picker, styled to match the audio sliders it sits with */
.audio-select {
    width: 100%; margin: 2px 0 10px; padding: 7px 9px;
    background: #12161f; color: #dfe6e9;
    border: 1px solid #2d3646; border-radius: 8px;
    font-family: inherit; font-size: 14px; cursor: pointer;
}
.audio-select:focus { outline: none; border-color: #54a0ff; }

/* 🎲 the mode announcement at the top of a round. Never takes a click — the
   player is painting underneath it. */
#mode-card {
    position: fixed; left: 50%;
    /* 🃏 clear of the match banner, which is centred too and runs to two
       lines once it carries the warm-up line. At 11% the card sat 14px into
       it — two centred panels converging is the same mistake the status
       readout kept making. Measured from the banner rather than from the
       viewport, so it holds at any height. */
    top: calc(15px + 4.6em);
    transform: translate(-50%, -14px);
    background: rgba(18, 22, 31, 0.92);
    border: 2px solid #54a0ff; border-radius: 14px;
    padding: 12px 22px; text-align: center;
    max-width: min(560px, 88vw);
    opacity: 0; pointer-events: none; z-index: 60;
    transition: opacity .35s ease, transform .35s ease;
}
#mode-card.show { opacity: 1; transform: translate(-50%, 0); }
/* 🃏 on a short phone the explainer and the centred '+10 HITS' offer collide.
   The card lasts 3.6 seconds; the offer waits for it. Only here — on a tall
   screen there is room for both and the offer should not lose three seconds
   for a problem that does not exist there. */
@media (max-height: 460px) {
    body.mode-card-up #seeker-hits-btn { display: none !important; }
}
#mode-card .mc-name {
    font-size: clamp(20px, 4.2vw, 30px); font-weight: bold;
    color: #54a0ff; letter-spacing: .5px;
}
#mode-card .mc-blurb { font-size: clamp(12px, 2.4vw, 15px); color: #dfe6e9; margin-top: 3px; }
#overlay-mode-btn { background: #2d3646; border-color: #54a0ff; }

/* 🏆 achievement celebration. Sits a little above centre so it never lands on
   the result card's headline, and never takes a tap. */
#ach-pop {
    position: fixed; left: 50%; top: 26%;
    transform: translate(-50%, 10px) scale(.92);
    background: linear-gradient(180deg, #1b2130, #12161f);
    border: 3px solid #feca57; border-radius: 18px;
    padding: 14px 30px; text-align: center;
    max-width: min(520px, 90vw);
    box-shadow: 0 10px 40px rgba(0,0,0,.55), 0 0 22px rgba(254,202,87,.28);
    opacity: 0; pointer-events: none; z-index: 70;
    transition: opacity .28s ease, transform .28s cubic-bezier(.2,1.4,.5,1);
}
#ach-pop.show { opacity: 1; transform: translate(-50%, 0) scale(1); }
#ach-pop .ap-kicker {
    font-size: clamp(10px, 2.2vw, 13px); font-weight: 900; letter-spacing: 3px;
    text-transform: uppercase; color: #feca57;
}
#ach-pop .ap-name {
    font-size: clamp(22px, 5vw, 34px); font-weight: bold; color: #fff; margin: 2px 0 1px;
}
#ach-pop .ap-reward { font-size: clamp(13px, 2.6vw, 17px); color: #dfe6e9; }

/* 🚪 lobby browser — same sheet look as the mode picker */
/* 95, with the other sheets that open FROM the menu. At 62 it opened UNDER
   #main-menu (80): the room list drew behind the rank ladder and the button
   column, which reads as the panel being broken rather than badly stacked.
   Same fault the player card had at 63. */
#lobby-screen { position: fixed; inset: 0; z-index: 95; display: none;
    align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#lobby-screen.visible { display: flex; }
#lobby-screen .sheet-card {
    background: #12161f; border: 2px solid #2d3646; border-radius: 16px;
    width: min(460px, 92vw); max-height: 82vh; display: flex; flex-direction: column; padding: 12px; }
#lobby-screen .sheet-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
#lobby-screen .sheet-title { font-size: 18px; font-weight: bold; color: #dfe6e9; flex: 1; }
#lobby-list { overflow-y: auto; flex: 1; min-height: 90px; }
.lobby-empty { color: #8395a7; font-size: 13px; text-align: center; padding: 22px 8px; }
.lobby-row { display: flex; align-items: center; gap: 10px; padding: 9px 10px;
    border: 1px solid #2d3646; border-radius: 10px; margin-bottom: 7px; background: #171d28; }
.lobby-row.locked { opacity: .58; }
.lb-main { flex: 1; min-width: 0; }
.lb-name { font-size: 15px; font-weight: bold; color: #dfe6e9; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-sub { font-size: 12px; color: #8395a7; }
.lb-n { font-size: 13px; color: #feca57; font-variant-numeric: tabular-nums; flex: none; }
.lb-go { flex: none; }
.lobby-join { display: flex; gap: 8px; margin-top: 8px; }
.lobby-join input { flex: 1; min-width: 0; padding: 8px 10px; border-radius: 8px;
    border: 1px solid #2d3646; background: #0d1017; color: #dfe6e9; font-family: inherit; font-size: 14px; }

/* 🎨 menu backdrop panel */
#menubg-screen { position: fixed; inset: 0; z-index: 95; display: none;
    align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#menubg-screen.visible { display: flex; }
#menubg-screen .sheet-card {
    background: #12161f; border: 2px solid #2d3646; border-radius: 16px;
    width: min(420px, 92vw); padding: 12px;
    max-height: 92vh; overflow-y: auto; box-sizing: border-box; }
#menubg-screen .sheet-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
#menubg-screen .sheet-title { font-size: 18px; font-weight: bold; color: #dfe6e9; flex: 1; }
.mbg-note { font-size: 12.5px; color: #8395a7; margin: 0 0 9px; }
#menubg-preview { width: 100%; height: auto; display: block; border-radius: 10px; box-sizing: border-box;
    border: 1px solid #2d3646; background: #0d1017; image-rendering: pixelated; }
.mbg-acts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.mbg-acts .me-btn { flex: 1 1 auto; }

/* 🪜 the invite ladder */
#invite-screen { position: fixed; inset: 0; z-index: 96; display: none; align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#invite-screen.visible { display: flex; }
.invite-card { width: min(460px, 94vw); max-height: 92vh; overflow-y: auto; text-align: center; }
.inv-sub { font-size: 13px; color: #8395a7; margin: 0 0 10px; letter-spacing: 1px; text-transform: uppercase; }
.inv-track { position: relative; height: 26px; border-radius: 13px; background: #0d1017; border: 2px solid #2d3646; overflow: hidden; margin-bottom: 4px; }
.inv-fill { height: 100%; width: 0; background: linear-gradient(90deg, #2ed573, #7bed9f); transition: width .3s; }
.inv-num { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: bold; color: #dfe6e9; text-shadow: 0 1px 2px #000; }
.inv-wait { font-size: 12px; color: #feca57; margin: 4px 0 0; }
.inv-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin: 10px 0; }
.inv-cell { position: relative; background: #0d1017; border: 2px solid #2d3646; border-radius: 10px; padding: 4px 2px 5px; }
.inv-cell.big { border-color: #feca57; }
.inv-cell.got { border-color: #2ed573; opacity: .9; }
.inv-n { position: absolute; top: 2px; left: 5px; font-size: 10px; font-weight: bold; color: #8395a7; }
.inv-art { height: 40px; display: flex; align-items: center; justify-content: center; margin-top: 8px; }
.inv-art canvas { width: 36px; height: 36px; }
.inv-art svg { width: 30px; height: 30px; }
.inv-art .coin-ico { width: 24px; height: 24px; }
.inv-lab { font-size: 10.5px; font-weight: bold; color: #feca57; line-height: 1.1; margin-top: 2px; overflow-wrap: anywhere; }
.inv-cell.big .inv-lab { color: #ff9f43; }
.inv-tick { position: absolute; top: 2px; right: 4px; font-size: 12px; color: #2ed573; font-weight: bold; }

/* 🤝 THE FRIEND LIST, in the same card as the ladder.
   ⚠️ Every rule here that sets a `color` sets a `background` too, even where
   the parent's would have done: deploy/recolour.js decides whether a block
   is dark-on-light or light-on-dark from THAT BLOCK's own declarations, so a
   rule with a colour and no ground gets read as text on paper and darkened
   — which is how fifty labels went dark-on-dark the first time the theme
   was built. Naming the ground is what keeps the pair legible. */
.fr-tabs { justify-content: center; margin: 0 0 10px; }
.fr-mine { display: flex; align-items: center; gap: 8px; justify-content: center; flex-wrap: wrap;
    background: #0d1017; border: 2px solid #2d3646; border-radius: 10px; padding: 8px 10px; }
.fr-lab { font-size: 11px; font-weight: 900; color: #8395a7; text-transform: uppercase; letter-spacing: .5px; background: transparent; }
.fr-code { font-family: ui-monospace, Consolas, monospace; font-size: 20px; font-weight: 900; letter-spacing: 3px;
    color: #feca57; background: transparent; }
.fr-add { display: flex; gap: 6px; margin: 10px 0 8px; }
.fr-input {
    flex: 1 1 auto; min-width: 0; font-family: ui-monospace, Consolas, monospace; font-size: 17px;
    font-weight: bold; letter-spacing: 3px; text-align: center; text-transform: uppercase;
    color: #fff; background: #0d1017; border: 2px solid #2d3646; border-radius: 9px; padding: 8px 6px;
}
.fr-input::placeholder { color: #576574; letter-spacing: 2px; font-size: 14px; }
.fr-reqs:not(:empty) { margin-bottom: 8px; }
.fr-list { display: flex; flex-direction: column; gap: 6px; text-align: left; }
.fr-head { font-size: 11px; font-weight: 900; color: #8395a7; background: transparent;
    text-transform: uppercase; letter-spacing: .5px; margin-top: 8px; }
.fr-row { display: flex; align-items: center; gap: 7px;
    background: #0d1017; border: 2px solid #2d3646; border-radius: 10px; padding: 6px 8px; }
/* 🟡 a request is the one row that wants looking at */
.fr-row.fr-req { background: #0d1017; border-color: #feca57; }
/* 🟢 the dot is the whole online/offline signal — it is read before the
   words are, so it is never the only one: the tag says it as well. */
.fr-dot { width: 9px; height: 9px; flex: none; border-radius: 50%; background: #576574; }
.fr-row.on .fr-dot { background: #2ed573; }
.fr-name { flex: 1 1 auto; min-width: 0; font-size: 14px; font-weight: bold; color: #fff;
    background: transparent; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.fr-tag { font-size: 11px; color: #8395a7; background: transparent; white-space: nowrap; }
.fr-row.on .fr-tag { color: #7bed9f; }
.fr-row .me-btn { flex: none; padding: 6px 9px; font-size: 12px; }
.fr-join { background: #2ed573; color: #10141c; }
.fr-join:hover { background: #7bed9f; }
.fr-yes { background: #2ed573; color: #10141c; }
.fr-gift { background: #2f3640; color: #feca57; }
.fr-x { background: #2f3640; color: #8395a7; }
.fr-x.armed { background: #ff6b6b; color: #10141c; }

/* 🎖️ the lifetime level badge on the menu */
#level-badge { pointer-events: auto; background: rgba(13,16,23,.72); border: 1px solid #2d3646; border-radius: 10px;
    padding: 6px 9px; margin-bottom: 6px; max-width: 220px; }
.lvb-top { display: flex; align-items: baseline; gap: 6px; }
.lvb-n { color: #feca57; font-size: 14px; }
.lvb-pts { font-size: 11px; color: #8395a7; margin-left: auto; white-space: nowrap; }
.lvb-track { height: 5px; border-radius: 3px; background: #0d1017; border: 1px solid #2d3646; overflow: hidden; margin-top: 4px; }
.lvb-track i { display: block; height: 100%; background: linear-gradient(90deg, #feca57, #ff9f43); }
.lvb-next { font-size: 10.5px; color: #8395a7; margin-top: 4px; }
.mode-row.mode-locked { opacity: .6; }
.mode-row .mode-lock { font-style: normal; color: #feca57; font-size: 12px; }

/* 🤝 invite a friend, on the result card */
#overlay-invite { display: block; margin: 8px auto 0; padding: 9px 14px; border-radius: 12px; border: 2px solid #feca57; background: #1b2130; color: #feca57;
    font: inherit; font-size: 14px; font-weight: bold; cursor: pointer; max-width: 420px; }
#overlay-invite:hover { background: #242c3d; }

/* ✨ a flavour item's preview on its shop card */
.si-fxprev { width: 84px; height: 56px; display: block; margin: 0 auto; }

/* 🎁 gifts */
#present-screen { position: fixed; inset: 0; z-index: 96; display: none; align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#present-screen.visible { display: flex; }
.present-card { width: min(460px, 94vw); max-height: 90vh; overflow-y: auto; text-align: left; }
.present-h { font-size: 13px; letter-spacing: 1px; text-transform: uppercase; color: #8395a7; margin: 6px 0 8px; }
.present-who { display: flex; align-items: center; gap: 10px; width: 100%; box-sizing: border-box; padding: 9px 12px; margin-bottom: 6px; border-radius: 10px;
    border: 1px solid #2d3646; background: #0d1017; color: #dfe6e9; font: inherit; font-size: 14px; cursor: pointer; text-align: left; }
.present-who:hover { border-color: #54a0ff; }
.present-who .pw-dot { width: 9px; height: 9px; border-radius: 50%; background: #57606f; flex: none; }
.present-who.online .pw-dot { background: #2ed573; box-shadow: 0 0 6px #2ed573; }
.present-who .pw-name { flex: 1; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.present-who .pw-tag { font-size: 12px; color: #feca57; white-space: nowrap; }
.present-back { margin-bottom: 8px; }
.present-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
.present-item { background: #0d1017; border: 2px solid #2d3646; border-radius: 12px; padding: 8px 4px; color: #dfe6e9; font: inherit; cursor: pointer; text-align: center; }
.present-item:hover { border-color: #54a0ff; }
.present-item.armed { border-color: #2ed573; box-shadow: 0 0 0 2px rgba(46,213,115,.25); }
.present-item .pi-art { height: 56px; display: flex; align-items: center; justify-content: center; }
.present-item .pi-art canvas { width: 52px; height: 52px; }
.present-item .pi-art svg { width: 44px; height: 44px; }
.present-item .pi-dot { width: 36px; height: 36px; border-radius: 50%; border: 2px solid #000; display: inline-block; }
.present-item .pi-name { font-size: 12px; font-weight: bold; margin-top: 4px; min-height: 2.2em; line-height: 1.1; display: flex; align-items: center; justify-content: center; }
.present-item .pi-price { font-size: 12px; color: #feca57; display: flex; align-items: center; justify-content: center; gap: 4px; }
.present-item .pi-price .coin-ico { width: 14px; height: 14px; }
.present-item .pi-price s { color: #8395a7; }
.inbox-giftback { margin-top: 6px; }

/* 🔁 the return ladder — on the round card, in the menu pop, in the quests */
.ladder { margin: 10px auto 6px; max-width: 420px; }
.ladder-head { font-size: 17px; font-weight: bold; color: #feca57; text-align: center; margin-bottom: 8px; }
.ladder-row { display: flex; gap: 8px; justify-content: center; }
.ladder-tile { flex: 1 1 0; min-width: 0; background: #0d1017; border: 2px solid #2d3646; border-radius: 12px; padding: 8px 4px; text-align: center; }
.ladder-tile.today { border-color: #feca57; box-shadow: 0 0 0 2px rgba(254,202,87,.25); }
.ladder-tile.done { border-color: #2ed573; opacity: .85; }
.ladder-tile.locked { opacity: .75; }
.ladder-day { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; color: #8395a7; }
.ladder-art { height: 64px; display: flex; align-items: center; justify-content: center; margin: 4px 0; }
.ladder-art canvas { width: 60px; height: 60px; }
.ladder-coin { width: 34px; height: 34px; }
.ladder-name { font-size: 13px; font-weight: bold; color: #dfe6e9; line-height: 1.15; min-height: 2.3em; display: flex; align-items: center; justify-content: center; overflow-wrap: anywhere; }
.ladder-coins { font-size: 12px; color: #feca57; }
.ladder-when { font-size: 11px; color: #8395a7; margin-top: 4px; }
.ladder-tile.today .ladder-when { color: #feca57; font-weight: bold; }
.ladder-tile.done .ladder-when { color: #2ed573; }
.ladder-note { font-size: 12.5px; color: #8395a7; text-align: center; margin-top: 8px; }
#ladder-screen { position: fixed; inset: 0; z-index: 96; display: none; align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#ladder-screen.visible { display: flex; }
#ladder-screen .ladder { margin-bottom: 12px; }
.q-pill.ladder-pill { border-color: #feca57; }
.q-pill.ladder-pill.locked { opacity: .8; }
.ladder-quest { cursor: pointer; }
.ladder-quest .ach-sub { font-size: 12px; color: #feca57; }
.ladder-right canvas { width: 40px; height: 40px; margin-right: 6px; vertical-align: middle; }

/* 👤 account panel */
#account-screen { position: fixed; inset: 0; z-index: 95; display: none;
    align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#account-screen.visible { display: flex; }
#account-screen .sheet-card {
    background: #12161f; border: 2px solid #2d3646; border-radius: 16px;
    width: min(420px, 92vw); padding: 12px;
    max-height: 92vh; overflow-y: auto; box-sizing: border-box; }
#account-screen .sheet-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
#account-screen .sheet-title { font-size: 18px; font-weight: bold; color: #dfe6e9; flex: 1; }
#account-status { font-size: 14px; color: #dfe6e9; margin: 0 0 10px; }
.acct-link { border-top: 1px solid #2d3646; margin-top: 12px; padding-top: 10px; }
.acct-code-text { font-family: ui-monospace, Consolas, monospace; font-size: 34px; letter-spacing: 4px; text-align: center;
    color: #2ed573; background: #0d1017; border: 1px solid #2d3646; border-radius: 10px; padding: 12px 8px; margin: 10px 0 6px; user-select: all; }
.acct-enter { display: flex; gap: 8px; margin-top: 10px; }
.acct-enter input { flex: 1; min-width: 0; font-family: ui-monospace, Consolas, monospace; font-size: 22px; letter-spacing: 3px;
    text-transform: uppercase; text-align: center; padding: 8px; border-radius: 10px; border: 1px solid #2d3646; background: #0d1017; color: #dfe6e9; }

/* 🖌️ body paint editor — with the other sheets that open from the menu */
#cpaint-screen { position: fixed; inset: 0; z-index: 95; display: none;
    align-items: center; justify-content: center; background: rgba(6,9,14,.72); }
#cpaint-screen.visible { display: flex; }
#cpaint-screen .sheet-card {
    background: #12161f; border: 2px solid #2d3646; border-radius: 16px;
    width: min(420px, 92vw); padding: 12px;
    max-height: 92vh; overflow-y: auto; box-sizing: border-box; }
#cpaint-screen .sheet-head { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
#cpaint-screen .sheet-title { font-size: 18px; font-weight: bold; color: #dfe6e9; flex: 1; }
#cpaint-canvas {
    /* shrinks on short screens so the pose row, swatches and Save stay on screen */
    display: block; width: auto; max-width: 100%; height: auto; aspect-ratio: 6 / 7;
    max-height: min(420px, calc(92vh - 310px)); margin: 0 auto;
    background: #0d1017; border: 2px solid #2d3646; border-radius: 10px; box-sizing: border-box;
    touch-action: none; cursor: crosshair; }
#cpaint-swatches { display: flex; flex-wrap: wrap; gap: 7px; justify-content: center; margin: 10px 0 2px; }
#cpaint-poses { display: flex; gap: 6px; justify-content: center; margin: 8px 0 0; }
.cp-pose { width: 52px; height: 56px; padding: 0; background: #1e2533; border: 2px solid #3a4560; border-radius: 9px; cursor: pointer; }
.cp-pose canvas { display: block; width: 52px; height: 56px; }
.cp-pose.on { border-color: #feca57; background: #2a3347; }
/* a phone held sideways has no height to stack canvas, poses, swatches and
   Save — so the canvas takes the left and everything else the right */
@media (max-height: 560px) {
    #cpaint-screen .sheet-card {
        width: min(96vw, 840px); max-height: 96vh; padding: 8px 12px;
        display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 14px;
        grid-template-areas: "head head" "note note" "canvas poses" "canvas swatches" "canvas acts";
        align-content: start; }
    #cpaint-screen .sheet-head { grid-area: head; margin-bottom: 2px; }
    #cpaint-screen .mbg-note { grid-area: note; margin: 0 0 6px; }
    #cpaint-canvas { grid-area: canvas; align-self: start; max-height: calc(96vh - 96px); }
    #cpaint-poses { grid-area: poses; flex-wrap: wrap; margin: 0; align-self: end; }
    #cpaint-swatches { grid-area: swatches; margin: 8px 0; }
    #cpaint-screen .mbg-acts { grid-area: acts; align-self: start; }
}

.cp-sw { width: 30px; height: 30px; border-radius: 50%; border: 2px solid #000; cursor: pointer; padding: 0; }
.cp-sw.on { outline: 3px solid #fff; outline-offset: 1px; }
.cp-sw.locked { opacity: .55; font-size: 13px; line-height: 26px; text-align: center; color: #fff; text-shadow: 0 0 3px #000; }
.cp-more { height: 30px; padding: 0 10px; border-radius: 15px; border: 1px solid #feca57; background: #12161f; color: #feca57; font: inherit; font-size: 12px; font-weight: bold; cursor: pointer; white-space: nowrap; }

/* 🪪 player card */
/* ABOVE EVERY OTHER SHEET. This was 63 while the mode, quest and gift screens
   sit at 95-130, so any of those covered the card outright if one was open.
   Only the sitelock at 500 is allowed to win, because that one exists to stop
   the game rather than to sit beside it. */
#card-screen { position: fixed; inset: 0; z-index: 140; display: none;
    align-items: center; justify-content: center; background: rgba(6,9,14,.78); }
#card-screen.visible { display: flex; }
#card-screen .sheet-card {
    background: #12161f; border: 2px solid #2d3646; border-radius: 16px;
    width: min(940px, 94vw); padding: 12px;
    /* THE CARD HAS TO FIT THE WINDOW. Unconstrained, a 900x500 canvas at 94vw
       of a 1064px window renders ~555px tall — taller than the window on its
       own — so the header and the Save button were pushed clean outside the
       viewport. What was left visible was a full-bleed canvas with no chrome,
       which reads exactly like the card rendering in the background with no
       download button. */
    max-height: 92vh;
    display: flex; flex-direction: column; box-sizing: border-box; }
#card-screen .sheet-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex: none; }
#card-screen .sheet-title { font-size: 18px; font-weight: bold; color: #dfe6e9; flex: 1; }
/* width and height both auto with both maxima set: the canvas shrinks inside
   whatever room is left and keeps its own 9:5 aspect without being told */
#card-canvas {
    width: auto; height: auto;
    max-width: 100%;
    max-height: calc(92vh - 120px);
    margin: 0 auto; display: block; border-radius: 10px; flex: 0 1 auto; }
#card-screen .mbg-acts { flex: none; }

/* 🚫 RETIRED: the menu's own "Log in with CrazyGames" button. The element is
   gone from index.html — signing in is their platform's prompt to make, not
   ours, and a player already signed in gets their name from CG.user.getUser()
   without it. This rule stays because a browser holding a cached index.html
   would otherwise paint the old button over the menu with its styling intact.
   The three #cg-login-btn blocks above are dead and can go once no cached
   copy of the old page can plausibly still be out there. */
#cg-login-btn { display: none !important; }

/* 🗺️ MAP PICKER — built-in maps first, community maps ten at a time.
   Same card furniture as the private-room modal so the two read as one
   flow: the picker is opened from it and closes back into it. */
#mappick-screen {
    position: fixed;
    inset: 0;
    z-index: 96;            /* above #private-screen — it opens on top of it */
    display: none;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.7);
    backdrop-filter: blur(3px);
}
#mappick-screen.visible { display: flex; }
.mp-card {
    width: min(560px, 94vw);
    zoom: max(0.85, var(--card-scale));
    max-height: calc(94vh / max(0.85, var(--card-scale)));
    overflow-y: auto;
    background: #2d3446;
    border: 2px solid #576574;
    border-radius: 18px;
    padding: 18px 22px 22px 22px;
    box-sizing: border-box;
}
/* auto-fill, so the grid reflows to one or two columns on a phone rather
   than forcing a horizontal scroll */
.mp-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 10px;
    margin: 4px 0 14px 0;
}
.mp-tile {
    position: relative;
    border: 2px solid #576574;
    border-radius: 12px;
    overflow: hidden;
    cursor: pointer;
    background: #1e2433;
    padding: 0;
    aspect-ratio: 16 / 10;
    max-width: 100%;
}
.mp-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* a community map is a painted GRID, rasterised one pixel per cell, so it
   arrives as a small canvas (160x90-ish) that the tile scales up. Nearest
   neighbour on purpose — the cells ARE the art, the same reasoning as
   pmapToCanvas in game.js. */
.mp-tile canvas {
    width: 100%; height: 100%; object-fit: cover; display: block;
    image-rendering: pixelated;
}
.mp-tile .mp-name {
    position: absolute; left: 0; right: 0; bottom: 0;
    background: rgba(0, 0, 0, 0.62);
    color: #fff; font-size: 11px; font-weight: bold;
    padding: 3px 5px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mp-tile.sel { border-color: #feca57; box-shadow: 0 0 0 2px rgba(254, 202, 87, 0.35); }
.mp-tile.mp-random { display: flex; align-items: center; justify-content: center; }
.mp-tile.mp-random span { color: #c8d6e5; font-size: 13px; font-weight: bold; }
.mp-more-row { display: flex; align-items: center; justify-content: center; gap: 10px; min-height: 34px; }
/* the spinner the community pages load behind */
.mp-spinner {
    width: 22px; height: 22px;
    border: 3px solid rgba(200, 214, 229, 0.25);
    border-top-color: #54a0ff;
    border-radius: 50%;
    animation: mp-spin 0.8s linear infinite;
}
@keyframes mp-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .mp-spinner { animation-duration: 2.4s; } }

/* 🎛️ the private host's between-rounds controls, on the end card. Three
   buttons (Mode, Map, Twists), wrapping on a narrow frame like everything
   else in this panel. 90px, not 120: at 120 a narrow panel put each of the
   three on a row of its own and the card scrolled; at 90 it is two rows at
   worst, the same height the two buttons used to take. */
#mv-host-row { display: flex; gap: 8px; flex-wrap: wrap; margin: 8px 0 4px 0; }
#mv-host-row button { flex: 1 1 90px; font-size: 12px; padding: 7px 10px; }

/* 🏆 LIVE SCOREBOARD, held open with TAB. Sits over the match without
   stopping it — the round keeps running underneath, so it is deliberately
   translucent and never takes pointer events away from the game. */
#live-board {
    position: fixed; inset: 0; z-index: 70;
    display: none; align-items: center; justify-content: center;
    pointer-events: none;                 /* the match keeps playing under it */
    background: rgba(0, 0, 0, 0.45);
}
#live-board.visible { display: flex; }
.lb-card {
    width: min(460px, 92vw);
    max-height: 78vh; overflow-y: auto;
    background: rgba(45, 52, 70, 0.96);
    border: 2px solid #576574; border-radius: 16px;
    padding: 14px 16px;
    box-sizing: border-box;
}
.lb-title { font-size: 15px; font-weight: bold; color: #feca57; text-align: center; margin-bottom: 10px; }
.lb-row {
    display: flex; align-items: center; gap: 8px;
    font-size: 13px; color: #c8d6e5;
    padding: 4px 6px; border-radius: 8px;
}
.lb-row + .lb-row { margin-top: 2px; }
.lb-row.me { background: rgba(84, 160, 255, 0.18); color: #fff; }
.lb-row.out { opacity: 0.5; }
.lb-role { width: 18px; text-align: center; flex: 0 0 auto; }
.lb-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* digits line up in a column, which is the entire point of a scoreboard */
.lb-score { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: bold; color: #fff; }
.lb-head { font-size: 11px; color: #8fa5bd; text-transform: uppercase; letter-spacing: 0.06em; margin: 8px 0 4px 6px; }
.lb-hint { text-align: center; font-size: 11px; color: #8fa5bd; margin-top: 10px; }

/* 🛒 the in-game shop button, sitting beside Home in the HUD corner. Same
   furniture as #home-btn so the row reads as one control cluster. */
#ingame-shop-btn {
    position: fixed;
    top: 12px;
    right: 116px;
    z-index: 60;
    width: 42px; height: 42px;
    background: rgba(30, 39, 46, 0.85);
    border: 2px solid #576574;
    border-radius: 50%;
    cursor: pointer;
    padding: 0;
    align-items: center; justify-content: center;
    backdrop-filter: blur(4px);
    transform: scale(var(--ctl-scale));
    transform-origin: top right;
}
#ingame-shop-btn:hover { border-color: #c8d6e5; }
@media (max-width: 720px) { #ingame-shop-btn { right: 100px; } }

/* 🗺️ the lobby row's map thumbnail and its open/running badge */
.lb-map {
    width: 54px; height: 34px; flex: 0 0 auto;
    object-fit: cover; border-radius: 6px;
    border: 1px solid #576574; background: #1e2433;
}
.lb-map-cm { display: flex; align-items: center; justify-content: center; font-size: 16px; }
.lb-mapname { opacity: 0.85; }
.lb-badge {
    font-size: 10px; font-weight: bold; text-transform: uppercase;
    letter-spacing: 0.05em; padding: 1px 6px; border-radius: 6px; margin-left: 6px;
    vertical-align: middle;
}
.lb-badge.open { background: rgba(46, 204, 113, 0.22); color: #6ee7a0; }
.lb-badge.run  { background: rgba(232, 163, 61, 0.22); color: #f0b75f; }
.lb-badge.shut { background: rgba(120, 132, 148, 0.22); color: #9fb0c2; }

/* 🏆 past championships, one block per weekend */
.ch-past { border: 1px solid #3d4759; border-radius: 10px; padding: 8px 10px; margin-bottom: 8px; }
.ch-past-head { display: flex; align-items: baseline; gap: 8px; font-size: 13px; margin-bottom: 4px; }
.ch-past-when { color: #8fa5bd; font-size: 11px; }
.ch-past-n { margin-left: auto; color: #8fa5bd; font-size: 11px; }
.ch-past-row { display: flex; font-size: 13px; color: #c8d6e5; padding: 1px 0; }
.ch-past-w { margin-left: auto; font-variant-numeric: tabular-nums; color: #feca57; font-weight: bold; }

/* ============ 🛒 THE SHOP'S FITTING ROOM ============
   The card used to be sized by its contents: four Hider Perks made a small
   window, forty hats made a tall one, and switching tabs moved the close
   button out from under the thumb reaching for it. It is one size now, and
   the shelf scrolls inside it.

   The stage on the left holds the player's own character wearing whatever
   they tapped. It is the first honest answer the shop has ever given to
   "what does this look like on me" — an icon on a card cannot say it.
   Below ~760px there is no room for both, and the shelf wins: on a phone
   the stage would take the half of the screen the items need. */
.shop-card { width: min(1100px, 96vw); height: min(92vh, 880px); }
.shop-body { display: flex; gap: 16px; flex: 1; min-height: 0; }
.shop-stage {
    width: 250px; flex: none;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: rgba(13, 16, 23, 0.45);
    border: 2px solid #2d3646; border-radius: 14px;
}
#shop-me { width: 100%; height: auto; flex: 1; min-height: 0; object-fit: contain; }
.shop-try-note { font-size: 12px; color: #8395a7; text-align: center; padding: 0 12px 12px; line-height: 1.35; }
.shop-item.si-tryable { cursor: pointer; }
.shop-item.trying { border-color: #feca57; box-shadow: 0 0 0 2px rgba(254, 202, 87, 0.3); }
@media (max-width: 760px) { .shop-stage { display: none; } }

/* 🏆 the championship row in the lobby browser. Gold edge and a crown: it is
   the one room in the list where a win is worth something, and the list is
   scanned rather than read. */
.lobby-row.champ { border-left-color: #feca57 !important; background: rgba(254,202,87,0.07); }
.lobby-row.champ .lb-name { color: #feca57; }

/* 🏆 the championship door: the first line of the panel, and the only thing
   on that card a player can act on. Laid out as a row so the call, the
   detail and the PLAY read in one pass instead of stacking. */
.champ-door { display: flex; align-items: center; gap: 10px; text-align: left; flex-wrap: wrap; }
.champ-door b { font-size: 17px; }
.champ-door .cd-sub { flex: 1 1 auto; font-size: 12px; font-weight: normal; opacity: 0.85; }
.champ-door .cd-go { font-size: 16px; font-weight: bold; letter-spacing: 1px; }

/* 📦 WHAT'S NEW — the update card. It borrows .ach-card/.gift-card for
   the frame and the button, so only the three things that are its own
   are here: the headline, the list of lines, and the coin row. */
#release-screen {
    position: fixed; inset: 0; z-index: 96;
    display: none; align-items: center; justify-content: center;
    background: rgba(6, 9, 14, .72); backdrop-filter: blur(3px);
}
#release-screen.visible { display: flex; }
.release-card { max-width: 420px; }
.release-headline {
    font-size: 1.15rem; font-weight: 800; text-align: center;
    margin: 2px 0 10px; text-wrap: balance;
}
.release-lines {
    margin: 0 0 12px; padding: 0 0 0 1.1em;
    text-align: left; line-height: 1.45; font-size: .95rem;
}
.release-lines li { margin: 0 0 6px; }
.release-coins {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    font-weight: 800; font-size: 1.1rem; margin: 0 0 12px;
}

/* ---- 📐 the result card on one screen (2026-10-17) ----
   The structure only; the theme sizes it (theme-rally.css PASS 43).
   Coins and the ad that doubles them share a row, the camo score and the
   map rating share the ballot's top row, and the result sentence is gone
   (an empty #overlay-text takes no room). */
#overlay-text:empty { display: none; }
#overlay-coin-row { display: flex; align-items: center; justify-content: center; gap: 14px; flex-wrap: wrap; margin: 0 0 10px; }
#overlay-coin-row #overlay-coins, #overlay-coin-row #double-coins-btn { margin: 0; }
#mv-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex: none; }
/* nothing in it: no row, and no stray rule under nothing */
#mv-top:not(:has(> #overlay-camo:not([style*="none"]), > #map-like:not([style*="none"]))) { display: none; }

/* 🎲 THE ROUND'S TWIST. Same place and manner as the mode card, which it
   follows when both are due (showTwistCard waits for it). Coloured by the
   side it helps: hider blue, seeker red — the banner counts' own colours.
   Never takes a click. */
#twist-card {
    position: fixed; left: 50%;
    top: calc(15px + 4.6em);
    transform: translate(-50%, -14px);
    background: rgba(18, 22, 31, 0.92);
    border: 2px solid #feca57; border-radius: 14px;
    padding: 9px 22px 12px; text-align: center;
    max-width: min(560px, 88vw);
    opacity: 0; pointer-events: none; z-index: 60;
    transition: opacity .35s ease, transform .35s ease;
}
#twist-card.show { opacity: 1; transform: translate(-50%, 0); }
#twist-card .tc-tag {
    display: inline-block; padding: 2px 10px; border-radius: 9px;
    font-size: 11px; font-weight: bold; letter-spacing: .8px; text-transform: uppercase;
    color: #fff; background: #3867d6;
}
#twist-card.seeker .tc-tag { background: #e84118; }
#twist-card .tc-name { font-size: clamp(19px, 4vw, 28px); font-weight: bold; color: #feca57; margin-top: 4px; }
#twist-card .tc-blurb { font-size: clamp(12px, 2.4vw, 15px); color: #dfe6e9; margin-top: 3px; }

/* 🔫 the hider's twist weapon (Fight Back's slap, the Stun Blaster). On a
   desktop it sits bottom-centre, where nothing else lives during a round —
   bottom-right is the palette's. On a phone it takes the spider's inboard
   slot: Spider Sense is a seeker's and this is a hider's, so they never meet. */
#zap-btn {
    position: fixed; left: 50%; bottom: 22px;
    width: 62px; height: 62px; display: none;
    align-items: center; justify-content: center;
    font-size: 24px; font-family: inherit; font-weight: 900;
    background: #2f3640; color: #fff;
    border: 3px solid #000; border-radius: 50%;
    box-shadow: 3px 3px 0 rgba(0,0,0,0.4);
    transform: translateX(-50%) scale(var(--ctl-scale)); transform-origin: bottom center;
    z-index: 18; cursor: pointer; pointer-events: auto;
}
#zap-btn.ready { background: #e1b12c; animation: zap-pulse 1.1s ease-in-out infinite; }
@keyframes zap-pulse {
    0%, 100% { box-shadow: 3px 3px 0 rgba(0,0,0,0.4), 0 0 0 0 rgba(254,202,87,0.7); }
    50% { box-shadow: 3px 3px 0 rgba(0,0,0,0.4), 0 0 0 12px rgba(254,202,87,0); }
}
body.touch #zap-btn {
    left: auto; right: 150px; bottom: 122px;
    transform: scale(var(--ctl-scale)); transform-origin: bottom right;
}

/* 🦸 the skills shelf in the seeker tab: own any, one equipped */
.shop-item.skill-on { box-shadow: 0 0 0 3px #2ed573 inset; }
