/* static/css/v3-menu.css - sketch 1 v3, lane "v3-menu" (prefix v3m-).
 *
 * The main menu (templates/menu_v2.html, .mv2-shell.v3-menu) and the pre-lobby
 * (templates/prelobby_v2.html, .pv2-shell.v3-pre): the live game's paint,
 * fitted to one phone screen. STYLE.md 9.1, 9.2, 11.1 to 11.3; the pictures
 * are pitches/menu-lobby/*.png without the counts, the strip and the quote.
 *
 * Phone rules at 960 px wide and under (the game's own break). Folds are
 * classes V3Fold writes on the shell after measuring (static/js/v3-menu.js
 * says which step is which); no height media query chooses one.
 * Unlayered, loaded after the page's own <style>; tokens from tokens.css and
 * v3-tokens.css, with fallbacks for sketch 1's motion tokens, which these two
 * pages do not load. No new hex values.
 */

/* ── shared parts ─────────────────────────────────────────────────────── */
.v3m-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.v3m-ic {
    width: 20px; height: 20px; flex: 0 0 auto;
    fill: none; stroke: currentColor; stroke-width: 1.5;
    stroke-linecap: round; stroke-linejoin: round;
}
/* Read by screen readers, not drawn: off the left edge (which never scrolls),
   so it is not on the screen at all, not just clipped. */
.v3m-sr {
    position: absolute !important; left: -10000px; top: 0; width: 1px; height: 1px;
    overflow: hidden; white-space: nowrap; margin: 0; padding: 0; border: 0;
}
.v3-menu button, .v3-menu a, .v3-pre button {
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
}
.v3-menu .dsf-btn[hidden], .v3-menu [hidden].v3m-empty-acts { display: none; }
/* The kit's `transition:.15s` is `all`: a button's height would glide when a
   fold changes it, and the fold measures in between. Colours only, here. */
.v3-menu .dsf-btn, .v3-menu .dsf-inp, .v3-pre .dsf-btn {
    transition-property: background-color, border-color, color, opacity;
}

/* A button named inside a sentence, drawn as the button reads ("Play Against Bots"). */
.v3-menu .v3m-cap { text-transform: capitalize; }

/* No counts on the menu (the owner): the chip stays in the page, drawn by nobody. */
.v3-menu #lobby-count { display: none; }

/* ── the menu column, every width ─────────────────────────────────────── */
.v3-menu .mv2-session { display: flex; flex-direction: column; min-height: 0; }
.v3-menu .v3m-body { padding: 16px; gap: 10px; }
.v3-menu .v3m-name { display: flex; flex-direction: column; gap: 6px; }
.v3-menu .v3m-name .mv2-namehint { margin: 0; }
.v3-menu .v3m-namerow { display: flex; gap: 8px; align-items: stretch; }
.v3-menu .v3m-namewell { position: relative; flex: 1 1 auto; min-width: 0; display: flex; }
.v3-menu .v3m-namewell .v3m-ic {
    position: absolute; left: 12px; top: 50%; transform: translateY(-50%);
    color: var(--dim); pointer-events: none;
}
.v3-menu #username-input {
    flex: 1 1 auto; min-width: 0; width: 100%; height: 48px; margin: 0;
    padding: 0 12px 0 40px; font-size: 15px;
}
.v3-menu #username-input::placeholder { color: var(--dim); opacity: .7; }
.v3-menu .v3m-namerow > .pf-no-auth,
.v3-menu .v3m-namerow > .pf-auth-required { flex: 0 0 auto; display: flex; }
.v3-menu .v3m-signin { height: 48px; padding: 0 16px; font-size: 14px; font-weight: 600; }

/* Start a Game: the one amber fill. Its line says what it will do. */
.v3-menu .v3m-start {
    width: 100%; height: var(--v3-start-h, 64px); flex-direction: column; gap: 4px;
    padding: 0 16px; border-radius: var(--radius-control, 2px);
}
.v3-menu .v3m-start-t { font: 600 17px/1.1 var(--sans); }
.v3-menu .v3m-start-line {
    font: 500 11px/1.2 var(--sans); opacity: .78; max-width: 100%;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.v3-menu .v3m-start.busy { color: var(--on-acc); }
.v3-menu .v3m-start.busy::before { border-color: rgba(11, 14, 17, .35); border-top-color: var(--on-acc); }

/* The four tiles: 2 x 2, icon box top left, words bottom left. */
.v3-menu .v3m-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.v3-menu .v3m-tiles > .mv2-botcta { display: contents; }
.v3-menu .v3m-tile {
    height: var(--v3-tile-h, 88px); width: 100%; min-width: 0;
    display: flex; flex-direction: column; align-items: flex-start; justify-content: space-between;
    gap: 4px; padding: 9px 12px; text-align: left;
    background: var(--pnl2); border: 1px solid var(--line2); border-radius: var(--radius-control, 2px);
    color: var(--fg);
}
.v3-menu .v3m-tile-ic {
    width: 30px; height: 30px; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--line2); background: var(--pnl); border-radius: var(--radius-control, 2px);
    color: var(--fg);
}
.v3-menu .v3m-tile-t { font: 600 15px/1.15 var(--sans); white-space: normal; }
.v3-menu .v3m-tile.busy::before { position: absolute; right: 12px; top: 12px; }
.v3-menu .v3m-tile { position: relative; }
@media (hover: hover) {
    .v3-menu .v3m-tile:hover { border-color: var(--dim); background: var(--line); }
}

/* Join by code: one 44 px slot. The line, or the field in its place. */
.v3-menu .v3m-code { display: flex; flex-direction: column; }
.v3-menu .v3m-code-line {
    height: 44px; width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    background: none; border: 0; padding: 0; cursor: pointer;
    font: 400 13px/1 var(--sans); color: var(--dim);
}
.v3-menu .v3m-code-line .v3m-ic { width: 16px; height: 16px; }
.v3-menu .v3m-code-a {
    color: var(--fg); text-decoration: underline dotted var(--dim); text-underline-offset: 4px;
}
/* Every width (look-over, round 2: desktop kept a labelled field always on
   show, against the owner's "tucked away"): the line, and the live field in
   its place once the line is pressed (v3-menu.js openCode); it closes again if
   left empty. The label is read, not drawn: the line says it. */
.v3-menu .v3m-code-field { display: none; }
.v3-menu .v3m-code-lbl { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
.v3-menu[data-v3m-code] .v3m-code-line { display: none; }
.v3-menu[data-v3m-code] .v3m-code-field {
    display: block; animation: v3m-field-open var(--dur-reflow, 180ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1));
}
.v3-menu .v3m-code-field .mv2-inline { margin-top: 0; }
.v3-menu #join-code-input, .v3-menu #join-by-code-btn { height: 44px; }
.v3-menu #join-by-code-btn { min-width: 72px; }
.v3-menu .mv2-joinerr:not(:empty) { margin-top: 4px; }

/* The footer, and What's new's dot while there is news. */
.v3-menu .mv2-foot { flex: 0 0 auto; flex-wrap: nowrap; min-width: 0; }
.v3-menu .mv2-foot [data-whats-new-link] { display: inline-flex; align-items: center; gap: 5px; }
.v3-menu .v3m-dot { display: none; width: 6px; height: 6px; border-radius: 50%; background: var(--acc); }
.v3-menu [data-whats-new-link][data-news] .v3m-dot { display: inline-block; }
.v3-menu .mv2-foot #app-version { overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 0 1 auto; }

/* The lobby rows' phone slots, and the list's own parts, are phone only. */
.v3-menu .v3m-by, .v3-menu .v3m-meta, .v3-menu .v3m-inplay-head,
.v3-menu .v3m-list-foot, .v3-menu .v3m-back, .v3-menu .v3m-empty-acts { display: none; }
.v3-menu .lobby-row.v3m-off, .v3-menu .v3m-inplay-head.v3m-off { display: none !important; }
.v3-menu .v3m-refresh { gap: 6px; }

/* Desktop's Find a Lobby: the table's border lights up, holds, and fades.
   First minutes, round 1: a faint 1 px border for 0.9 s was missed; now a
   2 px amber border with a soft glow for about 2 s, and the first Join button
   lights up with it. */
.v3-menu .mv2-lobbies { position: relative; }
.v3-menu .mv2-lobbies::after {
    content: ""; position: absolute; inset: 0; pointer-events: none;
    border: 2px solid var(--acc); box-shadow: 0 0 0 3px var(--acc-dim), inset 0 0 24px var(--acc-dim);
    opacity: 0;
}
.v3-menu .mv2-lobbies.v3m-flash::after { animation: v3m-flash 2000ms var(--ease-out, cubic-bezier(.2, .8, .2, 1)) 1; }
@keyframes v3m-flash { 0% { opacity: 0; } 10% { opacity: 1; } 70% { opacity: 1; } 100% { opacity: 0; } }
.v3-menu .lobby-join-btn.v3m-flash-join { animation: v3m-flash-join 2000ms var(--ease-out, cubic-bezier(.2, .8, .2, 1)) 1; }
@keyframes v3m-flash-join {
    0%, 100% { box-shadow: 0 0 0 0 transparent; }
    10%, 70% { box-shadow: 0 0 0 2px var(--acc), 0 0 12px 2px var(--acc-line); }
}

/* ── desktop (over 960 px): a 440 px column, the live table on the right ─ */
@media (min-width: 961px) {
    .v3-menu .mv2-grid { grid-template-columns: var(--v3-menu-col, 440px) minmax(0, 1fr); }
    .v3-menu .mv2-session { border-right: 1px solid var(--line); border-bottom: 0; }
    .v3-menu .v3m-band { display: none; }
    .v3-menu .v3m-body { flex: 1 1 auto; justify-content: flex-start; padding: 20px; gap: 12px; }
    .v3-menu .v3m-code { margin-top: 4px; }
    .v3-menu .mv2-foot { flex-wrap: wrap; }
}

/* ── the phone (960 px and under) ─────────────────────────────────────── */
@media (max-width: 960px) {
    /* The shell is the window and never taller (STYLE 9.1). The SEO text sits
       below it on purpose (tests/test_seo.py); a player never needs it. */
    .mv2-shell.v3-menu {
        height: 100vh; height: 100dvh; min-height: 0; overflow: hidden;
    }
    .v3-menu .mv2-bar { height: 44px; padding: 0 16px; gap: 8px; }
    .v3-menu .wn-bar { display: none; }
    .v3-menu .mv2-grid { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
    .v3-menu .mv2-session { flex: 1 1 auto; background: var(--bg); border: 0; }
    .v3-menu .mv2-session > .v3m-band,
    .v3-menu .mv2-session > .v3m-body,
    .v3-menu .mv2-session > .mv2-foot { width: 100%; max-width: 520px; margin-left: auto; margin-right: auto; }

    /* The free band: the logo, centred, over the eight set colours. */
    .v3-menu .v3m-band {
        flex: 1 1 auto; min-height: 0; overflow: hidden;
        display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 10px;
        padding: 8px 0;
    }
    .v3-menu .v3m-logo { font: 600 32px/1 var(--mono); letter-spacing: -.02em; color: var(--fg); }
    .v3-menu .v3m-logo i, .v3-menu .v3m-barmark i { font-style: normal; color: var(--acc); }
    .v3-menu .v3m-stripe { width: 96px; height: 3px; flex: 0 0 auto; display: flex; }
    .v3-menu .v3m-stripe i { flex: 1 1 0; }
    .v3-menu .v3m-stripe i:nth-child(1) { background: var(--s-brown); }
    .v3-menu .v3m-stripe i:nth-child(2) { background: var(--s-lblue); }
    .v3-menu .v3m-stripe i:nth-child(3) { background: var(--s-pink); }
    .v3-menu .v3m-stripe i:nth-child(4) { background: var(--s-orange); }
    .v3-menu .v3m-stripe i:nth-child(5) { background: var(--s-red); }
    .v3-menu .v3m-stripe i:nth-child(6) { background: var(--s-yellow); }
    .v3-menu .v3m-stripe i:nth-child(7) { background: var(--s-green); }
    .v3-menu .v3m-stripe i:nth-child(8) { background: var(--s-blue); }
    .v3-menu .v3m-barmark { display: none; }

    .v3-menu .v3m-body { flex: 0 0 auto; padding: 0 16px 8px; gap: 10px; }
    /* field-open (above): the field takes the line's 44 px; nothing above it moves. */
    .v3-menu .mv2-foot {
        height: 40px; padding: 0 16px; gap: 14px; border-top: 1px solid var(--line); flex-wrap: nowrap;
    }
    /* The build id: the hash alone ("build" is read, not drawn), and only when
       it fits whole; v3-menu.js marks it v3m-cut when it would be cut. */
    .v3-menu .mv2-foot .v3m-build-w {
        position: absolute; left: -10000px; top: 0; width: 1px; height: 1px; overflow: hidden;
    }
    .v3-menu .mv2-foot #app-version.v3m-cut { display: none; }

    /* Room to spare (fold step 0, the Android app at 390x844): tiles first,
       then a 40 px logo. */
    .v3-menu:not(.v3-fold-1) .v3m-tile { height: 112px; }
    .v3-menu:not(.v3-fold-1) .v3m-tile-ic { width: 40px; height: 40px; }
    .v3-menu:not(.v3-fold-1) .v3m-logo { font-size: 40px; }
    /* Fold 2: the band goes and the logo returns to the bar. */
    .v3-menu.v3-fold-2 .v3m-band { display: none; }
    .v3-menu.v3-fold-2 .v3m-barmark { display: inline; }
    /* The controls stay at the bottom, where the thumb is. An auto margin, not
       flex-end: when even the last fold cannot hold everything (a keyboard up
       in the app), the overflow falls below, where the focused field can be
       scrolled to, never above the bar. */
    .v3-menu.v3-fold-2 .v3m-body { padding-top: 12px; flex: 1 1 auto; }
    .v3-menu.v3-fold-2 .v3m-body > :first-child { margin-top: auto; }
    /* Fold 3: one-line tiles of 48 px (icon, words). */
    .v3-menu.v3-fold-3 .v3m-tile {
        height: 48px; flex-direction: row; align-items: center; justify-content: flex-start;
        gap: 8px; padding: 0 10px;
    }
    .v3-menu.v3-fold-3 .v3m-tile-ic { width: 20px; height: 20px; border: 0; background: none; }
    .v3-menu.v3-fold-3 .v3m-tile-t { font-size: 13px; }
    .v3-menu.v3-fold-3 .v3m-tiles { gap: 8px; }
    /* Fold 4: the footer is one line of three short links. */
    .v3-menu.v3-fold-4 .mv2-foot { height: 32px; gap: 12px; }
    .v3-menu.v3-fold-4 .mv2-foot #app-version { display: none; }
    /* When even the last step does not fit (a phone held sideways, 844x390),
       the shell scrolls rather than cut the footer and the code's answer off.
       When it fits there is nothing to scroll. */
    .mv2-shell.v3-menu.v3-fold-4 { overflow-y: auto; }

    /* ── Find a Lobby: the live lobbies column as its own screen ───────── */
    /* While it is open the page under it does not scroll (v3-menu.js sets
       data-v3m-lock on <html>; checks/noscroll.md). */
    html[data-v3m-lock], html[data-v3m-lock] body { overflow: hidden; }
    .v3-menu .mv2-lobbies { overscroll-behavior: contain; }
    /* Closed, it is not drawn at all (nothing of it sits past the right edge);
       v3-menu.js shows it off to the right, then slides it in (list-in), and
       slides it out before hiding it again (list-out). */
    .v3-menu .mv2-lobbies {
        position: fixed; left: 0; right: 0; top: 44px; bottom: 0; z-index: 40;
        display: none; flex-direction: column; min-height: 0;
        background: var(--bg); border-top: 1px solid var(--line);
        transform: translateX(100%);
        transition: transform var(--dur-exit, 160ms) var(--ease-in, cubic-bezier(.4, 0, 1, 1));
    }
    .v3-menu[data-v3m-list] .mv2-lobbies { display: flex; }
    .v3-menu[data-v3m-list="open"] .mv2-lobbies {
        transform: none;
        transition: transform var(--dur-enter, 220ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1));
    }
    .v3-menu .mv2-lobbies::after { display: none; }
    /* Head 52: back, "Open lobbies", Refresh. Filter row 48 under it. */
    .v3-menu .mv2-lobbies-head {
        height: auto; min-height: 0; padding: 4px 8px 8px; gap: 0 8px;
        display: grid; grid-template-columns: 44px minmax(0, 1fr) 44px;
        grid-template-areas: "back title refresh" "filter filter filter";
        align-items: center; border-bottom: 1px solid var(--line); background: var(--pnl);
    }
    .v3-menu .v3m-back { display: inline-flex; grid-area: back; width: 44px; height: 44px; padding: 0; border-color: transparent; }
    .v3-menu .v3m-list-title {
        grid-area: title; font: 600 17px/1.2 var(--sans); letter-spacing: 0; text-transform: none; color: var(--fg);
    }
    .v3-menu .mv2-lobbies-head .mv2-fill { display: none; }
    .v3-menu #refresh-btn { grid-area: refresh; width: 44px; height: 44px; padding: 0; }
    .v3-menu .v3m-refresh-t { display: none; }
    .v3-menu #lobby-filter { grid-area: filter; justify-self: start; margin: 4px 0 0 8px; }
    .v3-menu #lobby-filter button { height: 44px; padding: 0 14px; font-size: 12px; }
    .v3-menu .mv2-lobbies > .dsf-row.hd, .v3-menu .mv2-lobbies > .mv2-note { display: none; }

    .v3-menu .mv2-lobbies .mv2-scroll { flex: 1 1 auto; min-height: 0; overflow: hidden; }
    .v3-menu #empty-state { padding: 20px 16px 12px; }
    .v3-menu .v3m-empty-acts { display: flex; flex-direction: column; gap: 10px; padding: 0 16px 16px; }
    .v3-menu .v3m-empty-btn { width: 100%; height: 48px; gap: 8px; font-size: 14px; }
    .v3-menu .v3m-inplay-head {
        display: flex; align-items: center; height: 28px; padding: 0 16px;
        background: var(--pnl); border-bottom: 1px solid var(--line); color: var(--dim);
    }

    /* A room, 64 px: name and "by Dana · 6 min"; then seats, cash, one rule. */
    .v3-menu .lobby-row.mv2-row {
        display: grid; height: var(--v3-lobby-row-h, 64px); padding: 0 16px; gap: 2px 8px;
        grid-template-columns: minmax(0, max-content) minmax(0, 1fr) 72px;
        grid-template-areas: "name by act" "meta meta act";
        align-content: center; align-items: center;
        border-bottom: 1px solid var(--line);
    }
    .v3-menu .lobby-row .mv2-c-code, .v3-menu .lobby-row .lobby-row-host,
    .v3-menu .lobby-row .mv2-c-rules, .v3-menu .lobby-row .lobby-row-cash,
    .v3-menu .lobby-row .lobby-row-occupancy, .v3-menu .lobby-row .mv2-c-status { display: none; }
    .v3-menu .lobby-row .lobby-row-name {
        grid-area: name; font: 600 14px/1.25 var(--sans); color: var(--fg);
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .v3-menu .lobby-row .v3m-by {
        display: block; grid-area: by; font: 400 12px/1.25 var(--sans); color: var(--dim);
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .v3-menu .lobby-row .v3m-meta {
        display: flex; grid-area: meta; align-items: center; gap: 6px; min-width: 0;
        font: 400 12px/1.25 var(--sans); color: var(--fg); white-space: nowrap; overflow: hidden;
    }
    .v3-menu .lobby-row .v3m-fig { font-family: var(--mono); }
    .v3-menu .lobby-row .v3m-seats.v3m-full { color: var(--dim); }
    /* A dot between seats and cash; the rule chip is its own separator. */
    .v3-menu .lobby-row .v3m-cash::before { content: "·"; color: var(--dim); margin-right: 6px; font-family: var(--sans); }
    .v3-menu .lobby-row .v3m-rules:empty { display: none; }
    .v3-menu .lobby-row .v3m-rules { display: inline-flex; gap: 4px; align-items: center; }
    .v3-menu .lobby-row .v3m-rules .dsf-chip { letter-spacing: 0; padding: 0 5px; font-size: 10px; }
    .v3-menu .lobby-row.v3m-tight .v3m-rules .dsf-chip:first-child:not(.v3m-more) { display: none; }
    .v3-menu .lobby-row .mv2-c-act { grid-area: act; display: flex; justify-content: flex-end; }
    .v3-menu .lobby-row .mv2-c-act .dsf-btn { width: 72px; height: 44px; padding: 0; font-size: 14px; }

    /* The bottom bar: Join by code, the pager, Create a Lobby. */
    .v3-menu .v3m-list-foot {
        display: flex; align-items: center; gap: 8px; flex: 0 0 auto;
        padding: 10px 16px calc(10px + env(safe-area-inset-bottom, 0px));
        border-top: 1px solid var(--line); background: var(--pnl);
    }
    .v3-menu .v3m-foot-btn { flex: 1 1 0; height: 44px; gap: 8px; font-size: 14px; min-width: 0; white-space: nowrap; }
    /* With the pager between them, the two lose their icons so nothing wraps at 360 px. */
    .v3-menu .v3m-list-foot[data-paged] .v3m-foot-btn { padding: 0 8px; font-size: 13px; }
    .v3-menu .v3m-list-foot[data-paged] .v3m-foot-btn .v3m-ic { display: none; }
    .v3-menu .v3m-pager { flex: 0 0 auto; height: 44px; min-width: 44px; padding: 0 8px; gap: 2px;
                            font: 500 13px/1 var(--mono); white-space: nowrap; }
    .v3-menu .v3m-pager .v3m-ic { width: 16px; height: 16px; }
    /* The last page: "‹ 2 of 2", the arrow before the words and pointing back. */
    .v3-menu .v3m-pager.v3m-pager-back { flex-direction: row-reverse; }
    .v3-menu .v3m-pager.v3m-pager-back .v3m-ic { transform: scaleX(-1); }
}
@media (max-width: 360px) {
    .v3-menu .v3m-body, .v3-menu .mv2-foot { padding-left: 12px; padding-right: 12px; }
    .v3-menu .lobby-row.mv2-row { padding: 0 12px; }
    .v3-menu .v3m-list-foot { padding-left: 12px; padding-right: 12px; }
}
@keyframes v3m-field-open { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
    .v3-menu .mv2-lobbies, .v3-menu[data-v3m-list="open"] .mv2-lobbies { transition: opacity var(--dur-enter, 220ms); transform: none; opacity: 0; }
    .v3-menu[data-v3m-list="open"] .mv2-lobbies { opacity: 1; }
    .v3-menu[data-v3m-code] .v3m-code-field { animation: none; }
}

/* ═════════════════════════════════════════════════════════════════════════
 * The pre-lobby (templates/prelobby_v2.html, .pv2-shell.v3-pre)
 * ═════════════════════════════════════════════════════════════════════════ */

/* ── colour picker B, every width (STYLE 11.3) ─────────────────────────── */
.v3-pre .v3m-colhead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.v3-pre .pv2-colors { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 6px; }
.v3-pre .pv2-color {
    min-width: 0; height: auto; min-height: var(--tap, 44px); padding: 4px 0 0; margin: 0;
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    background: none; border: 0; border-radius: var(--radius-control, 2px);
    color: var(--fg); cursor: pointer; font: inherit; opacity: 1;
}
.v3-pre .pv2-color:disabled { cursor: not-allowed; opacity: 1; background-image: none; }
.v3-pre .pv2-color:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px; }
/* Wide screens: 32 px, not STYLE's 38 (--v3-dot-desk). Six 38 px dots in the
   255 px left column left about 5 px between them, and your colour's ring
   (a 2 px gap and a 2 px ring) filled it. The phone rule below uses --v3-dot. */
.v3-pre .v3m-cdot {
    position: relative; flex: 0 0 auto; border-radius: 50%;
    width: 32px; height: 32px;
    display: inline-flex; align-items: center; justify-content: center;
}
/* Taken: an empty ring in its colour, the holder's initial inside. */
.v3-pre .v3m-taken .v3m-cdot { background-color: var(--bg); box-shadow: inset 0 0 0 var(--v3-ring, 3px) var(--v3m-c); }
.v3-pre .v3m-cmark { position: relative; z-index: 1; font: 600 13px/1 var(--sans); color: var(--v3m-c); }
/* Yours: filled, a 2 px gap, a 2 px ring, a check. */
.v3-pre .v3m-mine .v3m-cdot { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--fg); }
.v3-pre .v3m-mine .v3m-cmark::before { content: "\2713"; display: inline-block; font: 600 16px/1 var(--sans); color: var(--on-acc); }
.v3-pre .v3m-cname {
    max-width: 100%; min-height: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font: 600 11px/1.2 var(--sans); color: var(--fg);
}
.v3-pre .v3m-taken .v3m-cname { font: 400 10px/1.3 var(--sans); color: var(--dim); }
@media (hover: hover) {
    .v3-pre .pv2-color:not(:disabled):not(.v3m-mine):hover .v3m-cdot { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--line2); }
}
/* ring-drain: someone takes a colour; the fill fades to the ring, the initial fades in. */
.v3-pre .v3m-drain .v3m-cdot::after {
    content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--v3m-c);
    animation: v3m-drain var(--dur-ring, 180ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1)) forwards;
}
.v3-pre .v3m-drain .v3m-cmark { animation: v3m-fadein var(--dur-ring, 180ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1)); }
/* your pick's check pops in. */
.v3-pre .v3m-pop .v3m-cmark::before { animation: v3m-pop var(--dur-ring, 180ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1)); }
@keyframes v3m-drain { from { opacity: 1; } to { opacity: 0; } }
@keyframes v3m-fadein { from { opacity: 0; } to { opacity: 1; } }
@keyframes v3m-pop { from { transform: scale(.6); } to { transform: none; } }

/* Create a Private Lobby: said once, under the code. */
.v3-pre .v3m-private-note { font: 400 11px/1.3 var(--sans); color: var(--info); margin-top: 2px; }
.v3-pre .v3m-private-note[hidden] { display: none; }

/* Your own row in the roster: a dim "you" after the name (first minutes,
   round 1; the owner's pick, prelobby B). Drawn here so the name's text stays
   the name alone. */
.v3-pre #players-list .pl-player-row.pl-me .nm::after {
    content: "\00A0\00A0you"; color: var(--dim); font: 400 11px var(--sans); letter-spacing: 0;
}

/* The phone-only parts are off on a wide screen; the wide layout is v2's. */
.v3-pre .v3m-addbot, .v3-pre .v3m-bots-x, .v3-pre .v3m-cashsum, .v3-pre .v3m-chatnote,
.v3-pre .v3m-seatsleft, .v3-pre .v3m-scrim { display: none; }
.v3-pre .v3m-chatsheet[hidden], .v3-pre .v3m-bots[hidden], .v3-pre .v3m-presets[hidden] { display: none; }
/* On a phone the presets block lives in the Game options dialog, outside the
   shell, where the page's only [hidden] rule (.pv2-shell [hidden]) does not
   reach, and the kit's display (.pv2-stack flex, .dsf-btn inline-flex) beats
   the browser's own [hidden]. So a guest's read-only dialog showed Reset and
   Save preset, and a host with no preset saw Apply preset. */
#game-options-modal .v3m-presets[hidden],
#game-options-modal .v3m-presets [hidden] { display: none; }
.v3-pre .v3m-bots-head { display: flex; align-items: center; gap: 8px; }

/* Before the first lobby payload (data-v3m-wait, cleared by v3-menu.js): who
   hosts is not known yet, so nothing that only a host sees is drawn, and the
   rules column waits (it is Host controls or Room rules). Look-over, round 2:
   a guest's first frame showed Add a bot, Host controls and Reset/Save preset.
   On a wide screen the column keeps its place, so the grid does not move. */
.v3-pre[data-v3m-wait] [data-host-only] { display: none; }
.v3-pre[data-v3m-wait] .pv2-col.right > * { visibility: hidden; }

/* ── the phone (780 px and under, this page's own single-column switch) ── */
@media (max-width: 780px) {
    /* Waiting for the first payload: the rules column, the bot zone (its fold
       rules outrank the one above), the chat's box (v2 folds an empty chat to
       its row) and the two big buttons in their wide
       homes stay out; the bottom bar is pinned, empty, where Start or I'm
       ready will sit (v2-loose-ends-lobby.js moves them in once the roster
       is drawn), so nothing jumps to the bottom later. */
    .v3-pre[data-v3m-wait] .pv2-col.right,
    .v3-pre[data-v3m-wait] .v3m-botzone,
    .v3-pre[data-v3m-wait] .pv2-col.mid > .pv2-chat,
    .v3-pre[data-v3m-wait] .pv2-col.mid > .pv2-chat-send,
    .v3-pre[data-v3m-wait] #ready-btn,
    .v3-pre[data-v3m-wait] #start-game-btn { display: none; }
    .v3-pre[data-v3m-wait] #v2l-lobbybar {
        position: sticky; bottom: 0; z-index: 20; flex: 0 0 auto; display: flex;
        box-sizing: border-box; min-height: calc(56px + env(safe-area-inset-bottom, 0px));
        background: var(--pnl); border-top: 1px solid var(--line2); box-shadow: 0 -6px 12px rgba(0, 0, 0, .35);
    }
    .v3-pre .v3m-cdot { width: var(--v3-dot, 44px); height: var(--v3-dot, 44px); }
    .v3-pre .pv2-bar { padding: 8px 16px; gap: 8px; }
    .v3-pre .dsf-panel__head { padding: 0 16px; }
    /* The roster, tightened: 40 px rows, so a full room fits 360x640. The bots'
       ✕ (the live control) is 44 wide and the row's 40 tall, marked
       data-v3-small-ok by v3-menu.js; no box of its own, so its edges do not
       run into the row lines. */
    .v3-pre .pl-player-row { padding: 0 16px; height: 40px; }
    .v3-pre .pl-player-row .pl-remove-bot {
        width: 44px; height: 40px; min-height: 0; padding: 0; border-color: transparent;
    }
    .v3-pre .pl-player-row .pl-remove-bot:active { background: var(--line); }
    /* Spare height gathers above the pinned Start, not between the parts. */
    .v3-pre .pv2-grid { align-content: start; }
    .v3-pre .pv2-col.left > .pv2-foot { padding: 10px 16px; gap: 10px; }

    /* Add bot and Spectate only share one zone: the four kinds in one row,
       Add "Me" bot beside Spectate only. */
    .v3-pre .v3m-botzone {
        display: grid; grid-template-columns: minmax(0, 1fr) auto; column-gap: 12px;
        grid-template-areas: "head head" "grid grid" "me spec"; align-items: center;
    }
    .v3-pre .v3m-bots { display: contents; }
    .v3-pre .v3m-bots-head { grid-area: head; justify-content: space-between; }
    .v3-pre .v3m-seatsleft { display: inline; }
    .v3-pre .v3m-botzone .pl-bot-grid { grid-area: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 6px; margin-top: 8px; }
    .v3-pre .v3m-botzone .bot-btn { min-width: 0; padding: 0 2px; font-size: 12px; }
    .v3-pre .v3m-botzone .pl-add-me-bot { grid-area: me; width: 100%; margin-top: 8px; }
    .v3-pre .v3m-addbot { grid-area: me; width: 100%; margin-top: 8px; height: 44px; }
    /* V3.2 (brief B): LabBot 2.0 are THE bots. The Bot button has its own row;
       "Classic bots (old)" shares one with Spectate only; the classic block
       (the four kinds in one row, then Add "Me" bot) opens under them. With lab
       bots off the classic block is the list, and Spectate only has a row. */
    .v3-pre .v3m-botzone.v3m-botzone--lab { grid-template-areas: "head head" "new new" "me spec" "classic classic"; }
    .v3-pre .v3m-botzone:not(.v3m-botzone--lab) { grid-template-areas: "head head" "classic classic" "spec spec"; }
    .v3-pre .v3m-botzone .pv2-bot-new { grid-area: new; width: 100%; margin-top: 8px; padding: 0 12px; font-size: 14px; }
    .v3-pre .v3m-botzone .pv2-classic-btn { grid-area: me; width: 100%; margin-top: 8px; padding: 0 12px; font-size: 13px; }
    .v3-pre .v3m-botzone .pv2-classic { grid-area: classic; margin-top: 8px; }
    .v3-pre .v3m-botzone .pv2-classic .pl-bot-grid { margin-top: 0; }
    .v3-pre .v3m-botzone .pv2-classic .pl-add-me-bot { margin-top: 0; }
    .v3-pre .v3m-spec { grid-area: spec; height: 44px; margin-top: 8px; padding: 0; border: 0; gap: 8px; background: none; }
    .v3-pre:not([data-v3m-host]) .v3m-spec,
    .v3-pre[data-v3m-full] .v3m-spec { grid-area: auto; grid-column: 1 / -1; margin-top: 0; }
    /* A full room has no seat for a bot: Add bot goes. */
    .v3-pre[data-v3m-full] .v3m-bots, .v3-pre[data-v3m-full] .v3m-addbot { display: none; }

    /* The chat row (v2 folds an empty chat to it). */
    .v3-pre .pv2-col.mid > .dsf-panel__head { height: 44px; }

    /* Host controls: 44 px rows; Reset and Save preset are in the dialog. */
    .v3-pre .pv2-col.right .pv2-pad { padding: 0 16px; gap: 0; }
    .v3-pre .pv2-col.right .pv2-pad > .dsf-row { height: 44px; padding: 0; }
    .v3-pre .pv2-col.right .pv2-foot { padding: 0; border: 0; gap: 0; }
    .v3-pre .v3m-guesthint { display: none; }
    .v3-pre .pv2-optedit { min-width: 64px; }
    #game-options-modal .v3m-presets { margin-bottom: 10px; }
    #game-options-modal .v3m-presets .dsf-btn { height: var(--tap, 44px); }

    /* Start game / I'm ready, pinned: 56 px plus the phone's own bar. */
    .v3-pre #v2l-lobbybar[data-on="1"] { padding: 6px 16px calc(6px + env(safe-area-inset-bottom, 0px)); }

    /* The head: one block, the name and code over the status line, Copy and
       Leave at the right; nothing wraps (a long name is cut, never the code).
       A guest's starting cash is in the rules row instead. */
    .v3-pre .pv2-bar { flex-wrap: nowrap; min-height: 0; }
    .v3-pre .pv2-bar > div:first-child { flex: 1 1 auto; min-width: 0; }
    .v3-pre .pv2-idline { flex-wrap: nowrap; min-width: 0; }
    .v3-pre .pv2-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .v3-pre #lobby-code { flex: 0 0 auto; }
    .v3-pre .pv2-sub { flex-wrap: nowrap; overflow: hidden; white-space: nowrap; }
    .v3-pre #lobby-status-line { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
    .v3-pre #non-host-cash-info { display: none; }
    .v3-pre #non-host-message .v3m-cashsum { display: inline; flex: 0 0 auto; color: var(--fg); font-size: 13px; }
    .v3-pre .pv2-bar-actions { flex-wrap: nowrap; flex: 0 0 auto; }
    /* No "Code" label on a phone: the code is amber and Copy is beside it.
       Look-over, round 2: at 390 px the label cut the room's own name
       ("Marco's Lob… CODE 28f6ae78") while 360 px, without it, showed it whole. */
    .v3-pre .pv2-idline > .dsf-lbl { display: none; }

    /* The fold, by measuring (v3-menu.js), cumulative: 1 the chat, 2 the head,
       3 the bot buttons, 4 the rule rows. Look-over, round 2: the chat used to
       be step 3, so one message someone wrote (v2 then opens the chat in place)
       folded the head and the four bot buttons first, and with the chat open
       for good they never came back. The chat is the part that gives first. */

    /* Fold 1: an open chat is one row again; a tap opens it as a sheet. */
    .v3-pre.v3-fold-1 .pv2-col.mid > .pv2-chat,
    .v3-pre.v3-fold-1 .pv2-col.mid > .pv2-chat-send { display: none; }
    .v3-pre.v3-fold-1 .pv2-col.mid .v2l-chat-note,
    .v3-pre.v3-fold-1 .pv2-col.mid .v2l-chat-caret { display: none; }
    .v3-pre.v3-fold-1 .pv2-col.mid > .dsf-panel__head { cursor: pointer; }
    .v3-pre.v3-fold-1 .v3m-chatnote {
        display: block; margin-left: auto; min-width: 0; max-width: 70%;
        overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
        font: 400 12px/1.2 var(--sans); color: var(--dim);
    }

    /* Fold 2: the head loses 4 px of padding. */
    .v3-pre.v3-fold-2 .pv2-bar { padding: 6px 12px; }
    .v3-pre.v3-fold-2 .pv2-bar-actions { flex-wrap: nowrap; flex: 0 0 auto; }

    /* Fold 3: Add bot is one button; its list rises from the bottom. */
    .v3-pre.v3-fold-3 .v3m-botzone { grid-template-areas: "me spec"; }
    .v3-pre.v3-fold-3 .v3m-bots { display: none; }
    .v3-pre.v3-fold-3 .v3m-addbot:not([hidden]) { display: inline-flex; }
    .v3-pre.v3-fold-3[data-v3m-full] .v3m-addbot { display: none; }
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots:not([hidden]) {
        display: flex; flex-direction: column; gap: 10px;
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
        padding: 6px 16px calc(16px + env(safe-area-inset-bottom, 0px));
        background: var(--pnl); border-top: 1px solid var(--line2); box-shadow: var(--elev-sheet);
        animation: v3m-sheet-in var(--dur-enter, 220ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1));
    }
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots-x { display: inline-flex; width: 44px; height: 44px; padding: 0; margin-left: auto; }
    /* The sheet's head reads "Add a bot · 2 seats left", not one run-on phrase. */
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-seatsleft:not(:empty)::before { content: "\00B7\00A0"; }
    /* Behind an open sheet the page dims and stops taking taps; a tap on the
       dimmed page closes the sheet (v3-menu.js). Under the sheets (60), over
       the pinned Start bar. */
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-scrim,
    .v3-pre[data-v3m-chat] .v3m-scrim {
        display: block; position: fixed; inset: 0; z-index: 59;
        background: var(--v3-scrim, rgba(0, 0, 0, .55));
        animation: v3m-fadein var(--dur-enter, 220ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1));
    }
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots .pl-bot-grid { grid-template-columns: 1fr 1fr; margin-top: 0; }
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots .bot-btn { font-size: 13px; }
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots .pl-add-me-bot { margin-top: 0; }
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots .pv2-bot-new,
    .v3-pre.v3-fold-3[data-v3m-bots] .v3m-bots .pv2-classic-btn { margin-top: 0; }

    /* The chat sheet (fold 1, or a tap on the closed chat row). */
    .v3-pre .v3m-chatsheet {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 60;
        height: min(420px, 70vh); height: min(420px, 70dvh);
        display: flex; flex-direction: column;
        padding-bottom: env(safe-area-inset-bottom, 0px);
        background: var(--pnl); border-top: 1px solid var(--line2); box-shadow: var(--elev-sheet);
        animation: v3m-sheet-in var(--dur-enter, 220ms) var(--ease-out, cubic-bezier(.2, .8, .2, 1));
    }
    .v3-pre .v3m-sheet-head {
        flex: 0 0 auto; display: flex; align-items: center; height: 44px; padding: 0 4px 0 16px;
        border-bottom: 1px solid var(--line);
    }
    .v3-pre .v3m-sheet-x { margin-left: auto; width: 44px; height: 44px; padding: 0; }
    .v3-pre .v3m-chatsheet .pv2-chat { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

    /* Fold 4: the host's two rule rows are one ("$1,500 · no auctions  Edit"). */
    .v3-pre.v3-fold-4 .v3m-cashrow { display: none; }
    .v3-pre.v3-fold-4 .pv2-col.right .dsf-panel__head { display: none; }
    .v3-pre.v3-fold-4 .pv2-optsum .nm { display: none; }
    .v3-pre.v3-fold-4 .v3m-cashsum { display: inline; flex: 0 0 auto; color: var(--fg); font-size: 13px; }
}
@media (max-width: 380px) {
    /* The status line's short form (first minutes, round 1): "· hosting" (the
       pinned Start button says the count) or "· joined · waiting on 1". The
       full line stays the element's text, for screen readers and wider screens. */
    .v3-pre #lobby-status-line[data-short]:not([data-short=""]) { font-size: 0; }
    .v3-pre #lobby-status-line[data-short]:not([data-short=""])::after {
        content: attr(data-short); font-size: 10px; letter-spacing: .1em;
    }
}
@media (max-width: 360px) {
    .v3-pre .pv2-bar, .v3-pre .dsf-panel__head, .v3-pre .pl-player-row,
    .v3-pre .pv2-col.left > .pv2-foot { padding-left: 12px; padding-right: 12px; }
    .v3-pre .pv2-col.right .pv2-pad { padding: 0 12px; }
}
@keyframes v3m-sheet-in { from { transform: translateY(100%); } to { transform: none; } }

@media (prefers-reduced-motion: reduce) {
    .v3-pre .v3m-drain .v3m-cdot::after, .v3-pre .v3m-drain .v3m-cmark,
    .v3-pre .v3m-pop .v3m-cmark::before, .v3-pre .v3m-chatsheet, .v3-pre .v3m-bots,
    .v3-pre .v3m-scrim { animation: none !important; }
}
