/* public/style.css — the game client.
   Design tokens live in tokens.css, which every page loads first. */

* {
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}

html,
body {
    margin: 0;
    padding: 0;
    /* Two off-centre pools of colour behind the menu so the flat #0b0e13 field
       has some depth. Fixed attachment keeps them still while a card scrolls. */
    background:
        radial-gradient(1100px 620px at 12% -10%, rgba(41, 211, 160, 0.10), transparent 60%),
        radial-gradient(900px 560px at 108% 110%, rgba(64, 132, 214, 0.12), transparent 62%),
        var(--bg);
    background-attachment: fixed;
    color: var(--ink);
    font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial,
        Apple Color Emoji, Segoe UI Emoji;
    overflow: hidden;
    height: 100vh;
    width: 100vw;
    touch-action: none;
    -webkit-user-select: none;
    user-select: none;
}

.panel {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    padding: 16px;
    max-width: 1200px;
    margin: 0 auto;
    height: 100vh;
    overflow: hidden;
    place-items: center;
}

.panel.show-home {
    grid-template-columns: 1fr;
}

.panel.show-waiting {
    grid-template-columns: 1fr;
}

/* Mobile responsive layout */
@media (max-width: 768px) {
    .panel {
        grid-template-columns: 1fr;
        padding: 12px;
        gap: 12px;
    }
    
    .panel.show-home,
    .panel.show-waiting {
        grid-template-columns: 1fr;
    }
}

.card {
    background: var(--card);
    border: 1px solid var(--card-border);
    border-radius: 14px;
    padding: 16px;
    overflow-y: auto;
    max-height: 100%;
    transition: opacity 0.4s ease, transform 0.4s ease;
    opacity: 1;
    transform: scale(1);
}

.card.hidden {
    opacity: 0;
    transform: scale(0.95);
    pointer-events: none;
    position: absolute;
}

.card::-webkit-scrollbar {
    width: 8px;
}

.card::-webkit-scrollbar-track {
    background: transparent;
}

.card::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 4px;
}

.card::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.15);
}

/* Global scrollbar styling */
::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: rgba(0, 0, 0, 0.2);
}

::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, 0.15);
    border-radius: 5px;
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(255, 255, 255, 0.25);
}

* {
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.15) rgba(0, 0, 0, 0.2);
}

.left h1 {
    margin: 0 0 14px 0;
    font-family: var(--font-display);
    font-size: 34px;
    font-weight: 800;
    letter-spacing: -0.02em;
    line-height: 1.05;
    background: linear-gradient(100deg, var(--ink) 10%, var(--accent) 95%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

/* Keyboard navigation of the menu was listed as the top accessibility gap.
   A single visible ring on every interactive element, shown only for keyboard
   focus so it never fires on a mouse click or a tap. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

.right h2 {
    margin: 0 0 8px 0;
    font-size: 20px;
}

.stack {
    display: grid;
    gap: 6px;
    margin: 10px 0;
}

.lbl {
    font-size: 12px;
    opacity: 0.8;
}

input,
button,
select {
    font: inherit;
    touch-action: manipulation;
}

input {
    padding: 8px 10px;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(255, 255, 255, 0.05);
    color: var(--ink);
}

select.map-select {
    padding: 8px 10px;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: rgba(255, 255, 255, 0.05);
    color: var(--ink);
}

.row {
    display: flex;
    gap: 8px;
    margin-top: 8px;
}

button {
    padding: 10px 14px;
    border-radius: 10px;
    border: 1px solid rgba(255, 255, 255, 0.15);
    background: #18202c;
    color: var(--ink);
    cursor: pointer;
    min-height: 44px;
}

button:hover {
    filter: brightness(1.05);
}

button:active {
    filter: brightness(0.95);
    transform: scale(0.98);
}

/* Mobile-friendly touch targets */
@media (max-width: 768px) {
    input {
        padding: 12px 14px;
        font-size: 16px;
    }
    
    button {
        padding: 14px 18px;
        font-size: 16px;
        min-height: 48px;
    }
}

button.hidden {
    display: none;
}

button.primary-btn {
    background: var(--accent);
    color: #0b0e13;
    font-weight: 600;
    border: none;
}

button.primary-btn:hover {
    filter: brightness(1.1);
}

button.danger-btn {
    background: #dc3232;
    color: #fff;
    font-weight: 600;
    border: none;
}

button.danger-btn:hover {
    background: #e84545;
}

.mode-section {
    margin: 8px 0;
}

.mode-section h3 {
    margin: 0 0 8px 0;
    font-size: 16px;
    opacity: 0.9;
}

/* Public Game Card */
.public-game-card {
    background: var(--card);
    border: 2px solid var(--card-border);
    border-radius: 12px;
    padding: 0;
    margin: 16px 0;
    cursor: pointer;
    transition: all 0.3s ease;
    overflow: hidden;
    display: flex;
    gap: 16px;
}

.public-game-card:hover {
    border-color: rgba(41, 211, 160, 0.4);
    transform: translateY(-2px);
}

.public-game-card:active {
    transform: translateY(0);
}

/* Mobile responsive game card */
@media (max-width: 768px) {
    .public-game-card {
        flex-direction: column;
        gap: 0;
    }
    
    .map-preview {
        width: 100% !important;
        height: 120px !important;
    }
    
    .game-info {
        padding: 12px !important;
    }
}

.public-game-card[data-in-queue="true"] {
    border-color: var(--accent);
    box-shadow: 0 0 20px rgba(41, 211, 160, 0.4);
    background: linear-gradient(135deg, rgba(41, 211, 160, 0.2), rgba(41, 211, 160, 0.3));
}

.map-preview {
    width: 140px;
    height: 140px;
    /* Reads as open water behind the land silhouette, matching the playfield. */
    background:
        radial-gradient(circle at 50% 40%, rgba(64, 132, 214, 0.18), transparent 70%),
        rgba(0, 0, 0, 0.32);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.map-preview svg {
    width: 100%;
    height: 100%;
    padding: 16px;
}

/* The silhouette is the only thing distinguishing 52 otherwise identical map
   cards, so it gets a fill and a readable stroke rather than a hairline. */
.map-outline-path {
    fill: rgba(41, 211, 160, 0.14);
    stroke: rgba(230, 240, 250, 0.55);
    stroke-width: 2;
    stroke-linejoin: round;
    vector-effect: non-scaling-stroke;
    transition: fill 0.25s ease, stroke 0.25s ease;
}

.map-card:hover .map-outline-path {
    fill: rgba(41, 211, 160, 0.26);
    stroke: var(--accent);
}

.map-card.selected .map-outline-path {
    fill: rgba(41, 211, 160, 0.38);
    stroke: var(--accent);
    filter: drop-shadow(0 0 6px rgba(41, 211, 160, 0.6));
}

.public-game-card[data-in-queue=\"true\"] .map-outline-path {
    stroke: var(--accent);
    stroke-width: 3;
    filter: drop-shadow(0 0 4px var(--accent));
}

.game-info {
    flex: 1;
    padding: 16px 16px 16px 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

.game-info h3 {
    margin: 0 0 4px 0;
    font-size: 18px;
    color: var(--ink);
}

.map-name {
    font-size: 14px;
    color: var(--accent);
    font-weight: 600;
    margin-bottom: 2px;
}

/* What the queued match is, under the map name. Muted, because the map is the
   headline and this is the detail — but present, because the public queue is
   not always a score race. */
.map-mode {
    font-size: 12px;
    color: var(--muted);
    margin-bottom: 8px;
}

.game-stats {
    display: flex;
    gap: 16px;
    font-size: 14px;
    opacity: 0.8;
}

.players-count {
    font-weight: 600;
}

.countdown {
    color: var(--accent);
}

#queueStatus {
    margin-top: 6px;
    font-size: 13px;
    padding: 6px;
    background: rgba(41, 211, 160, 0.1);
    border-radius: 6px;
    text-align: center;
}

.public-lobbies {
    margin-top: 16px;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.public-lobbies h3 {
    margin: 0 0 8px 0;
    font-size: 14px;
    opacity: 0.9;
}

.lobbies-list {
    display: grid;
    gap: 6px;
    font-size: 13px;
}

.lobby-item {
    padding: 8px;
    background: rgba(255, 255, 255, 0.03);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 6px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.lobby-item .lobby-info {
    flex: 1;
}

.lobby-item .lobby-map {
    font-weight: 600;
    color: var(--accent);
}

.lobby-item .lobby-players {
    opacity: 0.8;
    font-size: 12px;
}

.lobby-item .lobby-timer {
    background: rgba(41, 211, 160, 0.15);
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 11px;
}

.players {
    min-height: 140px;
}

#homeCard {
    max-width: 500px;
    width: 100%;
}

#waitingRoom {
    max-width: 500px;
    width: 100%;
}

/* Mobile canvas adjustments */
#game {
    touch-action: none;
    max-width: 100vw;
    max-height: 100vh;
}

@media (max-width: 768px) {
    #homeCard,
    #waitingRoom {
        max-width: 100%;
        max-height: 85vh;
    }
    
    .card {
        max-height: 85vh;
    }
}

#leaveLobby {
    margin-top: 12px;
    background: rgba(211, 41, 41, 0.2);
    border: 1px solid rgba(211, 41, 41, 0.4);
    color: rgba(255, 100, 100, 1);
}

#leaveLobby:hover {
    background: rgba(211, 41, 41, 0.3);
    border-color: rgba(211, 41, 41, 0.6);
}

#destroyLobby {
    margin-top: 12px;
    background: rgba(211, 41, 41, 0.3);
    border: 1px solid rgba(211, 41, 41, 0.5);
    color: rgba(255, 80, 80, 1);
    font-weight: 600;
}

#destroyLobby:hover {
    background: rgba(211, 41, 41, 0.4);
    border-color: rgba(211, 41, 41, 0.7);
}

/* Color Selector */
.color-selector {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 6px;
    margin-top: 4px;
}

@media (max-width: 768px) {
    .color-selector {
        gap: 8px;
    }
}

.color-option {
    position: relative;
    width: 100%;
    aspect-ratio: 1;
    border-radius: 8px;
    border: 2px solid rgba(255, 255, 255, 0.2);
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

.color-option:hover {
    transform: scale(1.1);
    border-color: rgba(255, 255, 255, 0.4);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.color-option:active {
    transform: scale(0.95);
}

/* Was scale(1.3), which pushed the swatch over its neighbours in a 6-column
   grid with a 6px gap. A ring conveys selection without stealing the space. */
.color-option.selected {
    transform: scale(1.08);
    border-color: var(--accent);
    border-width: 2px;
    box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent), 0 0 14px rgba(41, 211, 160, 0.45);
    z-index: 10;
}

.color-option.selected:hover {
    transform: scale(1.14);
}

.color-check {
    font-size: 18px;
    color: white;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
    opacity: 0;
    transition: opacity 0.2s ease;
    pointer-events: none;
}

.color-option.selected .color-check {
    opacity: 1;
}

.hint {
    opacity: 0.75;
    font-size: 12px;
}

.legend {
    display: grid;
    gap: 6px;
    margin-top: 8px;
    opacity: 0.8;
    font-size: 13px;
}

.chip {
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    margin-right: 6px;
    vertical-align: -2px;
    background: #888;
}

.chip-gator {
    background: #29d3a0;
}

.chip-human {
    background: #e65e5e;
}

.chip-crystal {
    background: #9d79f9;
}

.chip-florida {
    background: #3aa0f2;
}

#hud.hidden {
    display: none;
}

#gameOver {
    position: fixed;
    inset: 0;
    background: rgba(4, 7, 11, 0.94);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 1000;
    text-align: center;
    padding: 24px;
}

#gameOverCard {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    max-width: 560px;
    /* Rises into place rather than appearing. The match has just ended, so the
       screen is allowed a beat of its own before the player reads it. */
    animation: go-rise 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

#gameOverBadge {
    font-size: 64px;
    line-height: 1;
    margin-bottom: 4px;
}

/* Win and loss get different motion, not just a different word. A win pops
   open and settles; a loss drops and stays down. */
#gameOver.is-win #gameOverBadge {
    animation: go-pop 620ms cubic-bezier(0.16, 1, 0.3, 1) 120ms both;
}

#gameOver.is-loss #gameOverBadge {
    animation: go-sink 720ms ease-out 100ms both;
}

#gameOver.is-win #gameOverTitle {
    color: var(--accent);
    text-shadow: 0 0 26px var(--accent-dim);
}

#gameOver.is-loss #gameOverTitle {
    color: var(--danger);
}

#gameOver h2 {
    font-size: 32px;
    margin: 0 0 10px 0;
}

#gameOver p {
    margin: 4px 0;
}

#gameOverScores {
    color: var(--muted);
    max-width: 44ch;
}

#gameOverHome {
    margin-top: 20px;
}

#gameOver.hidden {
    display: none;
}

@keyframes go-rise {
    from { opacity: 0; transform: translateY(18px); }
    to   { opacity: 1; transform: none; }
}

@keyframes go-pop {
    0%   { opacity: 0; transform: scale(0.4) rotate(-12deg); }
    60%  { opacity: 1; transform: scale(1.18) rotate(4deg); }
    100% { opacity: 1; transform: scale(1) rotate(0); }
}

@keyframes go-sink {
    0%   { opacity: 0; transform: translateY(-14px) scale(1.05); }
    55%  { opacity: 1; transform: translateY(4px) scale(1); }
    100% { opacity: 0.75; transform: translateY(0) scale(0.94); }
}

/* The end screen is the one place a long animation is least welcome if motion
   makes you unwell: it is unskippable and it is between you and the button. */
@media (prefers-reduced-motion: reduce) {
    #gameOverCard,
    #gameOver.is-win #gameOverBadge,
    #gameOver.is-loss #gameOverBadge {
        animation: none;
        opacity: 1;
        transform: none;
    }
}

/* Modal styles */
#modal,
#soloMapModal,
#privateMapModal,
#joinLobbyModal,
#quitModal,
#publicLobbyModal,
#lobbyBrowserModal {
    position: fixed;
    inset: 0;
    background: var(--scrim);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    padding: 24px;
    opacity: 1;
    transition: opacity 0.3s ease;
}

#modal.hidden,
#soloMapModal.hidden,
#privateMapModal.hidden,
#joinLobbyModal.hidden,
#quitModal.hidden,
#publicLobbyModal.hidden,
#lobbyBrowserModal.hidden {
    opacity: 0;
    pointer-events: none;
}

.modal-content {
    background: var(--dialog);
    border: 1px solid var(--dialog-border);
    border-radius: 14px;
    padding: 24px;
    max-width: 400px;
    width: 100%;
    text-align: center;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
    transform: scale(1);
    transition: transform 0.3s ease;
    max-height: 90vh;
    overflow-y: auto;
}

@media (max-width: 768px) {
    #modal,
    #soloMapModal,
    #privateMapModal,
    #joinLobbyModal,
    #quitModal,
    #publicLobbyModal,
    #lobbyBrowserModal {
        padding: 12px;
    }
    
    .modal-content {
        padding: 16px;
        max-height: 85vh;
    }
}

#modal.hidden .modal-content,
#soloMapModal.hidden .modal-content,
#privateMapModal.hidden .modal-content,
#joinLobbyModal.hidden .modal-content,
#quitModal.hidden .modal-content,
#publicLobbyModal.hidden .modal-content,
#lobbyBrowserModal.hidden .modal-content {
    transform: scale(0.9);
}

.modal-content h3 {
    margin: 0 0 12px 0;
    font-size: 20px;
    color: var(--accent);
}

.modal-content p {
    margin: 0 0 20px 0;
    opacity: 0.9;
}

.modal-content button {
    min-width: 100px;
}

/* Map selection modal */
.modal-content.map-modal {
    max-width: 600px;
}

/* Win condition configuration */
.win-config {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 16px;
    padding: 12px;
    background: rgba(0, 0, 0, 0.2);
    border-radius: 8px;
}

.win-config .config-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.win-config label {
    font-size: 14px;
    white-space: nowrap;
}

.win-config input[type="number"] {
    width: 70px;
    padding: 6px 10px;
    border: 1px solid var(--card-border);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.1);
    color: var(--ink);
    font-size: 14px;
    text-align: center;
    -moz-appearance: textfield;
    appearance: textfield;
}

.win-config input[type="number"]::-webkit-outer-spin-button,
.win-config input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.win-config input[type="number"]:focus {
    outline: none;
    border-color: var(--accent);
}

.win-config input[type="text"] {
    padding: 6px 8px;
    border: 1px solid var(--card-border);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.1);
    color: var(--ink);
    font-size: 14px;
}

.win-config input[type="text"]:focus {
    outline: none;
    border-color: var(--accent);
}

.win-config select {
    padding: 6px 28px 6px 10px;
    border: 1px solid var(--card-border);
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.1);
    color: var(--ink);
    font-size: 14px;
    cursor: pointer;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23ffffff' d='M6 8L2 4h8z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
}

.win-config select:focus {
    outline: none;
    border-color: var(--accent);
}

.win-config select option {
    background: #1a1a2e;
    color: var(--ink);
    padding: 8px;
}

/* Custom Lobby Buttons */
.lobby-buttons-row {
    display: flex;
    gap: 12px;
    justify-content: center;
}

.lobby-buttons-col {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.lobby-buttons-col button {
    min-width: 130px;
}

/* Lobby Browser */
.lobbies-browser {
    display: flex;
    flex-direction: column;
    gap: 10px;
    max-height: 50vh;
    overflow-y: auto;
    margin: 16px 0;
}

.lobby-card {
    background: rgba(255, 255, 255, 0.05);
    border: 2px solid var(--card-border);
    border-radius: 10px;
    padding: 12px 16px;
    cursor: pointer;
    transition: all 0.2s ease;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.lobby-card:hover {
    border-color: var(--accent);
    background: rgba(41, 211, 160, 0.1);
}

.lobby-card-info {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.lobby-card-name {
    font-size: 16px;
    font-weight: 600;
    color: var(--ink);
}

.lobby-card-details {
    font-size: 12px;
    opacity: 0.8;
}

.lobby-card-players {
    font-size: 14px;
    font-weight: 600;
    color: var(--accent);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* A full lobby is still listed — knowing it exists and is popular is useful —
   but it should not look like the others. */
.lobby-card.full {
    opacity: 0.55;
}

.lobby-card.full .lobby-card-players {
    color: var(--danger);
}

/* Lobby configuration, shown in the browser and again in the waiting room. */
.lobby-config {
    display: flex;
    flex-wrap: wrap;
    gap: 5px;
    margin-top: 6px;
}

.lobby-chip {
    font-size: 11px;
    line-height: 1.6;
    padding: 1px 7px;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--card-border);
    color: var(--muted);
    white-space: nowrap;
}

.lobby-roster {
    list-style: none;
    padding: 0;
    margin: 10px 0 0 0;
    display: grid;
    gap: 4px;
}

.lobby-roster li {
    padding: 5px 9px;
    border-radius: 8px;
    background: var(--surface-2);
    font-size: 14px;
}

.lobby-roster li.me {
    background: var(--accent-dim);
    font-weight: 600;
}

.no-lobbies {
    text-align: center;
    padding: 24px;
    opacity: 0.7;
    font-style: italic;
}

/* There is no blanket `.hidden { display: none }` in this file, and adding one
   would break `.card.hidden`, which hides by fading rather than by display.
   These three elements are toggled with .classList and so had nothing backing
   them: the "no public lobbies" message showed above a full lobby list, and the
   lobby-code row showed an empty code in solo and quick-play games. */
#noLobbiesMessage.hidden,
#lobbyCodeRow.hidden,
.map-search-empty.hidden {
    display: none;
}

#lobbyCodeRow {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 8px 0;
}

/* Quit/Forfeit button (inside score panel) */
.quit-btn {
    margin-top: 10px;
    padding: 6px 16px;
    background: rgba(220, 50, 50, 0.85);
    border: none;
    border-radius: 6px;
    color: #fff;
    font-family: var(--font-display);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.2s ease;
    width: 100%;
}

.quit-btn:hover {
    background: rgba(220, 50, 50, 1);
}

.quit-btn:active {
    transform: scale(0.95);
}

@media (max-width: 768px) {
    .quit-btn {
        padding: 8px 16px;
        font-size: 14px;
    }
}

.map-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
    gap: 12px;
    margin: 16px 0;
    max-height: 60vh;
    overflow-y: auto;
}

@media (max-width: 768px) {
    .map-cards {
        grid-template-columns: 1fr;
        gap: 10px;
        max-height: 50vh;
    }
}

.map-card {
    background: rgba(255, 255, 255, 0.05);
    border: 2px solid var(--card-border);
    border-radius: 12px;
    padding: 0;
    cursor: pointer;
    transition: all 0.3s ease;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.map-card:hover {
    border-color: rgba(41, 211, 160, 0.4);
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}

.map-card.selected {
    border-color: var(--accent);
    background: rgba(41, 211, 160, 0.15);
}

.map-card .map-preview {
    width: 100%;
    height: 150px;
    background:
        radial-gradient(circle at 50% 40%, rgba(64, 132, 214, 0.18), transparent 70%),
        rgba(0, 0, 0, 0.32);
    display: flex;
    align-items: center;
    justify-content: center;
}

.map-card .map-preview svg {
    width: 100%;
    height: 100%;
    padding: 20px;
}

.map-card .map-info {
    padding: 12px;
    text-align: center;
}

.map-card .map-info .map-name {
    font-size: 14px;
    font-weight: 600;
    color: var(--accent);
    margin: 0;
}

.map-card .map-meta {
    font-size: 11px;
    color: var(--muted);
    margin-top: 3px;
    font-variant-numeric: tabular-nums;
}

/* Map search — the catalogue is 52 entries and scrolling it to find one state
   was the slowest part of starting a game. */
.map-search {
    width: 100%;
    margin-bottom: 4px;
}

.map-search-empty {
    text-align: center;
    padding: 24px;
    color: var(--muted);
    font-style: italic;
}

.map-card.filtered-out {
    display: none;
}

/* Game HUD Panels */
.game-hud-panel {
    position: fixed;
    background: rgba(11, 14, 19, 0.85);
    backdrop-filter: blur(8px);
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 12px;
    padding: 12px 16px;
    font-family: ui-monospace, monospace;
    /* Scores and timers change every snapshot. Proportional digits make the
       panel width jitter 20 times a second. */
    font-variant-numeric: tabular-nums;
    box-shadow: var(--shadow);
    z-index: 50;
    pointer-events: none;
}

.leaderboard-panel {
    top: 16px;
    left: 16px;
    min-width: 200px;
}

.leaderboard-panel h3 {
    margin: 0 0 8px 0;
    font-size: 14px;
    opacity: 0.8;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

#leaderboardList {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.leaderboard-item {
    /* Wraps, so the progress bar can take a line of its own under the name and
       score without either of them having to give up width. */
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 2px 8px;
    padding: 4px 0;
    font-size: 14px;
}

/* The target, beside the score. Quieter than the score itself: it is the same
   number on every row and only there to give the score a scale. */
.leaderboard-goal {
    font-weight: 500;
    font-size: 11px;
    color: rgba(255, 255, 255, 0.45);
    margin-left: 1px;
}

/* How close this player is to winning, as a shape. Full means they have won. */
.leaderboard-bar {
    flex-basis: 100%;
    height: 3px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.13);
    overflow: hidden;
}

.leaderboard-bar > i {
    display: block;
    height: 100%;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.45);
    transition: width 0.25s ease-out;
}

/* Your own row is the one you are looking for, so it keeps the accent colour
   the score uses; everybody else's is neutral. */
.leaderboard-item.me .leaderboard-bar > i {
    background: var(--accent);
}

.leaderboard-rank {
    font-weight: 700;
    min-width: 20px;
    text-align: center;
}

.leaderboard-medal {
    font-size: 16px;
    min-width: 20px;
    text-align: center;
}

.leaderboard-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Which gators the server is playing. Quiet — it is a label, not a warning —
   but present, because "am I losing to a person?" is a fair question. */
.leaderboard-bot {
    margin-left: 6px;
    padding: 1px 4px;
    border-radius: 4px;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.5px;
    color: rgba(255, 255, 255, 0.55);
    background: rgba(255, 255, 255, 0.1);
    vertical-align: middle;
}

.leaderboard-score {
    font-weight: 600;
    color: var(--accent);
}

.leaderboard-item.me {
    background: rgba(41, 211, 160, 0.15);
    border-radius: 6px;
    padding: 4px 8px;
    margin: 0 -8px;
}

.score-panel {
    top: 16px;
    right: 16px;
    text-align: right;
    min-width: 140px;
    pointer-events: auto;
}

/* The collapse control. Top-left of the panel, away from the numbers, and big
   enough to be a touch target rather than a decoration. */
.score-toggle {
    position: absolute;
    top: 4px;
    left: 4px;
    width: 26px;
    height: 26px;
    padding: 0;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: rgba(255, 255, 255, 0.55);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, transform 0.2s;
}

.score-toggle:hover,
.score-toggle:focus-visible {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
}

/* Collapsed: score and clock, nothing else. The arrow flips to say which way
   the panel will go. */
.score-panel.collapsed .score-toggle {
    transform: rotate(-90deg);
}

.score-panel.collapsed {
    min-width: 0;
}

.score-panel.collapsed .player-name,
.score-panel.collapsed .score-goal,
.score-panel.collapsed .score-bar,
.score-panel.collapsed .kill-tally,
.score-panel.collapsed #playerLength,
.score-panel.collapsed .quit-btn {
    display: none;
}

/* With everything else gone the footer is just the clock, so it loses the rule
   above it and sits beside the score instead of under it. */
.score-panel.collapsed .score-footer {
    margin-top: 4px;
    padding-top: 0;
    border-top: 0;
}

.score-panel.collapsed .score-figure {
    justify-content: flex-end;
}

.player-name {
    font-size: 16px;
    font-weight: 700;
    color: #fff;
    margin-bottom: 6px;
    padding-bottom: 6px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* The score, and what it takes to win, as one figure. The score is the thing
   the player is here for, so it is the only large number on the panel; the
   target rides alongside it at the weight of a unit, not of a second score. */
.score-figure {
    display: flex;
    align-items: baseline;
    justify-content: flex-end;
    gap: 4px;
    line-height: 1;
}

#playerScoreValue {
    font-size: 30px;
    font-weight: 800;
    color: var(--accent);
    font-variant-numeric: tabular-nums;
}

.score-target {
    font-size: 15px;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.55);
    font-variant-numeric: tabular-nums;
}

/* The gap. This is the line that answers "how close am I", so it is readable
   rather than a caption — it was 10px at half opacity and lost against the
   score above it. */
.score-goal {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.8px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.78);
    margin-top: 3px;
}

/* Progress, because "7 of 40" is a fact and a bar is a feeling. */
.score-bar {
    height: 5px;
    margin-top: 6px;
    border-radius: 3px;
    background: rgba(255, 255, 255, 0.14);
    overflow: hidden;
}

.score-bar-fill {
    height: 100%;
    width: 0;
    border-radius: 3px;
    background: linear-gradient(90deg, var(--accent) 0%, #4de8b5 100%);
    transition: width 0.25s ease-out;
}

/* Close enough to win that the map sends a tank after you — see TANK
   .progressThreshold. The bar turning amber is the only warning that exists
   before the tank itself appears. */
.score-bar-fill.hunted {
    background: linear-gradient(90deg, #b8791a 0%, #e0a33c 100%);
}

.kill-tally {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 8px;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.kill-tally-label {
    font-size: 9px;
    letter-spacing: 1.1px;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.45);
    margin-right: 1px;
}

.score-footer {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 8px;
    padding-top: 7px;
    border-top: 1px solid rgba(255, 255, 255, 0.18);
}

#playerLength {
    font-size: 12px;
    color: rgba(255, 255, 255, 0.6);
    font-variant-numeric: tabular-nums;
}

/* Mobile adjustments for HUD */
@media (max-width: 768px) {
    .game-hud-panel {
        padding: 8px 12px;
        font-size: 12px;
    }
    
    .leaderboard-panel {
        top: 8px;
        left: 8px;
        min-width: 160px;
    }
    
    .leaderboard-panel h3 {
        font-size: 12px;
    }
    
    .leaderboard-item {
        font-size: 12px;
    }
    
    .score-panel {
        top: 8px;
        right: 8px;
        min-width: 120px;
    }
    
    .player-name {
        font-size: 14px;
        margin-bottom: 4px;
        padding-bottom: 4px;
    }
    
    #playerScoreValue {
        font-size: 22px;
    }

    .score-target {
        font-size: 13px;
    }

    #playerLength {
        font-size: 11px;
    }

    .kill-tally {
        font-size: 11px;
        gap: 5px;
    }
}

/* Time display, in the footer row beside the length. It used to be the second
   largest thing on the panel, which put a clock above the score. */
.time-display {
    font-size: 13px;
    font-weight: 700;
    color: rgba(255, 255, 255, 0.85);
    font-variant-numeric: tabular-nums;
    margin-left: auto;
}

.time-display:empty {
    display: none;
}

.time-display.warning {
    color: #ffcc00;
    animation: pulse 1s infinite;
}

.time-display.critical {
    color: #ff4444;
    animation: pulse 0.5s infinite;
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.6; }
}

/* Boost Meter Container - visible on all devices */
#boostMeterContainer {
    position: fixed;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 50;
    text-align: center;

    /* Fixed width, wide enough for the longest the track can ever get
       (BOOST.capacityCeiling × --boost-track). Without it the container is
       shrink-to-fit around the track, so every change in capacity moved the
       whole meter sideways — and because the track's width was a *percentage*
       of that same auto width, the two chased each other. */
    --boost-track: 150px;
    width: 250px;
}

#boostMeterLabel {
    font-size: 11px;
    color: rgba(255, 255, 255, 0.7);
    margin-bottom: 4px;
    text-transform: uppercase;
    letter-spacing: 1px;
}

/* Winded or empty: the label carries the reason the gator is slow, so it has
   to read as a warning rather than as the control hint it is the rest of the
   time. Same amber as the fill and as the badge over the gator itself. */
#boostMeterLabel.winded {
    color: #e0a33c;
    font-weight: 700;
}

#boostMeter {
    /* Set from render.js as capacity × --boost-track. `margin: 0 auto` is what
       keeps it centred inside the fixed-width container as it grows and
       shrinks. */
    width: var(--boost-track);
    margin: 0 auto;
    transition: width 0.25s ease-out;
    height: 14px;
    background: rgba(0, 0, 0, 0.6);
    border: 2px solid rgba(255, 255, 255, 0.4);
    border-radius: 7px;
    overflow: hidden;
}

#boostMeterFill {
    height: 100%;
    width: 100%;
    background: linear-gradient(90deg, #29d3a0 0%, #4de8b5 100%);
    border-radius: 5px;
    transition: width 0.1s ease-out;
}

#boostMeterFill.low {
    background: linear-gradient(90deg, #ff3333 0%, #ff6600 100%);
    animation: boostLow 0.3s infinite;
}

/* Winded: two thirds of the tank spent, and slow until it is completely full.
   Amber rather than red — the meter is not empty and may be well past a third
   back, so "low" would be a lie; what the player needs to see is that the bar
   filling is the thing they are waiting on. */
#boostMeterFill.winded {
    background: linear-gradient(90deg, #b8791a 0%, #e0a33c 100%);
}

#boostMeterFill.cooldown {
    background: linear-gradient(90deg, #666 0%, #888 100%);
}

@keyframes boostLow {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}

/* Touch control overlay for mobile gameplay */
#touchControls {
    position: fixed;
    inset: 0;
    pointer-events: none;
    z-index: 200;
    display: none;
}

#touchControls.active {
    display: block;
    pointer-events: none;
}

/* Virtual Joystick - Left side */
#joystickContainer {
    position: absolute;
    left: 30px;
    bottom: 30px;
    width: 140px;
    height: 140px;
    pointer-events: auto;
    touch-action: none;
}

#joystickBase {
    position: relative;
    width: 100%;
    height: 100%;
    background: radial-gradient(circle, rgba(255,255,255,0.15) 0%, rgba(255,255,255,0.05) 70%, transparent 100%);
    border: 3px solid rgba(255, 255, 255, 0.4);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
}

#joystickKnob {
    position: absolute;
    width: 60px;
    height: 60px;
    background: radial-gradient(circle, rgba(41, 211, 160, 0.9) 0%, rgba(41, 211, 160, 0.6) 100%);
    border: 3px solid rgba(255, 255, 255, 0.6);
    border-radius: 50%;
    box-shadow: 0 4px 15px rgba(0, 0, 0, 0.5);
    transition: transform 0.05s ease-out;
}

#joystickKnob.active {
    background: radial-gradient(circle, rgba(41, 211, 160, 1) 0%, rgba(41, 211, 160, 0.8) 100%);
    box-shadow: 0 0 25px rgba(41, 211, 160, 0.7);
}

/* Boost Button - Right side */
#boostButton {
    position: absolute;
    right: 30px;
    bottom: 30px;
    width: 100px;
    height: 100px;
    background: radial-gradient(circle, rgba(255, 150, 0, 0.8) 0%, rgba(255, 100, 0, 0.6) 100%);
    border: 3px solid rgba(255, 255, 255, 0.5);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: auto;
    touch-action: none;
    box-shadow: 0 4px 15px rgba(255, 100, 0, 0.4);
}

#boostButton span {
    font-size: 14px;
    font-weight: bold;
    color: rgba(255, 255, 255, 0.95);
    text-transform: uppercase;
    letter-spacing: 1px;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
}

#boostButton.active {
    background: radial-gradient(circle, rgba(255, 200, 50, 1) 0%, rgba(255, 150, 0, 0.9) 100%);
    box-shadow: 0 0 30px rgba(255, 150, 0, 0.8);
    transform: scale(0.95);
}

/* Hide boost label on mobile, show touch controls */
@media (max-width: 768px) {
    #boostMeterLabel {
        display: none;
    }
    
    #boostMeterContainer {
        bottom: 180px;
    }
}

@media (min-width: 769px) {
    #touchControls {
        display: none !important;
    }
}

/* Reduced motion ------------------------------------------------------------
   The looping attention-grabbers (low-boost flash, timer pulse) are the ones
   that actually cause trouble, so they stop outright. Transitions are cut to a
   near-zero duration rather than `none`, because `transitionend` listeners
   still fire that way — nothing in the client depends on one today, but a
   removed transition is a silent trap for whoever adds the first one. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }

    .time-display.warning,
    .time-display.critical,
    #boostMeterFill.low {
        animation: none !important;
    }

    /* Hover lifts and selection scaling are pure decoration; the colour and
       border changes alongside them carry the same information. */
    .public-game-card:hover,
    .map-card:hover,
    .color-option:hover,
    .color-option.selected,
    .color-option.selected:hover {
        transform: none;
    }
}

/* Links in the menu. There is no generic anchor rule in this file — the game
   chrome had no links at all until the content pages existed — so without this
   the footer row renders in the browser's default blue on a near-black card. */
.site-links {
    margin-top: 14px;
    text-align: center;
}

.site-links a {
    color: var(--muted);
    text-decoration: none;
}

.site-links a:hover {
    color: var(--accent);
    text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   Kill feed, spectator bar, connection banner (public/js/match-hud.js).
   Each toggled element has its own `.hidden` rule — there is no global one,
   see MEMORY.md.
   --------------------------------------------------------------------------- */

.kill-feed {
    position: fixed;
    top: 16px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 50;
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    pointer-events: none;
    max-width: min(420px, calc(100vw - 32px));
}

.kill-feed-item {
    padding: 3px 10px;
    border-radius: var(--radius-sm);
    background: rgba(11, 14, 19, 0.72);
    border: 1px solid rgba(255, 255, 255, 0.1);
    font-size: 13px;
    color: var(--ink);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    animation: kill-feed-in 180ms ease-out;
}

/* A kill you made or suffered: the one line in the feed that is about you. */
.kill-feed-item.mine {
    border-color: var(--accent);
    background: rgba(41, 211, 160, 0.16);
}

@keyframes kill-feed-in {
    from { opacity: 0; transform: translateY(-4px); }
    to { opacity: 1; transform: none; }
}

.connection-banner {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 60;
    padding: 12px 20px;
    border-radius: var(--radius);
    background: var(--dialog);
    border: 1px solid var(--warn);
    color: var(--warn);
    font-weight: 600;
    box-shadow: var(--shadow);
    pointer-events: none;
}

.connection-banner.hidden {
    display: none;
}

.spectate-bar {
    position: fixed;
    bottom: 76px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 55;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 8px 12px;
    border-radius: var(--radius);
    background: rgba(11, 14, 19, 0.85);
    backdrop-filter: blur(8px);
    border: 1px solid rgba(255, 255, 255, 0.15);
    box-shadow: var(--shadow);
    max-width: calc(100vw - 32px);
}

.spectate-bar.hidden {
    display: none;
}

.spectate-text {
    text-align: center;
    min-width: 0;
}

.spectate-death {
    font-weight: 700;
    color: var(--danger);
}

.spectate-watching {
    font-size: 13px;
    color: var(--muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.spectate-watching.hidden {
    display: none;
}

.spectate-watching strong {
    color: var(--ink);
}

.spectate-hint {
    margin-left: 6px;
    font-size: 11px;
    opacity: 0.7;
}

/* No keyboard on a phone, so the arrows are the controls there. */
@media (hover: none) {
    .spectate-hint { display: none; }
}

.spectate-btn {
    min-width: 40px;
    min-height: 40px;
    border-radius: var(--radius-sm);
    border: 1px solid rgba(255, 255, 255, 0.2);
    background: rgba(255, 255, 255, 0.08);
    color: var(--ink);
    font-size: 16px;
    cursor: pointer;
}

.spectate-btn:hover:not(:disabled),
.spectate-btn:focus-visible {
    border-color: var(--accent);
}

.spectate-btn:disabled {
    opacity: 0.35;
    cursor: default;
}

.leaderboard-away {
    margin-left: 6px;
    padding: 1px 4px;
    border-radius: 4px;
    font-size: 9px;
    font-weight: 700;
    letter-spacing: 0.5px;
    color: var(--warn);
    background: rgba(255, 204, 0, 0.12);
    vertical-align: middle;
}

@media (max-width: 768px) {
    .kill-feed { top: 8px; }
    .kill-feed-item { font-size: 11px; }
    .spectate-bar { bottom: 140px; }
}

@media (prefers-reduced-motion: reduce) {
    .kill-feed-item { animation: none; }
}
