/* ============================================================
   Multiplayer Game Styles — Mobile Optimized
   Streamlined single source of truth
   ============================================================ */

/* ========== RESET & ROOT ========== */
* {
    box-sizing: border-box;
    -webkit-tap-highlight-color: transparent;
}

:root {
    --background-color: #fff;
    --text-color: #000;
    --header-bg: rgb(248, 249, 250);
    --header-border: #ddd;
    --header-link: #000;
    --header-link-hover: #007bff;
    --drawing-stroke-color: #031427;
    --drawing-bg-color: #ffffff;
}

body {
    margin: 0;
    padding: 0;
    font-family: Arial, sans-serif;
    background-color: #ffffff;
    min-height: 100vh;
    color: #333;
}

body.dark-mode {
    --background-color: #333;
    --text-color: #fff;
    --header-bg: rgb(51, 51, 51);
    --header-border: #444;
    --header-link: #fff;
    --header-link-hover: #00bcd4;
    --drawing-stroke-color: #ffffff;
    --drawing-bg-color: #121212;
    background-color: #1a1a1a;
    color: #ffffff;
}

/* ========== KEYFRAMES ========== */
@keyframes slideInUp   { from { opacity: 0; transform: translateY(30px); }  to { opacity: 1; transform: translateY(0); } }
@keyframes pulse       { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
@keyframes spin        { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@keyframes spinAnim    { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
@keyframes shake       { 0%, 100% { transform: translateX(0); } 25% { transform: translateX(-5px); } 75% { transform: translateX(5px); } }
@keyframes modalSlideIn{ from { opacity: 0; transform: scale(0.9) translateY(-20px); } to { opacity: 1; transform: scale(1) translateY(0); } }
@keyframes slideInRight{ from { opacity: 0; transform: translateX(100px); } to { opacity: 1; transform: translateX(0); } }
@keyframes glow        { from { box-shadow: 0 0 5px #00f; } to { box-shadow: 0 0 15px #00f; } }

/* ========== SITE HEADER ========== */
#site-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    width: 100%;
    z-index: 2000;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 10px 16px;
    box-sizing: border-box;
    background-color: var(--header-bg);
    border-bottom: 1px solid var(--header-border);
    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
    height: 60px;
}

.header-content {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0;
}

.header-content .logo { flex-shrink: 0; margin-right: 12px; }
.logo img { height: 40px; cursor: pointer; }

.header-content .nav-links { flex-shrink: 0; margin-right: auto; }

.nav-links { display: flex; gap: 15px; align-items: center; }

.nav-links a {
    text-decoration: none;
    font-size: 14px;
    color: var(--header-link, #333);
    font-weight: bold;
    transition: color 0.2s ease;
}

.nav-links a:hover { color: var(--header-link-hover, #007bff); }

/* Right-side icon group: uniform gap between every icon */
#donation-toggle,
#rules-toggle,
#sound-toggle,
#dark-mode-toggle {
    margin-left: 14px;
    flex-shrink: 0;
}

#donation-toggle,
#rules-toggle,
#sound-toggle {
    position: static;
    font-size: 20px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    color: inherit;
    transition: transform 0.2s ease;
}

#donation-toggle:hover,
#rules-toggle:hover,
#sound-toggle:hover { transform: scale(1.2); }

#dark-mode-toggle {
    display: none; /* shown on mobile only */
    /* Match the other header icons: 1.5rem + 0.25rem padding made this one
       33x28 next to their 25x20. */
    font-size: 20px;
    background: none;
    border: none;
    cursor: pointer;
    padding: 0;
    line-height: 1;
    color: inherit;
    transition: transform 0.2s ease;
}

#dark-mode-toggle:hover { transform: scale(1.2); }

#room-info-header { display: flex; align-items: center; gap: 1rem; }
#room-id-display { font-weight: 600; color: #082f59; }

#connection-indicator {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    background: #28a745;
    animation: pulse 2s infinite;
}

#connection-indicator.disconnected { background: #DC4325; }

/* ========== CONNECTION OVERLAY (lobby) ========== */
#connection-overlay {
    position: fixed;
    top: 60px;
    left: 0;
    width: 100%;
    height: calc(100% - 60px);
    background-color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 999;
}

body.dark-mode #connection-overlay { background-color: #1a1a1a; }

#connection-content {
    background: white;
    padding: 28px 24px 22px;
    border-radius: 18px;
    /* Two shadows: a tight one for the edge and a wide soft one for depth.
       The single flat shadow made the card look pasted on. */
    box-shadow: 0 2px 6px rgba(3, 20, 39, 0.10),
                0 18px 48px rgba(3, 20, 39, 0.16);
    text-align: center;
    max-width: 420px;
    width: 90%;
    border: 1px solid #e2e8f0;
    animation: slideInUp 0.5s ease-out;
}

body.dark-mode #connection-content {
    background: #262626;
    border-color: #3a3a3a;
    color: #ffffff;
}

/* The logo plate: neither logo file has an alpha channel — logo1.png carries
   #f8f9fa baked in and logo2.png #333333 — so a plate in exactly that colour
   turns a visible rectangle into a lockup. */
#lobby-logo {
    display: block;
    height: 34px;
    width: auto;
    margin: 0 auto 8px;
    padding: 7px 14px;
    background: #f8f9fa;
    border-radius: 10px;
    box-sizing: content-box;
}
body.dark-mode #lobby-logo { background: #333333; }

#lobby-tagline {
    margin: 0 0 20px;
    font-size: 0.85rem;
    font-weight: bold;
    opacity: 0.6;
}

#player-setup { margin-bottom: 0; }

/* ── The two ways in ──────────────────────────────────────────────────────
   Quick Match and Create Room used to sit side by side as equal buttons,
   which said nothing about what either did or which most people want. They
   are now stacked rows with a subtitle each, Quick Match filled because it
   is the one that needs no coordination. */
.lobby-choice {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 13px 15px;
    margin: 0 0 10px;
    border: 1px solid #d8e2ee;
    border-radius: 13px;
    background: #f4f8fc;
    color: inherit;
    font-family: inherit;
    text-align: left;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, transform 0.08s;
}
.lobby-choice:hover { background: #e8f0f9; border-color: #b9cee6; }
.lobby-choice:active { transform: translateY(1px); }
.lobby-choice:focus-visible { outline: 2px solid #1e5fa8; outline-offset: 2px; }

.lobby-choice-primary {
    background: #082f59;
    border-color: #082f59;
    color: #fff;
}
.lobby-choice-primary:hover { background: #0d3f75; border-color: #0d3f75; }

.lobby-choice-icon { font-size: 1.25rem; flex: 0 0 auto; }
.lobby-choice-text { display: flex; flex-direction: column; min-width: 0; }
.lobby-choice-title { font-size: 0.97rem; font-weight: bold; }
.lobby-choice-sub { font-size: 0.75rem; opacity: 0.72; margin-top: 1px; }

body.dark-mode .lobby-choice { background: #2f2f2f; border-color: #3f3f3f; }
body.dark-mode .lobby-choice:hover { background: #383838; border-color: #4d4d4d; }
body.dark-mode .lobby-choice-primary { background: #1e5fa8; border-color: #1e5fa8; }
body.dark-mode .lobby-choice-primary:hover { background: #2a74c9; border-color: #2a74c9; }

/* A labelled rule, rather than the bare border that used to divide these. */
.lobby-or {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 14px 0 12px;
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.5;
}
.lobby-or::before,
.lobby-or::after {
    content: "";
    flex: 1;
    height: 1px;
    background: currentColor;
    opacity: 0.35;
}

#join-room-section {
    display: flex;
    gap: 8px;
    border-top: 0;
    padding-top: 0;
    margin-top: 10px;
}

#room-code-input {
    flex: 1 1 auto;
    min-width: 0;
    width: auto;
    padding: 11px 13px;
    border: 1px solid #d8e2ee;
    border-radius: 11px;
    font-size: 0.92rem;
    font-family: inherit;
}
#room-code-input:focus { outline: 2px solid #1e5fa8; outline-offset: -1px; border-color: #1e5fa8; }

#join-room-button {
    flex: 0 0 auto;
    padding: 11px 20px;
    border: 1px solid #082f59;
    border-radius: 11px;
    background: #fff;
    color: #082f59;
    font-family: inherit;
    font-size: 0.92rem;
    font-weight: bold;
    cursor: pointer;
    transition: background-color 0.15s;
}
#join-room-button:hover { background: #e8f0f9; }

body.dark-mode #room-code-input { background: #1f1f1f; border-color: #3f3f3f; color: #eee; }
body.dark-mode #join-room-button { background: #2f2f2f; border-color: #1e5fa8; color: #8ab4dd; }
body.dark-mode #join-room-button:hover { background: #383838; }

/* ── Waiting in a room ────────────────────────────────────────────────────
   The code used to appear twice — once in the status line and again below —
   while the matchmaking buttons stayed on screen as though you could still
   pick something else. One state at a time, one copy of the code, and a way
   to actually copy it. */
#room-info {
    background: #f4f8fc;
    padding: 18px 16px 16px;
    border: 1px solid #d8e2ee;
    border-radius: 14px;
    margin-top: 0;
}

#room-code-label {
    font-size: 0.72rem;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    opacity: 0.6;
    margin-bottom: 6px;
}

#room-code-display {
    font-family: "SF Mono", Menlo, Consolas, monospace;
    font-size: 1.15rem;
    font-weight: bold;
    letter-spacing: 0.06em;
    color: #082f59;
    word-break: break-all;
    margin-bottom: 10px;
}
body.dark-mode #room-code-display { color: #8ab4dd; }

#copy-room-code {
    padding: 6px 14px;
    border: 1px solid #b9cee6;
    border-radius: 999px;
    background: #fff;
    color: #082f59;
    font-family: inherit;
    font-size: 0.78rem;
    font-weight: bold;
    cursor: pointer;
}
#copy-room-code:hover { background: #e8f0f9; }
#copy-room-code.copied { background: #0f7b3f; border-color: #0f7b3f; color: #fff; }
body.dark-mode #copy-room-code { background: #2f2f2f; border-color: #3f3f3f; color: #8ab4dd; }

#players-list { margin: 14px 0 0; font-size: 0.85rem; }

/* A quiet status line under the card's content, not a boxed field. It used
   to be styled like an input, which made a sentence look editable. */
#connection-status {
    margin: 16px 0 0;
    font-size: 0.82rem;
    line-height: 1.4;
    opacity: 0.7;
    min-height: 1.2em;
}

#leave-room-button {
    display: block;
    margin: 12px auto 0;
    padding: 5px 12px;
    border: 0;
    background: none;
    color: inherit;
    font-family: inherit;
    font-size: 0.76rem;
    opacity: 0.55;
    text-decoration: underline;
    cursor: pointer;
}
#leave-room-button:hover { opacity: 0.85; }

.ready-button {
    display: block;
    width: 100%;
    background: #082f59;
    color: white;
    border: 0;
    padding: 13px;
    border-radius: 12px;
    font-family: inherit;
    font-size: 1rem;
    font-weight: bold;
    cursor: pointer;
    margin-top: 14px;
    transition: background-color 0.15s, transform 0.08s;
}
.ready-button:hover { background: #0d3f75; }
.ready-button:active { transform: translateY(1px); }
body.dark-mode .ready-button { background: #1e5fa8; }
body.dark-mode .ready-button:hover { background: #2a74c9; }

/* The old green hover lived on after the button went navy and, being later
   in the file, quietly won. */
.ready-button:disabled {
    background: #6c757d;
    cursor: not-allowed;
    transform: none;
}

/* ========== MAIN GAME INTERFACE ========== */
#multiplayer-game-interface {
    position: fixed;
    top: 60px;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100vw;
    overflow: hidden;
}

body.dark-mode #multiplayer-game-interface { background-color: #1a1a1a; }

#game-wrapper {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

body.dark-mode #game-header { background: #2d2d2d; border-bottom: 2px solid #444; }

/* ========== GAME MAIN AREA ========== */
#game-main {
    flex: 1;
    display: flex;
    flex-direction: column;
    max-width: 1200px;
    margin: 0 auto;
    padding: 0.5rem;
    width: 100%;
    min-height: 0;
    overflow: hidden;
}

/* ========== PLAYERS SECTION ========== */
#players-section {
    flex: 1;
    display: flex;
    gap: 1rem;
    align-items: flex-start;
    justify-content: center;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    margin-bottom: 0.5rem;
}

.game-section {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    min-width: 280px;
    max-width: 450px;
    min-height: 0;
    height: fit-content;
    align-self: flex-start;
    background: white;
    border-radius: 10px;
    padding: 0.75rem;
    border: 2px solid #031427;
    box-shadow: 0 4px 8px rgba(3, 20, 39, 0.1);
    overflow: hidden;
    transition: transform 0.3s ease;
}

.game-section:hover { transform: translateY(-2px); }

body.dark-mode .game-section { background: #2d2d2d; border-color: #444; }

.section-header {
    flex: 0 0 auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.5rem;
    padding: 0.4rem 0.6rem;
    background: #f8f9fa;
    border: 2px solid #031427;
    border-radius: 10px;
}

body.dark-mode .section-header { background: #2d2d2d; border-color: #444; }

.section-header h3 { margin: 0; font-size: 0.9rem; color: #082f59; }

body.dark-mode .section-header h3,
body.dark-mode .score-display { color: #ffffff; }

.score-display { font-size: 0.85rem; font-weight: bold; color: #082f59; }

#player-status,
#opponent-status {
    flex: 0 0 auto;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.5rem;
    margin-bottom: 0.5rem;
    padding: 0.3rem 0.5rem;
    background: #f8f9fa;
    border: 2px solid #031427;
    border-radius: 10px;
    font-size: 0.75rem;
}

body.dark-mode #player-status,
body.dark-mode #opponent-status { background: #2d2d2d; border-color: #444; }

#player-connection-status.connected,
#opponent-connection-status.connected { color: #28a745; font-weight: bold; }

#player-connection-status.disconnected,
#opponent-connection-status.disconnected { color: #dc3545; font-weight: bold; }

#player-pattern-info {
    margin-bottom: 1rem;
    padding: 1rem;
    background: #f8f9fa;
    border: 2px solid #031427;
    border-radius: 10px;
}

body.dark-mode #player-pattern-info { background: #2d2d2d; border-color: #444; }

#player-pattern-description { font-weight: bold; color: #082f59; margin-bottom: 0.5rem; }
body.dark-mode #player-pattern-description { color: #ffffff; }

#player-pattern-preview {
    font-family: 'Courier New', monospace;
    background: white;
    padding: 0.5rem;
    border-radius: 10px;
    border: 2px solid #031427;
    color: #082f59;
    font-weight: bold;
    margin-bottom: 0.5rem;
}

body.dark-mode #player-pattern-preview {
    background: #2d2d2d;
    border-color: #444;
    color: #ffffff;
}

/* ========== GRID CONTAINERS ========== */
#player-game-container,
#opponent-game-container {
    flex: 0 0 auto;
    display: grid;
    gap: 3px;
    justify-content: center;
    align-content: start;
    width: fit-content;
    height: fit-content;
    align-self: center;
    margin: 0 auto;
    overflow: visible;
}

.game-grid {
    flex: 0 0 auto;
    display: grid;
    gap: 3px;
    justify-content: center;
    margin: 0;
    width: fit-content;
}

#player-stats,
#opponent-stats {
    flex: 0 0 auto;
    display: flex;
    justify-content: space-between;
    gap: 0.4rem;
    margin-top: 0.1rem;
    margin-bottom: 0;
}

#player-stats > div,
#opponent-stats > div {
    flex: 1;
    background: #f8f9fa;
    padding: 0.3rem 0.5rem;
    border-radius: 8px;
    border: 2px solid #031427;
    font-size: 0.75rem;
    font-weight: bold;
    color: #082f59;
    text-align: center;
}

body.dark-mode #player-stats > div,
body.dark-mode #opponent-stats > div {
    background: #2d2d2d;
    border-color: #444;
    color: #ffffff;
}

/* VS Separator */
#vs-separator {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    padding: 0 0.5rem;
}

.vs-text {
    background-color: #082f59;
    color: white;
    width: 50px;
    height: 50px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    font-size: 1.1rem;
    margin-bottom: 1rem;
    border: 2px solid #031427;
    animation: spin 8s linear infinite;
}

body.dark-mode .vs-text { background-color: #404040; border-color: #555; }

.separator-line { width: 2px; height: 100px; background: #031427; border-radius: 1px; }
body.dark-mode .separator-line { background: #444; }

/* ========== BOX STYLING ========== */
.box {
    width: 45px;
    height: 45px;
    background-color: #e0e0e0;
    border: 2px solid #b0b0b0;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 16px;
    font-weight: bold;
    color: #333333;
    text-align: center;
    line-height: 45px;
    caret-color: black;
    vertical-align: middle;
    transition: all 0.3s ease;
    border-radius: 8px;
    cursor: pointer;
}

.box:hover { border-color: #007bff; background-color: #f8f9fa; }

.box:focus { outline: none; caret-color: transparent; text-align: center; }

.box.user-input {
    background-color: rgba(0, 123, 255, 0.3);
    border-color: rgba(0, 123, 255, 0.6);
    color: #2d527a;
    backdrop-filter: blur(2px);
    box-shadow: 0 0 15px rgba(0, 123, 255, 0.4);
}

.box.hint-filled {
    background-color: rgba(255, 193, 7, 0.4);
    border-color: rgba(255, 193, 7, 0.7);
    color: #ffc107;
    backdrop-filter: blur(2px);
    box-shadow: 0 0 15px rgba(255, 193, 7, 0.4);
}

.box.correct { background-color: #006164; border-color: #006164; color: white; }
.box.present { background-color: #EDA247; border-color: #EDA247; color: white; }
.box.absent  { background-color: #DB4325; border-color: #DB4325; color: white; }

.box.hover-highlight {
    border-color: #007bff;
    background-color: #e3f2fd;
    box-shadow: 0 0 10px rgba(0, 123, 255, 0.3);
    transform: scale(1.05);
}

.box.current-row,
.box.active-row { cursor: pointer; opacity: 1; }

.box.inactive-row { opacity: 0.6; cursor: not-allowed; }

.box.shake,
.shake { animation: shake 0.6s ease-in-out; }

.opponent-grid .box {
    cursor: default;
    background: #f8f9fa;
    border-color: #b0b0b0;
    color: #6c757d;
}

.opponent-grid .box.correct { background-color: #006164; border-color: #006164; color: white; }
.opponent-grid .box.present { background-color: #EDA247; border-color: #EDA247; color: white; }
.opponent-grid .box.absent  { background-color: #DB4325; border-color: #DB4325; color: white; }

/* Highlight effects */
.active-row {
    transform: scale(1.05);
    background-color: #f0f0f0;
    z-index: 1;
    transition: transform 0.2s ease, background-color 0.2s ease;
}

.highlight-box {
    border: 2px solid #00f;
    box-shadow: 0 0 10px #00f;
    animation: glow 1s infinite alternate;
}

/* ========== GAME CONTROLS ========== */
#game-controls-section {
    flex: 0 0 auto;
    background: white;
    border-radius: 10px;
    padding: 0.75rem;
    border: 2px solid #031427;
    box-shadow: 0 4px 8px rgba(3, 20, 39, 0.1);
    max-width: 400px;
    margin: 0.5rem auto 0 auto;
    width: 100%;
}

body.dark-mode #game-controls-section { background: #2d2d2d; border-color: #444; }

#game-info-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 0.75rem;
}

#timer-and-switch { display: flex; align-items: center; gap: 8px; }

#game-timer {
    font-size: 0.5rem;
    font-weight: 600;
    color: #082f59;
    background: #f8f9fa;
    border-radius: 10px;
    border: 2px solid #031427;
}

body.dark-mode #game-timer { color: #ffffff; background: #2d2d2d; border-color: #444; }

/* Mobile Cycle Button (hidden on desktop, shown via mobile media query) */
#mobile-bottom-cycle-btn {
    display: none;
    width: 100%;
    padding: 0.75rem;
    margin-bottom: 0.75rem;
    background: #082f59;
    color: white;
    border: 2px solid #031427;
    border-radius: 8px;
    font-weight: bold;
    font-size: 1rem;
    cursor: pointer;
    transition: all 0.3s ease;
}

#mobile-bottom-cycle-btn:hover { background: #143354; }
#mobile-bottom-cycle-btn:active { transform: scale(0.98); }

body.dark-mode #mobile-bottom-cycle-btn { background: #505050; border-color: #555; }

.bottom-panel { display: block; }
.bottom-panel.hidden { display: none; }

/* Desktop Keypad (hidden in favor of mobile-style keypad panel) */
#multiplayer-keypad-container,
.keypad-container {
    display: none;
    justify-content: center;
    gap: 10px;
    flex-wrap: wrap;
    max-width: 500px;
    width: 100%;
    margin: 0 auto 0.75rem auto;
}

.keypad-button {
    width: 60px;
    height: 60px;
    font-size: 18px;
    font-weight: bold;
    border: 2px solid #031427;
    border-radius: 10px;
    background-color: #f8f9fa;
    color: #082f59;
    cursor: pointer;
    transition: all 0.3s ease;
    text-align: center;
}

.keypad-button:hover {
    background-color: #143354;
    color: white;
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}

.keypad-button:active { transform: translateY(0); }

.keypad-button.enter-button {
    width: 100px;
    background-color: #28a745;
    border-color: #28a745;
    color: white;
}

.keypad-button.enter-button:hover { background-color: #218838; border-color: #218838; }

.keypad-button.hint-button {
    background-color: #ffc107;
    border-color: #ffc107;
    color: #212529;
}

.keypad-button.hint-button:hover { background-color: #e0a800; border-color: #e0a800; }

.keypad-button:disabled { opacity: 0.5; cursor: not-allowed; transform: none !important; }

body.dark-mode .keypad-button { background-color: #404040; color: #ffffff; border-color: #555; }
body.dark-mode .keypad-button:hover { background-color: #505050; border-color: #00bcd4; }

/* Game Actions */
#game-actions { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; }

.action-button {
    padding: 0.75rem 1.5rem;
    border: 2px solid #031427;
    border-radius: 10px;
    background: #f8f9fa;
    font-size: 1rem;
    font-weight: bold;
    color: #082f59;
    cursor: pointer;
    transition: all 0.3s ease;
}

.action-button:hover {
    transform: translateY(-2px);
    background-color: #143354;
    color: white;
    box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}

body.dark-mode .action-button { background: #404040; color: #ffffff; border-color: #555; }
body.dark-mode .action-button:hover { background-color: #505050; border-color: #00bcd4; }

.action-button.submit-button { background-color: #28a745; color: white; border-color: #28a745; }
.action-button.submit-button:hover { background-color: #218838; border-color: #218838; }

.action-button.leave-button { background-color: #DC4325; color: white; border-color: #DC4325; }
.action-button.leave-button:hover { background-color: #B8341F; border-color: #B8341F; }

/* ========== SHARED BOTTOM-PANEL INNER LAYOUT ========== */
/* Defines HOW panels lay out children. Display is owned by JS/media
   queries; height is owned by the desktop/mobile context blocks below. */

#mobile-keypad-panel {
    display: none;
    flex-direction: column;
    gap: 4px;
    box-sizing: border-box;
    overflow: hidden;
}

#mobile-numpad-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    flex: 1;
    min-height: 0;
    gap: 4px;
}

.mobile-num-btn {
    width: 100%;
    height: 100%;
    min-height: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    border: 2px solid #031427;
    border-radius: 6px;
    background: #f8f9fa;
    color: #082f59;
    font-size: 0.95rem;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.3s ease;
}

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

body.dark-mode .mobile-num-btn { background: #404040; border-color: #555; color: #ffffff; }

#mobile-nav-row { display: flex; flex: 0 0 auto; gap: 4px; margin-top: 4px; }

/* Neutral grey for Left/Right nav buttons */
.mobile-nav-btn {
    width: 100%;
    height: 34px;
    min-height: 0;
    flex: 1;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    border: 2px solid #031427;
    border-radius: 6px;
    background: #f8f9fa;
    color: #082f59;
    font-size: 0.8rem;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.3s ease;
}

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

body.dark-mode .mobile-nav-btn { background: #3a3a3a; border-color: #555; color: #e0e0e0; }

/* Enter button — gold accent */
.mobile-nav-btn.enter { background: #EDA247; border-color: #EDA247; color: #212529; }
.mobile-nav-btn.enter:active { background: #d4893a; border-color: #d4893a; }
body.dark-mode .mobile-nav-btn.enter { background: #EDA247; border-color: #EDA247; color: #212529; }

/* Mobile Calculator Panel */
#mobile-calculator-panel {
    display: none;
    flex-direction: column;
    gap: 4px;
    background: #f8f9fa;
    padding: 0.75rem;
    border-radius: 8px;
    border: 2px solid #031427;
    box-sizing: border-box;
    overflow: hidden;
}

body.dark-mode #mobile-calculator-panel { background: #2d2d2d; border-color: #444; }

#mobile-calc-display {
    flex: 0 0 auto;
    width: 100%;
    box-sizing: border-box;
    padding: 0.6rem;
    margin-bottom: 0.5rem;
    border: 2px solid #031427;
    border-radius: 8px;
    background: white;
    font-size: 1.2rem;
    text-align: right;
    color: #082f59;
}

body.dark-mode #mobile-calc-display { background: #404040; border-color: #555; color: #ffffff; }

#mobile-calc-buttons {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    flex: 1;
    min-height: 0;
    gap: 4px;
}

.mobile-calc-btn {
    width: 100%;
    height: 100%;
    min-height: 0;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    border: 2px solid #031427;
    border-radius: 6px;
    background: #f8f9fa;
    color: #082f59;
    font-size: 0.85rem;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.3s ease;
}

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

body.dark-mode .mobile-calc-btn { background: #404040; border-color: #555; color: #ffffff; }

.mobile-calc-btn.operator { background: #007bff; color: white; border-color: #007bff; }
body.dark-mode .mobile-calc-btn.operator { background: #0056b3; }

.mobile-calc-btn.equals { background: #28a745; color: white; border-color: #28a745; }
body.dark-mode .mobile-calc-btn.equals { background: #218838; }

.mobile-calc-btn.clear {
    background: #DC4325;
    color: white;
    border-color: #DC4325;
    grid-column: span 4;
}

body.dark-mode .mobile-calc-btn.clear { background: #B8341F; }

/* Mobile Drawing Panel */
#mobile-drawing-panel {
    display: none;
    flex-direction: column;
    gap: 4px;
    box-sizing: border-box;
    overflow: hidden;
    border: 2px solid #031427;
    border-radius: 8px;
    padding: 2px;
}

body.dark-mode #mobile-drawing-panel { border-color: #555; }

#mobile-drawing-canvas {
    display: block;
    width: 100%;
    height: 100%;
    flex: 1 1 auto;
    min-width: 0;
    min-height: 0;
    box-sizing: border-box;
    touch-action: none;
    -webkit-touch-callout: none;
    user-select: none;
    border: none !important; /* border lives on the wrapper — see note below */
}

body.dark-mode #mobile-drawing-canvas { background: #121212; }

#mobile-drawing-panel > div:last-child { flex: 0 0 auto; display: flex; gap: 4px; }

#mobile-drawing-panel > div:last-child button {
    flex: 1;
    height: 34px;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    font-size: 0.8rem;
    border-radius: 6px;
}

/* NOTE on canvas borders: with box-sizing:border-box, a border directly
   on a canvas element makes getBoundingClientRect() report a box larger
   than the actual paintable area, offsetting every drawn point toward
   the top-left of the real cursor/touch position. The border is kept on
   the wrapper (#mobile-drawing-panel / #drawing-container) instead, and
   removed from the canvas itself, so rect == the canvas's real paint area. */
#drawing-canvas { border: none !important; }

/* ========== DESKTOP CONTROLS (floating panels) ========== */
#controls-container {
    position: fixed;
    top: 80px;
    right: 20px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    z-index: 100;
}

#controls-container button {
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: 2px solid #031427;
    background: white;
    font-size: 24px;
    cursor: pointer;
    transition: all 0.3s ease;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

#controls-container button:hover { transform: scale(1.1); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); }

body.dark-mode #controls-container button { background: #2d2d2d; border-color: #444; }

#toggle-opponent-btn {
    display: none;
    padding: 0.75rem 1.5rem;
    border: 2px solid #031427;
    border-radius: 10px;
    background: #007bff;
    font-size: 1rem;
    font-weight: bold;
    color: white;
    cursor: pointer;
    transition: all 0.3s ease;
    margin: 0 auto;
    width: fit-content;
}

#toggle-opponent-btn:hover {
    background: #0056b3;
    transform: translateY(-2px);
    box-shadow: 0 4px 8px rgba(0, 123, 255, 0.3);
}

body.dark-mode #toggle-opponent-btn { background: #0056b3; border-color: #444; }

/* Drawing Container (desktop floating) */
#drawing-container {
    position: fixed;
    top: 80px;
    right: 80px;
    background: white;
    border-radius: 10px;
    border: 2px solid #031427;
    padding: 1rem;
    box-shadow: 0 8px 25px rgba(3, 20, 39, 0.15);
    z-index: 99;
}

#drawing-canvas {
    background-color: white;
    border-radius: 8px;
    cursor: crosshair;
}

body.dark-mode #drawing-canvas { background-color: #121212; }

#tool-controls { display: flex; gap: 0.5rem; margin-top: 0.5rem; justify-content: center; }

.tool-button {
    padding: 0.5rem 1rem;
    border: 2px solid #031427;
    border-radius: 8px;
    background: #f8f9fa;
    cursor: pointer;
    font-size: 1.2rem;
    transition: all 0.3s ease;
}

.tool-button.active { background: #082f59; color: white; }

/* Calculator Container (desktop floating) */
#calculator-container {
    position: fixed;
    top: 80px;
    right: 80px;
    background: white;
    border-radius: 10px;
    border: 2px solid #031427;
    padding: 1rem;
    box-shadow: 0 8px 25px rgba(3, 20, 39, 0.15);
    z-index: 99;
}

#calculator { width: 250px; }

#calc-display {
    width: 100%;
    padding: 1rem;
    margin-bottom: 0.5rem;
    border: 2px solid #031427;
    border-radius: 8px;
    background: #f8f9fa;
    font-size: 1.5rem;
    text-align: right;
    color: #082f59;
}

#calc-buttons { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.5rem; }

.calc-btn {
    padding: 1rem;
    border: 2px solid #031427;
    border-radius: 8px;
    background: #f8f9fa;
    color: #082f59;
    font-size: 1.2rem;
    font-weight: bold;
    cursor: pointer;
    transition: all 0.3s ease;
}

.calc-btn:hover { background: #143354; color: white; }

.calc-btn.calc-op { background: #007bff; color: white; border-color: #007bff; }
.calc-btn.calc-equal { background: #28a745; color: white; border-color: #28a745; }

.calc-btn.calc-clear {
    background: #DC4325;
    color: white;
    border-color: #DC4325;
    grid-column: span 4;
}


/* ========== MODALS & OVERLAYS ========== */
.modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(3, 20, 39, 0.8);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

body.dark-mode .modal { background: rgba(0, 0, 0, 0.8); }

.modal-content {
    background: white;
    border-radius: 15px;
    border: 2px solid #031427;
    padding: 2rem;
    max-width: 500px;
    width: 90%;
    max-height: 80vh;
    overflow-y: auto;
    animation: modalSlideIn 0.3s ease-out;
    box-shadow: 0 8px 25px rgba(3, 20, 39, 0.3);
}

body.dark-mode .modal-content { background: #2d2d2d; border-color: #444; color: #ffffff; }

#results-title { text-align: center; color: #082f59; margin-bottom: 1.5rem; font-weight: bold; }
body.dark-mode #results-title { color: #ffffff; }

#winner-announcement {
    text-align: center;
    font-size: 1.3rem;
    font-weight: bold;
    margin-bottom: 1.5rem;
    padding: 1rem;
    border-radius: 10px;
    border: 2px solid;
}

#winner-announcement.win  { background: #d4edda; color: #155724; border-color: #28a745; }
#winner-announcement.lose { background: #f8d7da; color: #721c24; border-color: #DC4325; }
#winner-announcement.draw { background: #e8f4fd; color: #0056b3; border-color: #007bff; }

body.dark-mode #winner-announcement.draw { background: #1a2a3a; color: #66b3ff; border-color: #0056b3; }

#final-scores { margin-bottom: 1.5rem; }

.score-row {
    display: flex;
    justify-content: space-between;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    background: #f8f9fa;
    border: 2px solid #031427;
    border-radius: 10px;
}

body.dark-mode .score-row { background: #404040; border-color: #555; }

.player-name,
.player-score { font-weight: bold; color: #082f59; }

body.dark-mode .player-name,
body.dark-mode .player-score { color: #ffffff; }

#game-stats { margin-bottom: 1.5rem; }

.stat-row {
    display: flex;
    justify-content: space-between;
    padding: 0.5rem 0;
    border-bottom: 2px solid #031427;
}

body.dark-mode .stat-row { border-bottom-color: #444; }

#pattern-reveal {
    background: #f8f9fa;
    padding: 1rem;
    border: 2px solid #031427;
    border-radius: 10px;
    margin-bottom: 1.5rem;
}

body.dark-mode #pattern-reveal { background: #2d2d2d; border-color: #444; }

#pattern-reveal h4 { margin-top: 0; color: #082f59; font-weight: bold; }
body.dark-mode #pattern-reveal h4 { color: #ffffff; }

#final-pattern-display {
    font-family: 'Courier New', monospace;
    font-size: 1.1rem;
    font-weight: bold;
    margin: 0.5rem 0;
    padding: 0.5rem;
    background: white;
    border: 2px solid #031427;
    border-radius: 6px;
    color: #082f59;
}

body.dark-mode #final-pattern-display { background: #404040; border-color: #555; color: #ffffff; }

/* ── End-of-game actions ──────────────────────────────────────────────────
   These carried .primary-button / .secondary-button, whose base styling was
   lost when the lobby CSS around it was rewritten — leaving three grey
   browser-default buttons with square corners in the middle of a rounded
   navy design. Rebuilt in the same language as the lobby choices and the
   welcome card's start button.

   They are also ranked rather than equal: Rematch is what most people want
   after a game, New Opponent is the alternative, and Main Menu is a way out
   rather than a suggestion. */
#results-actions {
    display: flex;
    flex-direction: column;
    gap: 8px;
    justify-content: center;
    margin-top: 14px;
}

.result-action {
    display: block;
    width: 100%;
    padding: 12px 14px;
    border: 1px solid #d8e2ee;
    border-radius: 12px;
    background: #f4f8fc;
    color: inherit;
    font-family: inherit;
    font-size: 0.95rem;
    font-weight: bold;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s, transform 0.08s, opacity 0.15s;
}
.result-action:hover:not(:disabled) { background: #e8f0f9; border-color: #b9cee6; }
.result-action:active:not(:disabled) { transform: translateY(1px); }
.result-action:focus-visible { outline: 2px solid #1e5fa8; outline-offset: 2px; }
.result-action:disabled { opacity: 0.45; cursor: not-allowed; }

.result-action-primary {
    background: #082f59;
    border-color: #082f59;
    color: #fff;
}
.result-action-primary:hover:not(:disabled) { background: #0d3f75; border-color: #0d3f75; }

/* A way out, not a suggestion — no border, no fill. */
.result-action-quiet {
    background: none;
    border-color: transparent;
    font-weight: normal;
    font-size: 0.85rem;
    opacity: 0.7;
    padding: 8px;
}
.result-action-quiet:hover:not(:disabled) { background: none; border-color: transparent; opacity: 1; }

body.dark-mode .result-action { background: #2f2f2f; border-color: #3f3f3f; }
body.dark-mode .result-action:hover:not(:disabled) { background: #383838; border-color: #4d4d4d; }
body.dark-mode .result-action-primary { background: #1e5fa8; border-color: #1e5fa8; }
body.dark-mode .result-action-primary:hover:not(:disabled) { background: #2a74c9; border-color: #2a74c9; }
body.dark-mode .result-action-quiet { background: none; border-color: transparent; }

/* ========== LOADING SPINNER ========== */
#loading-spinner {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(3, 20, 39, 0.8);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    z-index: 1001;
}

.spinner {
    width: 50px;
    height: 50px;
    border: 4px solid rgba(255, 255, 255, 0.3);
    border-top: 4px solid white;
    border-radius: 50%;
    animation: spinAnim 1s linear infinite;
}

.loading-text { color: white; font-size: 1.1rem; font-weight: bold; margin-top: 1rem; }

/* ========== TOAST MESSAGES ========== */
.error-toast,
.success-toast {
    position: fixed;
    top: 20px;
    right: 20px;
    padding: 1rem 1.5rem;
    border-radius: 10px;
    border: 2px solid;
    color: white;
    font-weight: bold;
    z-index: 1002;
    display: flex;
    align-items: center;
    gap: 1rem;
    animation: slideInRight 0.3s ease-out;
}

.error-toast { background: #DC4325; border-color: #B8341F; }
.success-toast { background: #28a745; border-color: #218838; }

.error-toast button,
.success-toast button {
    background: none;
    border: none;
    color: white;
    font-size: 1.2rem;
    font-weight: bold;
    cursor: pointer;
    width: 20px;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
}

.error-toast button:hover,
.success-toast button:hover { background: rgba(255, 255, 255, 0.2); }

/* ========== OPPONENT MINI INFO / EYE TOGGLE (mobile feature) ========== */
#opponent-mini-info {
    display: none;
    gap: 1rem;
    justify-content: center;
    align-items: center;
    padding: 2px 8px 4px;
    font-size: 0.75rem;
    font-weight: 600;
}

#opponent-mini-info span { color: #e03535; }

#eye-opponent-btn {
    display: none;
    align-items: center;
    background: none;
    border: 1.5px solid currentColor;
    border-radius: 6px;
    padding: 2px 7px;
    font-size: 1rem;
    cursor: pointer;
    line-height: 1;
    flex-shrink: 0;
    opacity: 0.7;
    transition: opacity 0.15s ease;
    margin-left: 6px;
}

#eye-opponent-btn:hover,
#eye-opponent-btn.active { opacity: 1; }

#opponent-score-strip {
    display: none;
    align-items: center;
    gap: 0.5rem;
    padding: 2px 0.5rem 4px;
    font-size: 0.72rem;
    font-weight: 600;
    color: #082f59;
    border-bottom: 1px solid rgba(3, 20, 39, 0.1);
    margin-bottom: 2px;
}

#opp-strip-label { opacity: 0.65; }
#opp-strip-score { color: #DB4325; }
#opp-strip-row { opacity: 0.65; margin-left: auto; }

body.dark-mode #opponent-score-strip { color: #ccc; }
body.dark-mode #opp-strip-score { color: #ff7f7f; }

/* ════════════════════════════════════════════════════════════
   DESKTOP (≥ 769px)

   #game-main is scaled via a JS-computed `zoom` factor (see
   fitGameToViewport() in multiplayer.js) so the game grids +
   controls always fit the viewport height without cropping.

   FIX HISTORY:
   - Originally 32 separate 40px-tall media-query breakpoints each
     jumping zoom by a fixed step -> snappy, jarring resizing.
   - Replaced with a single hardcoded clamp()/calc() formula
     `clamp(0.45, calc((100vh - 60px) / 910px), 1.6)` that assumed
     a fixed 910px "natural" content height. That was smoother, but
     the 910px guess didn't match the *real* rendered height (which
     varies with pattern column count etc.), so at some window
     heights (e.g. ~620px) the keypad panel's real content still
     didn't fit inside its box and its bottom rows (Enter/Left/
     Right) got clipped by overflow:hidden.
   - Now: CSS just sets a neutral `zoom: 1` baseline. The actual
     zoom factor is computed and applied in JS by measuring the
     TRUE rendered content height (main.scrollHeight) against the
     real available space (wrapper.clientHeight) every time it
     could change (resize, pattern load, entering the game). This
     is correct at any window size instead of only near a guessed
     constant. #game-wrapper also gets overflow-y:auto as a safety
     net so nothing is ever silently clipped/unreachable even in
     an edge case the JS measurement doesn't fully account for.
   ════════════════════════════════════════════════════════════ */
@media (min-width: 769px) {

    #game-wrapper {
        display: flex;
        flex-direction: column;
        height: 100%;
        min-height: 0;
        overflow-y: auto;   /* safety net: scroll instead of clip/cover */
        overflow-x: hidden;
    }

    #game-main {
        flex: 1;
        min-height: 0;      /* lets this shrink below natural content height */
        display: flex;
        flex-direction: column;
        align-items: center;
        padding: 0.5rem;
        gap: 0.5rem;
        overflow: visible;  /* let #game-wrapper's scroll handle overflow */
        transform-origin: top center;
        zoom: 1;            /* JS (fitGameToViewport) sets the real value */
        transition: zoom 0.12s ease-out;
    }

    #players-section {
        flex: 0 0 auto;
        min-height: 0;
        overflow: visible;
    }

    #game-controls-section {
        flex: 0 0 auto;
        min-height: 0;
        height: auto;
        max-height: none;
        max-width: 440px;
        width: 100%;
        margin: 0 auto;
        padding: 0.75rem;
        overflow: visible;
        box-sizing: border-box;
        align-self: center;
    }

    #mobile-keypad-panel,
    #mobile-calculator-panel,
    #mobile-drawing-panel {
        height: 200px;
        max-height: 200px;
        overflow: hidden;
        box-sizing: border-box;
    }

    #mobile-calc-display {
        padding: 6px 8px;
        font-size: 0.9rem;
        margin-bottom: 4px;
    }

    #mobile-drawing-panel > div:last-child button {
        height: 34px;
        font-size: 0.8rem;
    }
}

/* ════════════════════════════════════════════════════════════
   MOBILE (≤ 768px)
   ════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

    html, body {
        margin: 0;
        padding: 0;
        width: 100%;
        height: 100%;
        position: fixed;
        overflow: hidden;
        overscroll-behavior: none;
    }

    * { touch-action: manipulation; }

    /* ---- Hide desktop-only UI ----
       #donation-toggle used to be in this list: it was `position: fixed`
       bottom-right and landed on top of the keypad on a phone. It is a header
       icon now, sitting inline beside #rules-toggle and #sound-toggle, which
       were never hidden here — so hiding it just made the support button
       unreachable on mobile. */
    #controls-container,
    #drawing-container,
    #calculator-container,
    #toggle-notes,
    #toggle-calculator,
    #toggle-opponent-btn,
    #vs-separator,
    #opponent-section,
    #multiplayer-keypad-container {
        display: none !important;
    }

    #dark-mode-toggle { display: inline-block; }

    /* ---- Header ---- */
    #site-header { height: 48px; padding: 6px 12px; flex-shrink: 0; }
    .logo img { height: 32px; }
    .nav-links a { font-size: 0.8rem; padding: 0 0.5rem; }

    /* ---- Connection overlay / lobby ---- */
    #connection-overlay {
        top: 48px;
        height: calc(100dvh - 48px);
        padding: 1rem;
        padding-top: 2rem;
        align-items: flex-start;
    }

    #connection-content {
        margin: 0 auto;
        width: 100%;
        max-width: 420px;
        padding: 1.25rem;
    }

    #connection-content h1 { font-size: 1.4rem; margin-bottom: 1rem; }

    #player-name-input,
    #room-code-input {
        padding: 0.6rem;
        font-size: 0.95rem;
        margin-bottom: 0.6rem;
    }

    .connection-buttons { gap: 0.6rem; margin-bottom: 0.8rem; }


    #join-room-section { padding-top: 0.8rem; }

    /* ---- Game viewport ---- */
    #multiplayer-game-interface {
        position: fixed;
        top: 48px;
        left: 0;
        right: 0;
        bottom: 0;
        height: calc(100dvh - 48px);
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    #game-wrapper {
        flex: 1;
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
        height: 100%;
    }

    #game-main {
        flex: 1;
        display: flex;
        flex-direction: column;
        justify-content: flex-start;
        align-items: stretch;
        min-height: 0;
        height: 100%;
        overflow: hidden;
        padding: 0;
        margin: 0;
        gap: 0;
        zoom: 1; /* reset desktop zoom */
    }

    /* ---- Players section: shrinks to content ---- */
    #players-section {
        flex: 0 0 auto;
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
        padding: 0.3rem;
        margin: 0;
        justify-content: flex-start;
    }

    #player-section {
        flex: 1;
        display: flex;
        flex-direction: column;
        min-height: 0;
        max-height: 100%;
        padding: 0.5rem;
        margin: 0;
        overflow: hidden;
    }

    .game-section {
        width: 100%;
        max-width: 100%;
        height: 100%;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    .section-header {
        flex: 0 0 auto;
        padding: 0.3rem 0.4rem;
        margin-bottom: 0.25rem;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }

    .section-header.combined-bar {
        padding: 0.25rem 0.5rem;
        margin-bottom: 0.25rem;
        gap: 0.5rem;
        flex-wrap: nowrap;
        align-items: center;
    }

    .section-header.combined-bar h2 {
        font-size: 1rem;
        margin: 0;
        white-space: nowrap;
        flex-shrink: 0;
    }

    .section-header h3 { font-size: 0.8rem; }
    .score-display { font-size: 0.7rem; }

    .top-status-bar {
        display: flex;
        gap: 0.4rem;
        font-size: 0.68rem;
        font-weight: 500;
        align-items: center;
        flex-wrap: nowrap;
        overflow: hidden;
    }

    #player-status { flex: 0 0 auto; padding: 0.2rem 0.3rem; margin-bottom: 0.3rem; }
    #player-pattern-info { flex: 0 0 auto; padding: 0.3rem; margin-bottom: 0.3rem; }

    /* ---- Grid: constrained to available space ---- */
    #player-game-container {
        flex: 0 0 auto;
        display: grid;
        gap: 2px;
        justify-content: center;
        align-content: start;
        width: 100%;
        min-height: 0;
        height: fit-content;
        padding: 0.2rem 0;
        overflow: hidden;
    }

    .box {
        width: clamp(32px, 9vw, 48px);
        height: clamp(32px, 9vw, 48px);
        font-size: clamp(12px, 3.2vw, 17px);
        border-radius: 5px;
        line-height: 1;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    #player-stats { flex: 0 0 auto; margin-top: 0.25rem; gap: 0.25rem; }
    #player-stats > div { padding: 0.2rem 0.25rem; font-size: 0.65rem; }

    /* ---- Game controls: fills remaining space ---- */
    #game-controls-section {
        flex: 1;
        height: auto;
        max-height: none;
        width: 100%;
        max-width: 100%;
        margin: 0.3rem 0 0 0;
        padding: 0.3rem;
        border-radius: 0;
        border-left: none;
        border-right: none;
        border-bottom: none;
        overflow: hidden;
        display: flex;
        flex-direction: column;
        box-sizing: border-box;
        min-height: 0;
        padding-bottom: env(safe-area-inset-bottom, 0.3rem);
    }

    #game-info-bar {
        display: flex;
        justify-content: space-between;
        align-items: center;
        flex: 0 0 auto;
        gap: 0.25rem;
        margin-bottom: 0.2rem;
    }

    #room-id-display { font-size: 0.6rem; }
    #game-timer { font-size: 0.5rem; }

    #mobile-bottom-cycle-btn {
        display: block;
        flex: 0 0 auto;
        width: 100%;
        padding: 4px 8px;
        font-size: 0.9rem;
        height: 30px;
        min-width: 34px;
        border-radius: 6px;
        margin: 0 0 0.2rem 0;
    }

    .bottom-panel { display: none; flex-shrink: 0; }
    .bottom-panel.active { display: flex; flex-direction: column; gap: 0.2rem; }

    #mobile-keypad-panel,
    #mobile-calculator-panel,
    #mobile-drawing-panel {
        flex: 1;
        min-height: 0;
        overflow: hidden;
        box-sizing: border-box;
    }

    #mobile-numpad-grid { gap: clamp(3px, 1.2vw, 7px); }
    #mobile-nav-row { gap: clamp(3px, 1.2vw, 7px); margin-top: clamp(3px, 1vw, 6px); }

    .mobile-num-btn { font-size: clamp(1rem, 4.5vw, 1.4rem); border-radius: clamp(4px, 1.2vw, 8px); }

    .mobile-nav-btn {
        font-size: clamp(0.65rem, 2.5vw, 0.9rem);
        border-radius: clamp(4px, 1.2vw, 8px);
        height: clamp(28px, 5vh, 44px);
        flex: 1;
    }

    #mobile-calc-display {
        padding: clamp(3px, 0.8vw, 6px);
        font-size: clamp(0.7rem, 2.5vw, 0.9rem);
        margin-bottom: clamp(2px, 0.6vw, 4px);
        height: clamp(26px, 4vh, 34px);
    }

    #mobile-calc-buttons { gap: clamp(3px, 1.2vw, 7px); }

    .mobile-calc-btn { font-size: clamp(0.8rem, 3.2vw, 1rem); border-radius: clamp(4px, 1.2vw, 8px); }
    .mobile-calc-btn.clear { grid-column: span 4; }

    #mobile-drawing-panel > div:last-child {
        gap: clamp(3px, 1.2vw, 7px);
        margin-top: clamp(3px, 1vw, 6px);
    }

    #mobile-drawing-panel > div:last-child button {
        height: clamp(28px, 5vh, 44px);
        font-size: clamp(0.6rem, 2.5vw, 0.85rem);
        border-radius: clamp(4px, 1.2vw, 8px);
        flex: 1;
    }

    #game-actions { display: flex; gap: 0.2rem; flex-shrink: 0; }

    #game-actions .action-button {
        flex: 1;
        padding: 0.35rem 0.15rem;
        font-size: 0.65rem;
        border-radius: 5px;
    }

    /* ---- Opponent mini info / eye toggle ---- */
    #opponent-mini-info { display: flex; }
    #eye-opponent-btn { display: inline-flex; }
    #opponent-score-strip { display: flex; }

    #opponent-section.eye-active {
        position: fixed;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        z-index: 500;
        background: var(--background-color, #fff);
        display: flex !important;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        padding: 1rem;
        box-sizing: border-box;
        gap: 0.5rem;
    }

    #opponent-section.eye-active .section-header { display: flex; }

    #opponent-section.eye-active::before {
        content: "Opponent Progress";
        font-size: 0.95rem;
        font-weight: 700;
        opacity: 0.75;
    }

    #opponent-section.eye-active::after {
        content: "Tap \1F441\FE0F again to close";
        font-size: 0.72rem;
        opacity: 0.45;
        margin-top: 0.25rem;
    }

    #player-section > #opponent-game-container {
        display: grid;
        margin: 0 auto;
        justify-content: center;
    }

    #player-section > #opponent-game-container .opponent-box {
        width: clamp(32px, 9vw, 48px);
        height: clamp(32px, 9vw, 48px);
        font-size: clamp(12px, 3.2vw, 17px);
    }

    /* ---- Landscape adjustment ---- */
    @media (orientation: landscape) {
        #players-section { flex: 72; }
        #game-controls-section { flex: 28; }
        .box { max-width: 58px; max-height: 58px; font-size: clamp(14px, 4.5vh, 20px); }
    }
}
/* ════════════════════════════════════════════════════════════════════════
   LEGACY LAYOUT OVERRIDES

   styles.css carries a block of `#game-wrapper { zoom: … }` media queries
   (0.466 – 1.110, keyed off viewport height) from the single-player layout.
   Its guard is `@media (max-width: 7000px)`, which matches every screen ever
   made, so it applied here too — and the multiplayer mobile layout below is
   already a flex/dvh design that sizes itself. The two fought each other:

     • zoom < 1 shrank the contents of a fixed-height container, leaving
       ~120px of dead space under the controls on every phone (20% of the
       screen) and crushing the numpad to 116x20px buttons — well under the
       44px minimum for a reliable tap.
     • zoom > 1 (1.11, any viewport ≥ 870px tall — e.g. iPad) pushed the
       nav row 108px past the bottom of an `overflow: hidden` container, so
       the Enter / Left / Right buttons could not be reached at all.
     • CSS `zoom` is non-standard and WebKit before Safari 18 does not fold
       it into getBoundingClientRect(), while clientX/clientY are always in
       the zoomed space. Any canvas that maps pointer -> bitmap through a
       rect (the drawing pad) therefore mapped through a 1/zoom scale error
       that grew with distance from the pad's top-left corner. That is the
       iOS "the ink isn't under my finger" bug.

   The multiplayer page does not need the zoom; neutralise it here rather
   than in styles.css, which single-player still depends on.
   ════════════════════════════════════════════════════════════════════════ */
#game-wrapper {
    zoom: 1 !important;

    /* styles.css also pins this with `overflow: hidden !important` and
       `justify-content: center !important`. Together those meant that on a
       short window (a 1280x650 laptop, say) the layout's ~737px of content
       was centred and then clipped, putting the Enter / Left / Right row
       147px below the bottom edge with no way to scroll to it. `safe center`
       keeps the centring while it fits and falls back to top-aligned when it
       does not; the scroll is the belt-and-braces so nothing is ever
       unreachable even if a pattern renders taller than expected. */
    justify-content: center !important;
    justify-content: safe center !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
}

/* styles.css fixes #game-wrapper to `top: 60px` for the single-player
   header, but this page's header is 48px and #multiplayer-game-interface is
   already positioned against it — the mismatch left a 12px dead band under
   the header on every phone. */
@media (max-width: 768px) {
    #game-wrapper {
        top: 48px !important;
        height: calc(100dvh - 48px) !important;
    }
}
@media (orientation: landscape) and (max-height: 560px) {
    #game-wrapper {
        top: 44px !important;
        height: calc(100dvh - 44px) !important;
    }
}

/* The lobby centres its content in a fixed-height flex box with no scroll.
   In landscape the content is taller than the box, so the overflow spilled
   past both ends with no way to reach it — on a phone held sideways the
   Ready button sat ~140px below the fold and the game could not be started
   at all. Let it scroll, and never centre content that is taller than the
   box (`flex-start` + `auto` margins keeps short content centred). */
#connection-overlay {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    align-items: flex-start;
}
#connection-content { margin-top: auto; margin-bottom: auto; }

@media (max-width: 768px) {
    /* Minimum comfortable tap targets. These are floors, not fixed sizes —
       the clamp()-based rules above still grow the buttons when there is
       room. Without a floor the grid's `1fr` rows collapse to whatever is
       left over, which is how the keypad ended up 20px tall. */
    .mobile-num-btn  { min-height: 34px; }
    .mobile-nav-btn  { min-height: 34px; }
    .mobile-calc-btn { min-height: 30px; }
    #mobile-bottom-cycle-btn { min-height: 32px; }
    #eye-opponent-btn { min-width: 34px; min-height: 30px; }

    /* The pad needs enough height to write a digit in. Its backing store is
       sized from this box (see initializePanelDrawing), so a taller box is
       also a higher-resolution one. */
    #mobile-drawing-canvas { min-height: 120px; }
}

/* ── Grid cells sized from the space that is actually available ──────────
   `.box` had a fixed width (45px, or clamp(32px, 9vw, 48px) on mobile) while
   the grid tracks were `1fr`. The grid's intrinsic width was therefore
   columns x box-width regardless of the container, so a 7-column pattern
   wanted 348px inside a 329px container on a 375px-wide phone and the last
   column was clipped away by `overflow: hidden`. sizeGameGrids() in
   multiplayer.js measures the container and publishes the result as
   --box-size; these rules consume it and fall back to the old value when the
   JS has not run yet. */
.game-grid { --box-size: 45px; }

/* Fill the track rather than carrying an independent width. styles.css
   pins `.box { width: var(--box-size, 48px) !important }`, so these need
   both the extra specificity and !important to win — and having the track
   be the only thing that decides the geometry is what makes the size
   computable in one pass. */
.game-grid .box,
.game-grid .opponent-box {
    width: 100% !important;
    height: 100% !important;
    min-width: 0 !important;
    min-height: 0 !important;
    max-width: none !important;
    max-height: none !important;
    line-height: 1 !important;
    font-size: calc(var(--box-size) * 0.42) !important;
    box-sizing: border-box;
}

/* ── Landscape phones ────────────────────────────────────────────────────
   The mobile layout is gated on `max-width: 768px`, but a phone held
   sideways is wide and short (an iPhone 13 is 844x390). It fell through to
   the desktop layout, which needs ~740px of height: the keypad and the
   Enter / Left / Right row ended up 425px below the bottom of an
   `overflow: hidden` container, i.e. the game was unplayable in landscape.
   Short viewports get the fixed-viewport treatment too, with the grid and
   the controls side by side so a wide, short screen is used the way round
   it actually is. */
@media (orientation: landscape) and (max-height: 560px) {
    #site-header { height: 44px; padding: 4px 12px; }
    .logo img { height: 28px; }

    /* These are the game-area panels, which have no room on a landscape
       phone. The header icons stay: #donation-toggle is inline in the header
       now, not the fixed bottom-right button this rule was written for. */
    #controls-container,
    #drawing-container,
    #calculator-container { display: none !important; }

    #connection-overlay { top: 44px; height: calc(100dvh - 44px); padding: 0.75rem; }

    #multiplayer-game-interface {
        position: fixed;
        top: 44px;
        left: 0;
        right: 0;
        bottom: 0;
        height: calc(100dvh - 44px);
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    #game-wrapper {
        flex: 1;
        display: flex;
        min-height: 0;
        height: 100%;
        overflow: hidden;
        padding: 4px !important;
        gap: 6px !important;
    }

    #game-main {
        flex: 1;
        display: flex;
        flex-direction: row;       /* grid | controls, side by side */
        align-items: stretch;
        justify-content: center;   /* keep the two columns together */
        min-height: 0;
        height: 100%;
        overflow: hidden;
        padding: 0;
        margin: 0;
        gap: 8px;
    }

    #players-section {
        flex: 0 1 auto;
        max-width: 58%;
        display: flex;
        flex-direction: column;
        min-width: 0;
        min-height: 0;
        overflow: hidden;
        padding: 0;
    }

    #opponent-section, #vs-separator { display: none !important; }

    #player-section {
        flex: 1;
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
        padding: 4px;
        margin: 0;
    }

    .section-header.combined-bar { padding: 2px 6px; margin-bottom: 2px; gap: 0.4rem; }
    .section-header.combined-bar h2 { font-size: 0.85rem; }
    .top-status-bar { font-size: 0.6rem; gap: 0.3rem; }
    #opponent-score-strip { display: flex; font-size: 0.6rem; }
    #eye-opponent-btn { display: inline-flex; }

    #player-game-container {
        flex: 1;
        display: grid;
        gap: 2px;
        justify-content: center;
        align-content: center;
        min-height: 0;
        overflow: hidden;
        padding: 0;
    }

    #game-controls-section {
        flex: 1 1 42%;
        /* The desktop block sets `align-self: center`, which in this row
           layout stops the controls column stretching and left ~70px of the
           screen unused under the keypad. */
        align-self: stretch;
        display: flex;
        flex-direction: column;
        min-width: 0;
        min-height: 0;
        max-width: 380px;
        height: auto;
        max-height: none;
        margin: 0;
        padding: 4px;
        border: none;
        overflow: hidden;
        box-sizing: border-box;
    }

    #game-info-bar { flex: 0 0 auto; margin-bottom: 6px; }
    #room-id-display { font-size: 0.55rem; }

    #mobile-bottom-cycle-btn {
        flex: 0 0 auto;
        width: 100%;
        height: 28px;
        min-height: 28px;
        font-size: 0.7rem;
        padding: 2px 6px;
        margin: 0 0 3px 0;
    }

    #mobile-keypad-panel,
    #mobile-calculator-panel,
    #mobile-drawing-panel {
        flex: 1;
        min-height: 0;
        overflow: hidden;
        box-sizing: border-box;
    }

    #mobile-numpad-grid { gap: 3px; }
    #mobile-nav-row { gap: 3px; margin-top: 3px; }
    .mobile-num-btn  { font-size: 0.85rem; min-height: 22px; }
    .mobile-nav-btn  { font-size: 0.6rem;  min-height: 22px; height: auto; }
    .mobile-calc-btn { font-size: 0.7rem;  min-height: 20px; }
    #mobile-calc-display { height: 22px; font-size: 0.7rem; margin-bottom: 3px; }
    #mobile-drawing-canvas { min-height: 60px; }
    #mobile-drawing-panel > div:last-child button { height: 24px; min-height: 24px; font-size: 0.6rem; }
}

/* ── Short desktop windows ───────────────────────────────────────────────
   The desktop controls column has a ~300px natural height that does not
   respond to the viewport, so on a 1024x600 laptop window it pushed the
   Enter row past the bottom edge. Give the keypad a smaller floor when
   there is not much height to go round; the grid fit in multiplayer.js
   handles the rest. */
@media (min-width: 769px) and (max-height: 700px) {
    #game-controls-section { padding: 0.4rem; }
    #mobile-numpad-grid { gap: 4px; }
    #mobile-nav-row { gap: 4px; margin-top: 4px; }
    .mobile-num-btn  { min-height: 26px; font-size: 1rem; }
    .mobile-nav-btn  { min-height: 26px; height: auto; font-size: 0.75rem; }
    .mobile-calc-btn { min-height: 24px; font-size: 0.8rem; }
    #mobile-bottom-cycle-btn { height: 28px; min-height: 28px; }
    #mobile-drawing-canvas { min-height: 100px; }
}

/* A second tier for very short desktop windows (a 1024x600 laptop). Without
   it the controls column still ran ~27px past the bottom edge. */
@media (min-width: 769px) and (max-height: 620px) {
    #game-controls-section { padding: 0.25rem; }
    #game-info-bar { margin-bottom: 0.1rem; }
    #mobile-numpad-grid { gap: 3px; }
    #mobile-nav-row { gap: 3px; margin-top: 3px; }
    .mobile-num-btn  { min-height: 22px; font-size: 0.9rem; }
    .mobile-nav-btn  { min-height: 22px; font-size: 0.7rem; }
    .mobile-calc-btn { min-height: 20px; }
    #mobile-bottom-cycle-btn { height: 26px; min-height: 26px; margin-bottom: 0.1rem; }
    #mobile-drawing-canvas { min-height: 80px; }
}

/* ── Info bar above the panels ───────────────────────────────────────────
   Two problems, both visible on a phone in dark mode:

   1. #room-id-display is #082f59 with no dark-mode rule, i.e. navy text on
      the #2d2d2d dark panel — 1.02:1 contrast, effectively invisible.
   2. The row is `justify-content: space-between` with a long room code on
      one side and the "🔄 Switch to …" button on the other. Neither side
      could shrink below its min-content width, so on a narrow phone the
      button was pushed past the right edge of its container. Let the room
      code truncate and keep the button intact — it is the one you tap. */
#room-info-header {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

#room-id-display {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

body.dark-mode #room-id-display { color: #8ab4dd; }

#timer-and-switch {
    flex: 0 1 auto;
    min-width: 0;
    max-width: 62%;
}

#mobile-bottom-cycle-btn {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    box-sizing: border-box;
    /* `overflow: hidden` clips vertically as well, and the button is given a
       fixed height on short screens. Centre the label on a 1.0 line box so
       the ellipsis only ever trims horizontally. */
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
}

#mobile-bottom-cycle-btn::before { content: none; }

/* Respect the notch/home-indicator insets so nothing sits under them. */
@media (max-width: 768px) {
    #game-controls-section {
        padding-left:  max(0.3rem, env(safe-area-inset-left));
        padding-right: max(0.3rem, env(safe-area-inset-right));
    }
    #players-section {
        padding-left:  max(0.3rem, env(safe-area-inset-left));
        padding-right: max(0.3rem, env(safe-area-inset-right));
    }
}

/* The pad maps its backing store onto its content box, and offsetX/offsetY are
   measured from the padding box. Keeping both at zero means the two are the
   same box, so the pointer mapping stays an identity. */
#mobile-drawing-canvas,
#drawing-canvas {
    padding: 0 !important;
    border-width: 0 !important;
}

/* ════════════════════════════════════════════════════════════════════════
   STABLE CONTROLS COLUMN

   The keypad, calculator and drawing pad have different natural heights, so
   switching between them with `display: none` changed how much room was left
   above and the number grid visibly resized on every switch.

   All three now live in the same grid cell and are switched with
   `visibility`, so they are always all in the layout: the controls column is
   permanently as tall as the tallest panel, and nothing above it moves. It
   also means the drawing pad has a real box even while it is hidden, so its
   backing store is already the right size the moment you switch to it.
   ════════════════════════════════════════════════════════════════════════ */
#game-controls-section {
    display: grid !important;
    grid-template-rows: auto 1fr;
    grid-template-columns: 100%;
    align-content: stretch;
}

#game-controls-section > #game-info-bar {
    grid-row: 1;
    grid-column: 1;
}

#game-controls-section > .bottom-panel {
    grid-row: 2;
    grid-column: 1;
    display: flex !important;
    flex-direction: column;
    min-height: 0;
    min-width: 0;
    visibility: hidden;
    pointer-events: none;
}

#game-controls-section > .bottom-panel.panel-active {
    visibility: visible;
    pointer-events: auto;
}

/* The badge spins on an 8s loop, so "VS" spends most of its time sideways or
   upside down. A label you cannot read is not a decoration. */
.vs-text { animation: none; }

/* The cycle button sits in the info bar; give it room for its label rather
   than letting it ellipsise down to a few characters. */
#mobile-bottom-cycle-btn { min-width: 5.5rem; }
@media (min-width: 769px) {
    #timer-and-switch { max-width: none; }
    #mobile-bottom-cycle-btn { min-width: 8rem; padding-left: 0.6rem; padding-right: 0.6rem; }
}

/* ── iOS: never let the controls sit under the Safari toolbar ────────────
   `dvh` tracks the viewport as the URL bar shows and hides, but it is the
   value at layout time and iOS does not always recompute it before you need
   it — which is how the bottom of the keypad/calculator ended up underneath
   the address bar. `svh` is the *small* viewport: the height with the browser
   chrome showing. Sizing to that means the controls always fit whatever
   Safari is currently doing; the cost is a strip of background at the bottom
   when the bar is hidden, which is the right trade for buttons you must be
   able to reach. Declared after the vh/dvh versions so older engines keep
   the fallback. */
@media (max-width: 768px) {
    /* 48px is the mobile header height. Scoped to mobile: the desktop header
       is 60px, and applying the 48px figure there pushed the interface 12px
       past the bottom of the viewport. */
    #multiplayer-game-interface {
        height: calc(100vh - 48px);
        height: calc(100dvh - 48px);
        height: calc(100svh - 48px);
    }

    #game-wrapper {
        height: calc(100vh - 48px) !important;
        height: calc(100dvh - 48px) !important;
        height: calc(100svh - 48px) !important;
    }
    /* Keep the last row of buttons clear of the home indicator. */
    #game-controls-section {
        padding-bottom: max(0.3rem, env(safe-area-inset-bottom));
    }
}

@media (orientation: landscape) and (max-height: 560px) {
    #multiplayer-game-interface {
        height: calc(100vh - 44px);
        height: calc(100dvh - 44px);
        height: calc(100svh - 44px);
    }
    #game-wrapper {
        height: calc(100vh - 44px) !important;
        height: calc(100dvh - 44px) !important;
        height: calc(100svh - 44px) !important;
    }
}

/* ── Who gives up space first on a short screen ──────────────────────────
   #players-section was `flex: 0 0 auto`, so the number grid took its content
   height first and the controls got whatever was left. On a short viewport
   (an iPhone SE, or any iPhone once Safari's address bar is showing) that
   left the keypad ~108px for 148px of buttons and the calculator 90px for
   166px. The panels do not scroll, so the bottom rows simply rendered
   outside their box — overlapping the nav row, or disappearing under the
   address bar.

   Invert it: the controls are what you have to tap, so they claim the room
   they need and the grid — which is only being read — takes the remainder
   and is sized to fit by sizeGameGrids(). */
@media (max-width: 768px) and (orientation: portrait) {
    /* Share the column by proportion rather than letting either side be
       content-sized. Content-sizing the controls gave them 274px of a 505px
       viewport and left the grid 121px for six rows; content-sizing the grid
       (the original) did the reverse and pushed the keypad off the bottom.
       A 5:4 split leaves both usable at every height, and because each side
       is a share of the free space rather than a function of its own content
       there is no feedback loop in the measurement. */
    #players-section       { flex: 5 1 0%; min-height: 0; }
    #game-controls-section { flex: 4 1 0%; min-height: 0; }
    #player-section        { flex: 1 1 0%; min-height: 0; }

    /* A definite height, so the grid sizing has a real budget to divide up
       rather than a content-derived one that depends on itself. */
    #player-game-container {
        flex: 1 1 0%;
        min-height: 0;
        align-content: center;
    }

    /* No min-height needed: the panels all share one grid cell, so they are
       automatically the same height, and the 4-share above guarantees that
       cell a sensible size. A min-height here would only re-create the
       overflow it was meant to prevent on the shortest screens. */
    #game-controls-section > .bottom-panel { min-height: 0; }

    /* Floors low enough that the rows can actually share a small panel.
       The earlier 34px floor is what made the grid overflow its box. */
    .mobile-num-btn  { min-height: 24px; }
    .mobile-nav-btn  { min-height: 26px; height: auto; }
    .mobile-calc-btn { min-height: 21px; }
    #mobile-calc-display { height: clamp(22px, 3.4vh, 30px); }
    /* Low enough that the drawing panel is never the thing that overflows
       the shared cell on a 454px-tall viewport. */
    #mobile-drawing-canvas { min-height: 88px; }
}

/* ── Desktop: give the grid a real height budget ─────────────────────────
   #players-section was content-sized, so the grid had no definite height and
   sizeGameGrids() had to infer its budget from #game-main's scrollHeight.
   #game-main is flex-stretched, so its scrollHeight is its *stretched*
   height, not its content height — the inferred budget came out ~380px too
   small and the boxes were sized at 26px with 150px of the panel left empty.
   Stretching the section gives the grid a definite box, which is the same
   mechanism the portrait and landscape layouts already use. */
@media (min-width: 769px) {
    /* Zero flex-basis on purpose. With `auto` the item's base size is its own
       content, so the height we are computing feeds back into the budget we
       compute it from and each pass picks a smaller size than the last, all
       the way down to the floor. A zero basis makes the height purely a share
       of the free space, which is independent of the box size. */
    #players-section { flex: 1 1 0%; min-height: 0; align-items: stretch; }

    /* Full-height on purpose, and load-bearing: fittingBoxSize() reads the
       grid's own clientHeight as its budget, so letting the panel size to
       its content makes the budget a function of the size being computed.
       Tried it — the grid collapsed to 123px square. Leave it stretched; the
       dead space under the boxes is dealt with by capping the grid's height
       to the rows it actually holds (see #player-game-container below). */
    #player-section, #opponent-section {
        display: flex;
        flex-direction: column;
        height: fit-content;
        max-height: 100%;
        min-height: 0;
        align-self: flex-start;
        /* Desktop only. As a base rule this also applied on a phone, where
           it stopped the section shrinking past its 280px min-width and
           clipped the board horizontally from 320px to 600px wide. */
        flex: 1 1 0;
    }

    #player-game-container,
    #opponent-game-container {
        flex: 1 1 0%;
        min-height: 0;
        align-content: center;
        justify-content: center;
    }

    #game-controls-section { flex: 0 0 auto; }
}

/* The calculator is the tallest panel (a display plus five button rows), so
   it is the one that runs out of room first on a short phone. Tighten its
   floors there rather than letting it spill out of the shared cell. */
@media (max-width: 768px) and (orientation: portrait) and (max-height: 620px) {
    #mobile-calc-buttons { gap: 3px; }
    .mobile-calc-btn     { min-height: 17px; font-size: 0.75rem; }
    #mobile-calc-display { height: 20px; font-size: 0.7rem; margin-bottom: 2px; }
    #mobile-numpad-grid  { gap: 4px; }
    .mobile-num-btn      { min-height: 20px; }
    .mobile-nav-btn      { min-height: 24px; }
    #mobile-drawing-canvas { min-height: 70px; }
    #mobile-drawing-panel > div:last-child button { height: 26px; min-height: 26px; }
    /* Every pixel counts here: the grid's own padding is the difference
       between six rows fitting and being clipped. */
    #player-game-container { padding: 0; }
}

/* Safety net: if a panel's content genuinely cannot fit the space available
   — a 320x454 viewport with the address bar showing, say — let it scroll
   rather than render its bottom rows outside the box where they cannot be
   tapped. Only engages when the content actually overflows. */
.bottom-panel {
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
#mobile-drawing-panel { overflow-y: hidden; }   /* the pad sizes itself; never scroll it */

/* An original iPhone SE with the address bar showing is 320x454. At that size
   a six-row grid and a five-row calculator genuinely cannot both be roomy.
   The grid is the game and must be fully visible, so the calculator — a
   helper — takes the squeeze. It still fits rather than needing a scroll. */
@media (max-width: 768px) and (orientation: portrait) and (max-height: 500px) {
    #mobile-calc-display { height: 16px; font-size: 0.6rem; margin-bottom: 2px; }
    #mobile-calc-buttons { gap: 2px; }
    .mobile-calc-btn     { min-height: 14px; font-size: 0.62rem; }
}

/* Results modal: three outcomes — rematch the same opponent, find a new one,
   or leave — plus a line of status while a rematch is pending. They stack,
   which is what lets them be ranked; see .result-action above. */

#rematch-status {
    margin-top: 0.75rem;
    text-align: center;
    font-size: 0.85rem;
    min-height: 1.2em;
    color: #082f59;
}
body.dark-mode #rematch-status { color: #8ab4dd; }

@media (max-width: 768px) {
    .result-action { font-size: 0.88rem; padding: 0.6rem 0.7rem; }
}

/* ── Results modal: the buttons must never be the part that scrolls away ──
   `.modal-content` was one scrolling block capped at 80vh, so on a short
   viewport (a landscape phone, a 454px-tall SE) the scores pushed Rematch /
   New Opponent / Main Menu below the fold. You could scroll to them, but a
   button you have to go looking for is a button people think is missing.
   Header and actions are now pinned and only the scores scroll. */
#results-modal .modal-content {
    display: flex;
    flex-direction: column;
    overflow: hidden;                       /* the body scrolls, not the box */
    max-height: 88vh;
    max-height: 88svh;
    padding: 1.25rem;
}

#results-modal #results-header,
#results-modal #results-actions,
#results-modal #rematch-status {
    flex: 0 0 auto;
}

#results-modal #results-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/* Short viewports: trim everything so the whole modal fits without scrolling
   at all in the common cases. */
@media (max-height: 560px) {
    #results-modal .modal-content { padding: 0.7rem; max-height: 94svh; }
    #results-modal #results-title { font-size: 1.1rem; margin: 0 0 0.35rem; }
    #results-modal #winner-announcement { font-size: 0.95rem; padding: 0.4rem; margin-bottom: 0.4rem; }
    #results-modal .score-row { padding: 0.3rem 0.5rem; font-size: 0.85rem; }
    #results-modal #final-scores { gap: 0.3rem; }
    #results-actions { gap: 0.3rem; margin-top: 0.4rem; }
    .result-action { padding: 0.5rem 0.6rem; font-size: 0.8rem; }
    .result-action-quiet { padding: 0.35rem; font-size: 0.72rem; }
    #rematch-status { margin-top: 0.35rem; font-size: 0.72rem; }
}

/* The actions stack now, so a narrow viewport needs nothing special beyond
   the tighter padding set above — the rule that used to squeeze three
   buttons onto one row would fight the stack. */

/* ── Lobby on a short screen ──────────────────────────────────────────────
   The lobby stacks a title, three match buttons, a room-code field, a status
   line and the room panel. At full size that is fine; at 454px tall (an SE
   with the address bar up) or on a phone held sideways it ran ~150px past the
   bottom and the Ready button — the only thing you actually need — was below
   the fold. The overlay scrolls, so it was reachable, but a button you have
   to go looking for reads as a missing button. Compact everything so it
   fits. */
@media (max-height: 640px) {
    #connection-content { padding: 0.8rem; }
    #connection-content h1 { font-size: 1.15rem; margin-bottom: 0.5rem; }

    #player-setup { margin-bottom: 0.55rem; }
    .connection-buttons { gap: 0.4rem; margin-bottom: 0.5rem; }


    #room-code-input { padding: 0.45rem; font-size: 0.85rem; margin-bottom: 0.45rem; }
    #join-room-section { padding-top: 0.45rem; }

    #connection-status {
        font-size: 0.75rem;
        padding: 0.35rem 0.4rem;
        margin: 0.4rem 0;
        line-height: 1.25;
    }

    #room-info { margin-top: 0.4rem; }
    #room-code-display { font-size: 0.78rem; margin-bottom: 0.3rem; }
    #players-list { font-size: 0.75rem; }
    .ready-button { padding: 0.5rem 1rem; font-size: 0.9rem; margin-top: 0.4rem; }
}

/* Shorter still: the match-making buttons are not what you need once you are
   already in a room waiting to start, so let the panel win the space. */
@media (max-height: 500px) {
    #connection-content { padding: 0.55rem; }
    #connection-content h1 { font-size: 1rem; margin-bottom: 0.35rem; }
    .connection-buttons { margin-bottom: 0.35rem; }

    #room-code-input { padding: 0.35rem; font-size: 0.78rem; margin-bottom: 0.3rem; }
    #connection-status { font-size: 0.7rem; margin: 0.3rem 0; }
    .ready-button { padding: 0.4rem 0.9rem; font-size: 0.82rem; }
}

/* Extremely short windows (a 320px-tall browser, a phone in landscape with
   every bar showing). Compacting alone runs out of road, so pin the room
   panel — which holds Ready — and let the match-making controls above it
   scroll instead. Same reasoning as the results modal: the button you need
   must never be the part that scrolls away. */
@media (max-height: 430px) {
    #connection-overlay { align-items: stretch; padding: 0.4rem; }

    #connection-content {
        display: flex;
        flex-direction: column;
        max-height: 100%;
        min-height: 0;
        overflow: hidden;
        margin: 0 auto;
    }

    #connection-content h1 { flex: 0 0 auto; }
    #player-setup {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
    }
    #connection-status,
    #room-info { flex: 0 0 auto; }
}

/* ── Break the canvas feedback loop ───────────────────────────────────────
   A canvas is a replaced element: with `height: auto` the browser derives its
   height from the backing store's intrinsic aspect ratio. We size the backing
   store FROM the element, so the element's height ended up depending on its
   own previous height. Rotating to landscape and back left the pad 204px tall
   where it had been 183px, which grew the controls column and shrank the
   number grid — the same viewport produced a different layout depending on
   how you got there.

   A definite height (flex supplies the real one) stops the intrinsic ratio
   being consulted at all. */
#mobile-drawing-canvas {
    height: 0 !important;
    flex: 1 1 0 !important;
}


/* ── Rules button on mobile multiplayer ───────────────────────────────────
   #rules-toggle is `position: fixed` bottom-right for the desktop layout,
   which is why the mobile blocks used to hide it outright — it landed on top
   of the keypad. Put it back in the header flow instead, so the rules are
   actually reachable on a phone. */
@media (max-width: 768px), (orientation: landscape) and (max-height: 560px) {
    #rules-toggle {
        display: flex !important;
        position: static !important;
        bottom: auto !important; right: auto !important;
        align-items: center; justify-content: center;
        flex-shrink: 0;
        width: auto; height: auto;
        padding: 0 !important;
        font-size: 18px !important;
        background: none !important;
        border: none !important;
        box-shadow: none !important;
    }
}

/* Putting the ? back into the mobile header costs about 24px, which tips the
   row over the edge at 320px (measured: the dark-mode toggle ended at x=321
   in a 320px viewport). Same treatment as the single-player header — take the
   space out of the logo and the nav label rather than the icons. */
/* 355px, not 340px: same WebKit emoji-width issue as the single-player
   header — the row ran 2-9px over between 341 and 348. */
@media (max-width: 355px) {
    #site-header { padding: 0 8px !important; }
    .header-content .logo { margin-right: 6px !important; }
    .header-content .logo img { height: 26px !important; }
    .nav-links a { font-size: 11px !important; }
    #donation-toggle,
    #rules-toggle,
    #sound-toggle,
    #dark-mode-toggle { margin-left: 6px !important; font-size: 16px !important; }
}

/* Same story one size up. The ☕ replaced the X in the header, and unlike the
   X it is not hidden on mobile — so this header carries four icons at widths
   the 768px block only ever tightened for three, and the row ran past the
   right edge between 341px and ~430px (measured: 27px over at 360px, 7px at
   380px). The 340px block below already solves the narrowest case; this
   covers the band above it. */
@media (min-width: 341px) and (max-width: 430px) {
    .header-content .logo img { height: 28px; }
    .nav-links a { font-size: 12px; padding: 0 0.35rem; }
    #donation-toggle,
    #rules-toggle,
    #sound-toggle,
    #dark-mode-toggle { margin-left: 8px; }
    #donation-toggle,
    #rules-toggle,
    #sound-toggle { font-size: 18px; }
}

/* ── Large displays ───────────────────────────────────────────────────────
   #game-main is capped at 1200px, which is right for reading width but meant
   a 1920px monitor gained nothing: the two boards shared ~745px, so a
   6-column grid was held to ~46px boxes by width while the panel had height
   to spare. Give the row more to work with on genuinely wide screens — the
   per-section max-width of 450px still stops any one board getting silly. */
@media (min-width: 1500px) {
    #game-main { max-width: 1500px; }
}
@media (min-width: 1800px) {
    #game-main { max-width: 1680px; }
}

/* #game-main is a column with align-items:center, so #players-section sized
   itself to its content — 745px — no matter how much room the column had.
   Its two boards are `flex: 1 1 0`, so they were dividing a width that was
   itself decided by them, and settled at ~46px boxes. Letting the row span
   the column gives them a real share to grow into, up to the 450px cap each
   already carries. */
@media (min-width: 1200px) {
    #players-section {
        align-self: stretch;
        width: 100%;
        justify-content: center;
    }
}

/* Connection banner: shown while reconnecting, or while waiting for an
   opponent whose connection dropped. Deliberately a thin strip rather than a
   modal — the game underneath is still live and the clock is still running. */
.connection-banner {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: 10001;
    padding: 8px 14px;
    text-align: center;
    font-size: 0.85rem;
    font-weight: bold;
    color: #fff;
    background: #b8860b;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
}
body.dark-mode .connection-banner { background: #8a6508; }
