html, body {
    margin: 0;
    height: 100%;
    background: #101014;
    color: #e8e8ee;
    font-family: system-ui, sans-serif;
}

.top-bar {
    display: flex; align-items: center; gap: 1.2rem;
    padding: 0.45rem 1rem; background: #16161c; border-bottom: 1px solid #2c2c38;
}
.top-bar .brand { color: #f5b942; font-weight: 800; letter-spacing: 0.15em; text-decoration: none; }
.top-bar .top-link { color: #9a9aa8; text-decoration: none; font-size: 0.9rem; }
.top-bar .top-link:hover { color: #e8e8ee; }
.leave-button {
    background: none; color: #9a9aa8; border: 1px solid #2c2c38; border-radius: 4px;
    padding: 0.25rem 0.6rem; cursor: pointer; font-size: 0.8rem; margin-left: 0.5rem;
}
.leave-button:hover { color: #ef6a5a; border-color: #ef6a5a; }

/* The shell MEASURES the top bar rather than assuming it. `min-height:
   calc(100vh - 2.6rem)` was right only while the bar stayed on one line: at
   390px the brand wraps and the bar is 59px, so the main area was 17px taller
   than the room left for it and a landing page that fits grew a scrollbar
   anyway. A column here has no number in it to go stale. */
#app { min-height: 100%; display: flex; flex-direction: column; }
.app-main { flex: 1 0 auto; display: flex; align-items: center; justify-content: center; }

/* Vertically centred, deliberately: the panels are short, and pinning them to
   the top left the bottom two-thirds of a 1080p screen empty. Centred they
   frame the middle of the backdrop instead of abandoning it. */
.app-main:has(.home) {
    padding: 2rem 0;
    box-sizing: border-box;
}

/* Landing page: two panels hugging the edges, not one column down the middle
   (#199). The intro backdrop's subject is centre frame and so was the menu;
   they competed and the menu won by being opaque. The gap between the panels
   is now where the video is actually visible. */
.home {
    display: grid;
    grid-template-columns: 21rem minmax(20rem, 28rem);
    grid-template-areas: "join lobbies" "footer footer";
    /* space-between, not centre: the backdrop's subject is centre frame, so the
       panels go to the edges and the gap between them is where the video is
       actually seen. Centring them just made a wider version of the block the
       issue was about. */
    justify-content: space-between;
    align-items: stretch;
    gap: 1.5rem 3.5rem;
    width: min(100% - 4rem, 88rem);
    margin: 0 auto;
}
.home-join { grid-area: join; }
.lobby-list { grid-area: lobbies; }

/* One line in the join panel, whatever the name is. nowrap is deliberate — a
   logotype that wraps reads as a mistake — but nowrap plus a fixed size is how
   ATOMIC GRID came to be drawn as ATOMIC GRI: one character longer than the
   name this was tuned for, and 41px past the panel's edge. The size follows
   the panel now (container query), capped so it never grows past the original
   2.1rem, and a longer name shrinks to fit instead of losing its last letter. */
.home-join { container-type: inline-size; }
.home h1 {
    font-size: min(2.1rem, 10.2cqw);
    letter-spacing: 0.22em; margin: 0 0 0.4rem; white-space: nowrap;
}

@media (max-width: 460px) {
    /* nowrap keeps the logotype on one line where there is room; on a phone
       there is not, and an overflowing title is worse than a smaller one. */
    .home h1 { font-size: min(1.5rem, 10.2cqw); letter-spacing: 0.2em; }
}
/* Scoped to .home on purpose: the GAME page uses .tagline too, for its
   spectator count, and restyling it globally shifted the lobby panel 4.5px and
   broke the add-bot-stays-put test. */
.tagline { color: #9a9aa8; }
.home .tagline { margin: 0; font-size: 0.9rem; }

/* Each panel carries its own backing, which is what lets the video run
   brighter than the old flat dim over the whole page. */
.home-panel {
    background: rgba(10, 10, 14, 0.72);
    border: 1px solid #2c2c38;
    border-radius: 6px;
    padding: 1.25rem 1.4rem;
}

/* Its own row, left-aligned under the join panel — centred between two panels
   of different heights it just looked adrift. */
.build-footer { grid-area: footer; text-align: left !important; }
.play-link { color: #f5b942; font-weight: 600; text-decoration: none; }
.play-link:hover { text-decoration: underline; }

.game-stage { flex: 1; min-width: 0; }
/* Shrink-wraps the canvas so every absolute overlay (HUD strip, screens,
   banners, fps, touch pads) tracks the CANVAS edges, not the wider stage. */
.canvas-wrap {
    position: relative; margin: 0 auto;
    width: 100%;
    max-width: min(100%, calc((100vh - 6rem) * 1.3333));
}
.canvas-wrap canvas {
    display: block; image-rendering: pixelated; border: 2px solid #2c2c38;
    /* Height follows from the intrinsic aspect ratio; pixelated keeps it crisp. */
    width: 100%;
    height: auto;
}

/* ATOMIC GRID decals (#220): the arena frames itself in its own language —
   hazard-stripe brackets on two corners and a dim conduit glow under the
   canvas. Pure CSS, deliberately dimmer than anything the renderer draws. */
.canvas-wrap::before, .canvas-wrap::after {
    content: ""; position: absolute; width: 3.2rem; height: 0.5rem;
    background: repeating-linear-gradient(45deg,
        #b98a2e 0 0.4rem, #23252d 0.4rem 0.8rem);
    opacity: 0.85; pointer-events: none; z-index: 1;
}
.canvas-wrap::before { top: -0.5rem; left: -2px; }
.canvas-wrap::after { bottom: -0.5rem; right: -2px; }
.canvas-wrap canvas { box-shadow: 0 0.35rem 0 -0.15rem rgba(30, 160, 172, 0.35); }

/* On-screen controls: coarse pointers (touch) or anything phone-sized. */
.touch-controls {
    display: none;
    position: absolute; left: 0; right: 0; bottom: -7.5rem;
    justify-content: space-between; align-items: center;
    padding: 0 0.5rem;
    -webkit-touch-callout: none; user-select: none; touch-action: none;
}
@media (pointer: coarse), (max-width: 700px) {
    .touch-controls { display: flex; }
}
.touch-dpad {
    display: grid; gap: 0.25rem;
    grid-template-areas: ". up ." "left . right" ". down .";
}
.tc-up { grid-area: up; }
.tc-down { grid-area: down; }
.tc-left { grid-area: left; }
.tc-right { grid-area: right; }
.touch-actions { display: flex; gap: 0.75rem; }
.touch-btn {
    width: 3.4rem; height: 3.4rem;
    background: #1a1a22; color: #e8e8ee; border: 1px solid #2c2c38; border-radius: 10px;
    font-size: 1.3rem; touch-action: none;
}
.touch-btn:active { background: #2c2c38; }
.tc-bomb, .tc-action { border-radius: 50%; width: 4rem; height: 4rem; }
/* Attract mode: the lobby's self-playing demo match. Dimmed and desaturated
   so it reads as a backdrop rather than as your game — the canvas is the same
   one a live round draws to. */
.attract-veil {
    position: absolute; inset: 0; pointer-events: none;
    background: linear-gradient(rgba(10, 10, 14, 0.45), rgba(10, 10, 14, 0.6));
    backdrop-filter: saturate(0.75);
    display: flex; align-items: flex-end; justify-content: center;
    padding-bottom: 3%;
}
.attract-note {
    font: 600 clamp(9px, 1.2vw, 14px)/1.4 monospace;
    letter-spacing: 0.14em; color: #9a9aa8; text-shadow: 0 2px 4px #000;
    background: rgba(10, 10, 14, 0.55); border: 1px solid #2c2c38; border-radius: 4px;
    padding: 0.15rem 0.6rem;
}
.attract-note kbd {
    background: #1a1a22; border: 1px solid #2c2c38; border-radius: 3px;
    padding: 0 0.25rem; color: #e8e8ee;
}

.fps-overlay {
    position: absolute; top: 6px; right: 8px;
    font: 12px/1.4 monospace; color: #7ef58a; text-shadow: 0 0 4px #000;
    pointer-events: none;
}
/* In-game HUD in the canvas's top strip (66/480 ≈ 14 % tall):
   map name | round clock | per-player score chips. */
.hud-strip {
    position: absolute; top: 2%; left: 2%; right: 2%;
    display: flex; align-items: center; justify-content: space-between; gap: 0.8rem;
    pointer-events: none; text-shadow: 0 2px 4px #000;
}
.hud-map {
    flex: 1 1 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
    font: 600 clamp(10px, 1.4vw, 18px)/1.2 monospace; color: #e8e8ee; text-align: left;
}
.game-clock {
    font: 700 clamp(14px, 2.4vw, 30px)/1 monospace; letter-spacing: 0.08em;
    color: #f5b942; min-width: 5ch; text-align: center;
}
.game-clock.urgent { color: #ef6a5a; animation: clock-pulse 1s steps(2) infinite; }
@keyframes clock-pulse { 50% { opacity: 0.45; } }
.hud-score { flex: 1 1 0; display: flex; gap: 0.35rem; justify-content: flex-end; }
.hud-chip {
    display: inline-flex; align-items: center; gap: 0.25rem;
    padding: 0.05rem 0.35rem; border: 1px solid; border-radius: 4px;
    background: rgba(10, 10, 14, 0.55);
    font: 700 clamp(10px, 1.4vw, 18px)/1.3 monospace; color: #e8e8ee;
}
.hud-chip .swatch { width: 0.6em; height: 0.6em; }

#blazor-error-ui { display: none; position: fixed; bottom: 0; width: 100%; background: #5c2626; padding: 0.5rem 1rem; }
#blazor-error-ui.visible { display: block; }

.loading-progress { display: block; width: 8rem; height: 8rem; margin: 20vh auto 1rem; }
.loading-progress circle { fill: none; stroke: #2c2c38; stroke-width: 0.6rem; transform-origin: 50% 50%; }
.loading-progress circle:last-child {
    stroke: #f5b942;
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transform: rotate(-90deg);
}
.loading-progress-text { text-align: center; font-weight: 600; }
.loading-progress-text::after { content: var(--blazor-load-percentage-text, "Loading"); }

/* Pinned to the top, unlike every other page: the lobby side panel GROWS — a
   roster row per bot, a note when the map wants teams — and centred content
   re-centres by half of whatever it just gained. That is how the add-bot button
   still slid 5px up under the cursor on the first click, which is the exact
   complaint moving it above the roster was meant to answer. It also means a
   full roster overflows downwards, where you can scroll to it, instead of off
   the top of the viewport. */
.app-main:has(.game-layout) { align-items: flex-start; }
.game-layout { display: flex; gap: 1.5rem; align-items: flex-start; width: 100%; max-width: 100rem; padding: 0 1rem; }
.game-side { min-width: 12rem; flex-shrink: 0; }
@media (max-width: 900px) {
    .game-layout { flex-direction: column; align-items: stretch; }
    .game-stage { margin-bottom: 8rem; } /* room for the touch overlay */
}
.game-side h2 { letter-spacing: 0.25em; color: #f5b942; }
.roster { list-style: none; padding: 0; }
.roster li { display: flex; align-items: center; gap: 0.5rem; margin: 0.3rem 0; }
.swatch { width: 0.9rem; height: 0.9rem; display: inline-block; border-radius: 2px; }
.start-button, .join-form button {
    background: #f5b942; color: #101014; border: 0; border-radius: 4px;
    padding: 0.5rem 1rem; font-weight: 700; cursor: pointer;
}
.start-button:hover, .join-form button:hover { background: #ffd066; }
.banner {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    background: rgba(10, 10, 14, 0.65); font-size: 2rem; font-weight: 800; letter-spacing: 0.1em;
}
.join-form { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 1.25rem; }
.lobby-list h3 { letter-spacing: 0.25em; color: #f5b942; margin: 0 0 0.75rem; }
.lobby-empty { color: #9a9aa8; font-size: 0.9rem; margin: 0; }

/* A dashboard that is one sentence tall when quiet does not look like a
   dashboard. This keeps it the same shape whether or not anyone is playing. */
.lobby-list { display: flex; flex-direction: column; min-height: 13rem; }
.lobby-names { display: block; color: #e8e8ee; }
.lobby-meta { display: block; font-size: 0.8rem; color: #9a9aa8; }
.lobby-row {
    display: flex; align-items: center; gap: 0.7rem;
    background: #1a1a22; border: 1px solid #2c2c38; border-radius: 6px;
    padding: 0.5rem 0.8rem; margin: 0.4rem 0;
}
.lobby-code { font-weight: 800; letter-spacing: 0.2em; color: #f5b942; }
.lobby-info { flex: 1; color: #9a9aa8; font-size: 0.9rem; }
.lobby-row button {
    background: #f5b942; color: #101014; border: 0; border-radius: 4px;
    padding: 0.3rem 0.8rem; font-weight: 700; cursor: pointer;
}
.lobby-row button:hover { background: #ffd066; }
.join-form input {
    background: #1a1a22; color: #e8e8ee; border: 1px solid #2c2c38; border-radius: 4px; padding: 0.5rem;
}
.join-row { display: flex; gap: 0.5rem; }
.code-input { width: 6rem; text-transform: uppercase; letter-spacing: 0.3em; }
.create-button { background: #2c2c38 !important; color: #e8e8ee !important; }
.error { color: #ef6a5a; }

/* Sign in / register (#148): one quiet row under the join form, collapsed to
   a single button until someone wants it and gone entirely when the server
   has no accounts. Same palette and shapes as the join form so the panel
   reads as part of the page rather than a modal over it. */
.auth-panel { margin-top: 0.75rem; }
.playing-as { color: #9a9aa8; margin: 0; font-size: 0.95rem; }
.playing-as strong { color: #f5b942; }
.auth-row { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; }
.auth-user { color: #e8e8ee; font-weight: 600; }
.auth-toggle {
    background: #2c2c38; color: #9a9aa8; border: 0; border-radius: 4px;
    padding: 0.35rem 0.8rem; font-size: 0.85rem; cursor: pointer;
}
.auth-toggle:hover { background: #3a3a48; color: #e8e8ee; }
.auth-form { display: flex; flex-direction: column; gap: 0.6rem; }
.auth-form input {
    background: #1a1a22; color: #e8e8ee; border: 1px solid #2c2c38;
    border-radius: 4px; padding: 0.5rem;
}
.auth-modes { display: flex; gap: 0.5rem; }
.auth-modes button {
    flex: 1; background: none; color: #9a9aa8; border: 1px solid #2c2c38;
    border-radius: 4px; padding: 0.3rem 0; font-weight: 600; cursor: pointer;
}
.auth-modes button.on { color: #f5b942; border-color: #f5b942; }
.auth-submit {
    background: #f5b942; color: #101014; border: 0; border-radius: 4px;
    padding: 0.5rem 1rem; font-weight: 700; cursor: pointer;
}
.auth-submit:hover { background: #ffd066; }
.auth-signout {
    background: #2c2c38; color: #e8e8ee; border: 0; border-radius: 4px;
    padding: 0.3rem 0.8rem; font-weight: 600; cursor: pointer;
}
.auth-signout:hover { background: #3a3a48; }

.mute-button { background: #2c2c38; color: #e8e8ee; border: 0; border-radius: 4px; padding: 0.4rem 0.8rem; margin-top: 0.6rem; cursor: pointer; }

/* Full-screen result screens (round scoreboard / draw / match victory): the
   640×480 screen art fills the stage, the readable panel floats on top. */
.screen {
    position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    background-color: rgba(10, 10, 14, 0.9); background-size: cover; background-position: center;
    image-rendering: pixelated; text-align: center;
}
.screen-panel {
    background: rgba(10, 10, 14, 0.78); border: 1px solid #2c2c38; border-radius: 8px;
    padding: 1.2rem 2rem; max-width: 85%;
}
.screen-title { font-size: 1.8rem; font-weight: 800; letter-spacing: 0.05em; color: #f5b942; }
.screen-sub { color: #9a9aa8; margin: 0.2rem 0 0.8rem; }
.scoreboard { margin: 0 auto 0.8rem; border-collapse: collapse; }
.scoreboard td { padding: 0.15rem 0.7rem; text-align: left; font-weight: 600; text-shadow: 0 1px 2px #000; }
.scoreboard .kills { color: #9a9aa8; font-weight: 400; }
.countdown { font-size: 1.4rem; font-weight: 800; color: #7ef58a; margin-bottom: 0.8rem; }
.victory-title { font-size: 2.2rem; font-weight: 800; margin-bottom: 1rem; color: #f5b942; }
.roster-name { flex: 1; }
.wins { font-weight: 700; color: #f5b942; min-width: 1.5rem; text-align: right; }
.bot-remove {
    background: none; color: #9a9aa8; border: 1px solid #2c2c38; border-radius: 4px;
    padding: 0 0.35rem; cursor: pointer; font-size: 0.75rem; line-height: 1.3;
}
.bot-remove:hover { color: #d8342c; border-color: #d8342c; }
.invite-fallback {
    width: 100%; margin-top: 0.3rem; padding: 0.35rem;
    background: #1a1a22; color: #e8e8ee; border: 1px solid #2c2c38; border-radius: 4px;
    font: 0.8rem/1.3 monospace;
}
.qr-canvas {
    display: block; margin-top: 0.5rem; width: 8rem; height: 8rem;
    image-rendering: pixelated; border-radius: 4px;
}
.bind-player { color: #e8e8ee; font-weight: 600; margin-top: 0.5rem; }
.bind-presets button:hover { background: #3c3c4a; }
.settings { display: flex; flex-direction: column; gap: 0.6rem; margin: 0.8rem 0; }
.settings label { display: flex; flex-direction: column; gap: 0.2rem; font-size: 0.85rem; color: #9a9aa8; }
.settings select { background: #1a1a22; color: #e8e8ee; border: 1px solid #2c2c38; border-radius: 4px; padding: 0.35rem; }

/* The 4-direction head icon (hud/heads): 40x40 cells, east cell shown. */
.hud-head {
    width: 20px;
    height: 20px;
    display: inline-block;
    background-size: 400% 100%;
    background-position: 0 0;
    image-rendering: pixelated;
}

/* The flashing HURRY!! warning before the arena starts closing. */
.hurry-flash {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
    animation: hurry-blink 0.3s steps(1) 10;
}

.hurry-img {
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    image-rendering: pixelated;
}

.hurry-text {
    font-size: 4rem;
    font-weight: 900;
    color: #ff2020;
    text-shadow: 3px 3px 0 #000;
    letter-spacing: 0.1em;
}

@keyframes hurry-blink {
    0% { opacity: 1; }
    50% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .hurry-flash { animation: none; }
}

/* Gold Bomber roulette (#102): the elliptical prize wheel (center 320,240,
   radii 200x150 on the 640×480 screen) over the wheel's backdrop art. */
.roulette {
    position: relative;
    width: min(440px, 90%);
    aspect-ratio: 4 / 3;
    margin: 0.6rem auto;
    background-size: cover;
    background-position: center;
    border-radius: 8px;
    border: 2px solid #c9a227;
    image-rendering: pixelated;
    overflow: hidden;
}

.roulette-icon {
    position: absolute;
    width: 40px;
    height: 36px;
    transform: translate(-50%, -50%);
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    image-rendering: pixelated;
    opacity: 0.5;
    filter: grayscale(0.4);
    transition: transform 60ms linear;
    animation: var(--sprite-anim, none);
}

.roulette-icon.lit {
    opacity: 1;
    filter: none;
    transform: translate(-50%, -50%) scale(1.35);
}

.roulette-icon.landed {
    opacity: 1;
    filter: drop-shadow(0 0 8px #ffd94a);
    transform: translate(-50%, -50%) scale(1.6);
    /* the medallion keeps turning while it twinkles — two animations, one
       stepping background-position-x, one the glow (5 s total) */
    animation: var(--sprite-anim, none), roulette-twinkle 0.5s steps(2) 10;
}

.roulette-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 0.3rem 0.5rem;
    background: rgba(0, 0, 0, 0.65);
    color: #ffd94a;
    font-weight: 700;
    text-align: center;
    font-size: 0.95rem;
}

.roulette-label.brake {
    color: #ff5050;
}

/* One frame of a sprite strip per step. --sprite-span overshoots 100% by a
   frame because background-position-x spans edge to edge, not frame to frame;
   see Game.razor.cs PowIconStyle. */
@keyframes sprite-cycle {
    from { background-position-x: 0%; }
    to { background-position-x: var(--sprite-span, 0%); }
}

@keyframes roulette-twinkle {
    0% { filter: drop-shadow(0 0 8px #ffd94a); }
    50% { filter: drop-shadow(0 0 2px #ffd94a); }
}

.roulette-toggle {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.9rem;
    cursor: pointer;
    user-select: none;
}

@media (prefers-reduced-motion: reduce) {
    .roulette-icon, .roulette-icon.landed, .loadout-icon { animation: none !important; }
}

/* Loadout HUD (v5): what your bomber currently carries — icons + counts. */
.loadout-stack {
    position: absolute;
    left: 8px;
    bottom: 8px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    pointer-events: none;
}

.loadout-bar {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 3px 8px;
    background: rgba(0, 0, 0, 0.55);
    border: 1px solid;
    border-radius: 6px;
    width: fit-content;
}

.loadout-item {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    color: #ffd94a;
    font-size: 0.8rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.loadout-icon {
    display: inline-block;
    width: 24px;
    height: 22px;
    background-size: contain;
    background-repeat: no-repeat;
    background-position: center;
    image-rendering: pixelated;
    animation: var(--sprite-anim, none);
}

/* Lobby color picker (v5): the 10-color table, taken ones locked. */
.color-picker {
    display: flex;
    gap: 4px;
    margin: 0.35rem 0;
    flex-wrap: wrap;
}

.color-swatch {
    width: 22px;
    height: 22px;
    border: 2px solid rgba(255, 255, 255, 0.25);
    border-radius: 4px;
    cursor: pointer;
    padding: 0;
}

.color-swatch.mine {
    border-color: #ffd94a;
    box-shadow: 0 0 6px #ffd94a;
}

.color-swatch.taken {
    opacity: 0.25;
    cursor: not-allowed;
}

/* 🐛 report: optional one-line context box under the button. */
.bug-note {
    display: flex;
    gap: 4px;
    margin: 0.25rem 0;
}

.bug-note input {
    flex: 1;
    min-width: 0;
    font-size: 0.85rem;
    padding: 0.25rem 0.4rem;
}

/* Build version badge (#171). Quiet by default — it is reference information,
   not a call to action — until the server has moved on without this browser,
   which with auto-deploy on every merge happens often enough to matter. */
.build-badge {
    display: inline-block;
    font-size: 0.72rem;
    letter-spacing: 0.04em;
    color: #7a7a7a;
    font-variant-numeric: tabular-nums;
}

.build-footer {
    margin-top: 1.5rem;
    text-align: center;
}

.build-reload {
    font: inherit;
    cursor: pointer;
    color: #1a1a1a;
    background: #ffd23f;
    border: 2px solid #000;
    border-radius: 3px;
    padding: 0.15rem 0.5rem;
    box-shadow: 1px 1px 0 #000;
}

.build-reload:hover { background: #ffe07a; }

.bug-versions {
    display: block;
    margin-top: 0.35rem;
    font-size: 0.7rem;
    color: #7a7a7a;
    font-variant-numeric: tabular-nums;
}

.bug-versions.stale { color: #ffd23f; }

/* The title art behind the landing page. It replaced the intro video
   (and that video's own mute/volume/pause controls — audio lives in Options
   now). Fixed and dimmed so the panels stay readable over it; z-index 0 with
   the panels at 1, never -1, or it disappears behind the page background. */
.home-backdrop {
    position: fixed; inset: 0; z-index: 0;
    /* contain, not cover: the art is a LOGOTYPE on a 4:3 plate, and cover
       crops whatever does not fit the viewport's aspect — on a wide screen
       that is the top and bottom, which is exactly where the lettering is. */
    background-position: center; background-repeat: no-repeat; background-size: contain;
    opacity: 0.55; pointer-events: none; image-rendering: pixelated;
}

@media (max-width: 900px) {
    /* One column below this: two panels plus a gap no longer fit, and the
       layout must not assume a backdrop it may not have.
       grid-template-areas MUST be restated — leaving the two-column version in
       place while declaring one column silently stacked the panels ON TOP of
       each other, which is what the phone screenshot showed. */
    .home {
        grid-template-columns: minmax(0, 30rem);
        grid-template-areas: "join" "lobbies" "footer";
        justify-content: center;
    }
}

@media (max-width: 640px) {
    /* The backdrop is a still image now — cheap enough to keep on a phone,
       but dimmer, because the panels lose their own backing here. */
    .home-backdrop { opacity: 0.3; }
    .home-panel { background: none; border: none; padding: 0; }
    /* Same container cap as the desktop rule — a flat size here is what
       put ATOMIC GRID 2.5px over the edge on a 375px phone. */
    .home h1 { font-size: min(2.2rem, 10.2cqw); }
}

/* The menu sits above the intro backdrop (#166). Without this the backdrop's
   z-index: 0 would let it paint over static content. */
.top-bar,
.home {
    position: relative;
    z-index: 1;
}

/* A join that has genuinely failed (#175): state it once, and offer the exit. */
.join-failed {
    color: #ff8b6a;
    margin-bottom: 0.5rem;
}

/* Which side a player is on, shown only in team play (#195). */
.team-badge {
    font-size: 0.8rem;
    margin-left: 0.25rem;
}

/* Which seats joined under an account (#151) — roster and both scoreboards.
   Deliberately quieter than the name it marks: dimmed and desaturated so a
   full lobby of badges reads as texture rather than ten alerts, while still
   answering "is this seat a real player" at a glance. */
.account-badge {
    font-size: 0.72rem;
    margin-left: 0.25rem;
    opacity: 0.8;
    filter: grayscale(0.4);
}

/* Options screen (#186): every setting in one place, reachable without a lobby. */
.options {
    max-width: 40rem;
    margin: 0 auto;
    padding: 1rem;
    position: relative;
    z-index: 1;
}

.options h1 {
    font-size: 1.8rem;
    letter-spacing: 0.25em;
}

.options section {
    border: 2px solid #2a2a34;
    border-radius: 4px;
    padding: 0.75rem 1rem;
    margin-bottom: 1rem;
    background: rgba(16, 16, 20, 0.85);
}

.options section header {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.options h2 { font-size: 1rem; margin: 0.25rem 0 0.75rem; }

.options .hint { color: #9a9aa8; font-size: 0.85rem; }

/* The label column is fixed rather than sized to content because every row is
   its own grid — they line up only because the number is the same in each.
   7.5rem fits the widest slider label ("Announcer", 6.75rem measured). */
.option-row {
    display: grid;
    grid-template-columns: 7.5rem 1fr 3rem;
    align-items: center;
    gap: 0.5rem;
    margin: 0.4rem 0;
}

/* A checkbox row is not a slider row and should never have been on the same
   grid. It has no slider and no readout, so the middle column stranded its box
   in the centre of the panel; and "Headphone placement" needs 12.3rem, which
   at 7.5rem wrapped to three lines, orphaned the headphone emoji on a line of
   its own, and cost 44px of a modal already 55px too tall. Widening every row
   to fit it would have taken that 5rem out of the sliders for nothing. Sized
   to content, the label gets its one line and the sliders keep their width. */
.option-row.option-switch {
    /* minmax, not auto: the floor lines the boxes up with each other the way
       the sliders line up, and the ceiling lets a longer label than today's
       take the width it needs instead of wrapping again. */
    grid-template-columns: minmax(12.5rem, auto) auto;
    justify-content: start;
    gap: 0.75rem;
}

.option-row output { color: #9a9aa8; font-variant-numeric: tabular-nums; }

.option-sections { display: grid; gap: 0 1rem; align-items: start; }

.binding-grid {
    display: grid;
    grid-template-columns: repeat(2, 5rem 1fr);
    gap: 0.4rem 0.6rem;
    align-items: center;
}

.binding-label { color: #9a9aa8; }

.binding {
    min-width: 3.5rem;
    font: inherit;
    cursor: pointer;
    background: #1c1c24;
    color: #e8e8ee;
    border: 2px solid #000;
    border-radius: 3px;
    padding: 0.2rem 0.4rem;
}

.binding.listening { background: #ffd23f; color: #1a1a1a; }

.preset-row { display: flex; gap: 0.5rem; margin-top: 0.75rem; }

.preset-row button,
.options .reset,
.options .back {
    font: inherit;
    cursor: pointer;
    background: #1c1c24;
    color: #e8e8ee;
    border: 2px solid #000;
    border-radius: 3px;
    padding: 0.25rem 0.6rem;
}

.options .reset { font-size: 0.8rem; color: #9a9aa8; }
.options .back { margin-top: 0.5rem; }

/* Lobby panel polish.
   `.settings label` sets flex-direction: column for the select rows, and being
   the more specific selector it was also stacking every checkbox above its own
   text. The toggles are rows. */
.settings .roulette-toggle {
    flex-direction: row;
    align-items: center;
    gap: 0.45rem;
    color: #e8e8ee;
}

.settings .roulette-toggle input[type="checkbox"] {
    margin: 0;
    flex: 0 0 auto;
}

/* The "this map is built as two sides" nudge (#208). Indented under the toggle
   it belongs to, and quiet enough not to read as a warning — it is telling you
   about the map, not about a mistake. */
.settings-note {
    margin: -0.25rem 0 0 1.6rem;
    font-size: 0.78rem;
    line-height: 1.35;
    color: #9a9aa8;
}

/* Sits above the roster, so adding a bot cannot walk it away from the cursor. */
.add-bot { width: 100%; margin-bottom: 0.4rem; }


/* Options open over the page rather than replacing it (a route dropped you out
   of the lobby you were in). */
.modal-scrim {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.65);
    z-index: 50;
}

.modal {
    position: fixed;
    inset: 50% auto auto 50%;
    transform: translate(-50%, -50%);
    max-width: 96vw;
    max-height: 88vh;
    overflow-y: auto;
    z-index: 51;
    background: #14141a;
    border: 2px solid #2a2a34;
    border-radius: 6px;
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.6);
}

/* Two columns once there is room. The panel is a 40rem strip on screens three
   times that wide, and three stacked sections do not fit 88vh on a laptop. */
@media (min-width: 58rem) {
    .options { max-width: 52rem; }
    .option-sections { grid-template-columns: 1fr 1fr; }
}

/* And three where a desktop window allows: with the Graphics section the set
   is four sections, and two columns of two stacks past 88vh at 768px tall. */
@media (min-width: 80rem) {
    .options { max-width: 76rem; }
    .option-sections { grid-template-columns: 1fr 1fr 1fr; }
}

.top-link-button {
    font: inherit;
    background: none;
    border: 0;
    cursor: pointer;
}


/* ---- ATOMIC GRID site decals (#220) ------------------------------------
   Substation Nine's border language, quoted quietly: panels carry a teal
   conduit top edge with one magenta node; the title wears the palette. */
.home-panel, .lobby-list {
    border-top: 2px solid rgba(30, 160, 172, 0.55);
    position: relative;
}
.home-panel::before, .lobby-list::before {
    content: ""; position: absolute; top: -4px; right: 0.9rem;
    width: 6px; height: 6px; background: #d640a0;
    box-shadow: 0 0 6px 1px rgba(214, 64, 148, 0.6);
}
.home h1 {
    background: linear-gradient(90deg, #40dceb 0 70%, #e05ac0 92%);
    -webkit-background-clip: text; background-clip: text; color: transparent;
}

/* ---- Chiptune composer (/composer) -------------------------------------- */

.app-main:has(.composer) { padding: 2rem 0; box-sizing: border-box; align-items: flex-start; }

.composer {
    display: grid;
    grid-template-columns: minmax(24rem, 1fr) 24rem;
    gap: 1.5rem;
    width: min(100% - 4rem, 80rem);
    margin: 0 auto;
}
@media (max-width: 900px) {
    .composer { grid-template-columns: 1fr; width: min(100% - 2rem, 80rem); }
}

.composer h2 { margin: 0 0 0.3rem; letter-spacing: 0.12em; }
.composer h3 { margin: 0.2rem 0 0.4rem; color: #f5b942; }

.composer-source {
    width: 100%; box-sizing: border-box; min-height: 18rem; resize: vertical;
    margin-top: 0.9rem; padding: 0.7rem 0.8rem;
    background: #0d0d12; color: #e8e8ee; caret-color: #f5b942;
    border: 1px solid #2c2c38; border-radius: 4px;
    font-family: ui-monospace, monospace; font-size: 0.85rem; line-height: 1.5;
    white-space: pre; overflow-x: auto;
}
.composer-source:focus { outline: none; border-color: #f5b942; }

.composer-controls {
    display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap;
    margin-top: 0.7rem;
}
.composer-controls button {
    background: #1c1c24; color: #e8e8ee; border: 1px solid #2c2c38;
    border-radius: 4px; padding: 0.4rem 0.9rem; cursor: pointer; font-size: 0.9rem;
}
.composer-controls button:hover { border-color: #f5b942; color: #f5b942; }
.composer-controls .composer-play { background: #f5b942; color: #101014; font-weight: 700; }
.composer-controls .composer-play:hover { background: #ffd063; color: #101014; }
.composer-controls select {
    background: #1c1c24; color: #e8e8ee; border: 1px solid #2c2c38;
    border-radius: 4px; padding: 0.4rem 0.5rem; font-size: 0.9rem;
}
.composer-loop { color: #9a9aa8; font-size: 0.9rem; display: flex; align-items: center; gap: 0.3rem; }

.composer-stats { color: #9a9aa8; font-size: 0.85rem; margin: 0.6rem 0 0; }

.composer-help { font-size: 0.88rem; }
.composer-help p { margin: 0.3rem 0; color: #c8c8d2; }
.composer-help ul { margin: 0.3rem 0 0.9rem; padding-left: 1.1rem; color: #c8c8d2; }
.composer-help li { margin: 0.2rem 0; }
.composer-help code {
    background: #0d0d12; border: 1px solid #2c2c38; border-radius: 3px;
    padding: 0.05rem 0.3rem; font-size: 0.82rem; color: #40dceb;
}

/* ---- Career pages: /leaderboards + /profile (#154) -----------------------
   One column of house panels — the same .home-panel the landing and composer
   use, so the decals, backing and borders come for free. */

.app-main:has(.career) { padding: 2rem 0; box-sizing: border-box; align-items: flex-start; }

.career {
    display: grid; gap: 1.2rem;
    width: min(100% - 4rem, 56rem);
    margin: 0 auto;
}
@media (max-width: 700px) {
    .career { width: min(100% - 1.6rem, 56rem); }
}

.career h1 { font-size: 1.7rem; letter-spacing: 0.14em; margin: 0 0 0.3rem; }
.career h3 { color: #f5b942; letter-spacing: 0.12em; margin: 0 0 0.7rem; font-size: 1rem; }
.career .tagline { margin: 0; font-size: 0.9rem; }
.career .tagline strong { color: #f5b942; }
.career-note { color: #9a9aa8; font-size: 0.92rem; }

/* Stat tabs: the auth panel's mode buttons, widened to eight of them. */
.board-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0.9rem 0 0.9rem; }
.board-tabs button {
    background: none; color: #9a9aa8; border: 1px solid #2c2c38; border-radius: 4px;
    padding: 0.28rem 0.65rem; font-size: 0.85rem; cursor: pointer;
}
.board-tabs button:hover { color: #e8e8ee; border-color: #3a3a48; }
.board-tabs button.on { color: #f5b942; border-color: #f5b942; }
.board-tabs button:disabled { opacity: 0.55; cursor: default; }

/* The one table style both career pages share. Right-hand columns are numbers
   and say so with tabular figures so ranks and values line up down the page. */
.stat-table { width: 100%; border-collapse: collapse; font-size: 0.92rem; }
.stat-table th {
    text-align: left; color: #9a9aa8; font-weight: 600; font-size: 0.74rem;
    letter-spacing: 0.08em; text-transform: uppercase;
    padding: 0.35rem 0.6rem; border-bottom: 1px solid #2c2c38;
}
.stat-table td { padding: 0.32rem 0.6rem; border-bottom: 1px solid #22222c; }
.stat-table tr:last-child td { border-bottom: 0; }
.stat-table th.num, .stat-table th.value, .stat-table td.num, .stat-table td.value {
    text-align: right; font-variant-numeric: tabular-nums;
}
.stat-table td.value { font-weight: 700; }
.stat-table .rank { width: 3rem; color: #9a9aa8; font-variant-numeric: tabular-nums; }

/* Your own row, on a board or in the round history: a wash of the house
   amber, nothing louder — you already know where you are sitting. */
.stat-table tr.me td { background: rgba(245, 185, 66, 0.09); }
.stat-table tr.me .rank { color: #f5b942; }

.board-retry {
    background: #2c2c38; color: #e8e8ee; border: 0; border-radius: 4px;
    padding: 0.25rem 0.7rem; margin-left: 0.45rem; cursor: pointer; font-size: 0.85rem;
}
.board-retry:hover { background: #3a3a48; }

/* The career card's tiles: number first, context underneath in the muted
   voice every other page uses for secondary information. */
.stat-tiles {
    display: grid; gap: 0.6rem;
    grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
}
.tile {
    background: #1a1a22; border: 1px solid #2c2c38; border-radius: 6px;
    padding: 0.55rem 0.75rem;
    display: flex; flex-direction: column; gap: 0.15rem; min-width: 0;
}
.tile-value { font-weight: 800; font-size: 1.15rem; font-variant-numeric: tabular-nums; }
.tile-caption { color: #9a9aa8; font-size: 0.76rem; }

/* The fun facts: label in the left gutter, story beside it. */
.facts { list-style: none; padding: 0; margin: 0; display: grid; gap: 0.4rem; }
.facts li { display: flex; gap: 0.6rem; align-items: baseline; font-size: 0.95rem; }
.fact-label { color: #9a9aa8; flex: 0 0 8.5rem; font-size: 0.85rem; }
.fact-none { color: #9a9aa8; }
