ui/lobbymenu.razor.scss
// Lobby — fullscreen overlay, laid out like the original's main menu:
// options down the top left, players on the right, over a full-bleed image.

.lobby
{
    font-family: Inter;
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    width: 100%;
    height: 100%;

    // ⚠️ s&box PANELS ARE NOT HIT-TESTABLE UNLESS THEY OPT IN.
    //
    // This file declared no pointer-events at all, and that was the whole bug:
    // no hover highlight, no clicks, nothing. DevMenu.razor.scss has always had
    // the working pattern — `pointer-events: none` on the root, `all` on the
    // interactive frame — which is exactly why the Q menu's buttons worked and
    // the lobby's never did.
    //
    // It is NOT a cursor problem. Removing the player, and migrating
    // Mouse.Visible -> Mouse.Visibility, both changed nothing, because the
    // cursor was fine and there was simply nothing for it to hit. The giveaway
    // was the hand cursor flickering on click while no button ever highlighted.
    pointer-events: none;

    &.hidden { display: none; }

    // ── background ──────────────────────────────────────────────────────────
    .bg
    {
        position: absolute;
        top: 0; left: 0; right: 0; bottom: 0;
        width: 100%;
        height: 100%;
        background-size: cover;
        background-position: center;
    }

    // Sits over the image so text stays readable on ANY art — without it a
    // light background makes the whole menu vanish, and that is not something
    // you notice until someone swaps the image.
    .scrim
    {
        position: absolute;
        top: 0; left: 0; right: 0; bottom: 0;
        width: 100%;
        height: 100%;
        // Light touch — the art is already dark, and a heavy scrim would throw
        // away the thing we put it there for. Enough to stop text sitting on a
        // streetlamp, not enough to flatten the image.
        background-color: rgba( 6, 8, 12, 0.42 );
    }

    .content
    {
        position: absolute;
        top: 0; left: 0; right: 0; bottom: 0;
        width: 100%;
        height: 100%;
        flex-direction: row;
        padding: 56px 64px;

        // The menu itself is what you click. Matches DevMenu's `.frame`.
        // Deliberately here and not on the root, so the background and scrim
        // stay pass-through rather than eating clicks meant for the world.
        pointer-events: all;
    }

    // ── left: options, running downward ─────────────────────────────────────
    .left
    {
        flex-direction: column;
        width: 380px;

        // ⛔ THE COLUMN SCROLLS; ITS ROWS DO NOT SHRINK. With the character list expanded the
        // menu is taller than the screen leaves room for, and the flex default let every row
        // absorb the difference — which does not look like an overfull menu, it looks like a
        // rendering bug: `Creative` and `Build and edit the map config` printed ON TOP of each
        // other, two rows of text in one row of space. Capping the LIST instead only moved the
        // damage (the twelfth character fell off the bottom with no scrollbar to say so).
        // ⚠️ SO THE OVERFLOW IS SPENT IN ONE PLACE, DELIBERATELY: the column scrolls, every row
        // keeps the height it asked for, and nothing is silently missing or silently on top of
        // anything else.
        overflow-y: scroll;

        > * { flex-shrink: 0; }

        .brand   { font-size: 44px; font-weight: 600; color: #fff; }
        // ⚠️ A LABEL AGAIN. This was briefly a button; it was too quiet to find, and the Map row
        // in the menu below does the job properly. Left as the subtitle under the brand, which is
        // where you look to answer "what am I on" rather than "how do I change it".
        .mapname { font-size: 17px; color: rgba( 255, 255, 255, 0.4 ); margin-bottom: 22px; }

        // ⚠️ THE GAMEMODE BEING PLAYED, UNDER THE MAP NAME (published side, 2026-10-05): gold, the map browser's accent, and pulled
        // up against the name it belongs to.
        .modename
        {
            font-size: 14px;
            font-weight: 600;
            letter-spacing: 1.5px;
            text-transform: uppercase;
            color: #ffd479;
            margin-top: -18px;
            margin-bottom: 22px;
        }

        // ⚠️ THE EDITOR ON THE PLAYER'S SIDE (`nz_side`, 2026-10-05): amber, under the map's name, so it is the first thing
        // read in a lobby that has lost its building rows on purpose
        .sidetag
        {
            font-size: 12px;
            letter-spacing: 1px;
            color: #f0b44c;
            background-color: rgba( 240, 180, 76, 0.12 );
            border-left: 2px solid #f0b44c;
            padding: 6px 10px;
            margin-top: -12px;
            margin-bottom: 18px;
        }
        .hint    { margin-top: 24px; font-size: 13px; color: rgba( 255, 255, 255, 0.3 ); }
    }

    // ⛔ THE MENU READS OVER ANY PICTURE NOW (2026-10-05): *"i want to change the buttons to be more easily seen in the
    // lobby"*. The rows were white at 5% over the map's art, with their second lines at 38% white, and on a bright picture
    // both all but vanished. They are SOLID now, in the map select page's palette (MapBrowser.razor.scss): dark strips, the
    // gold rule (#ffd479) on the row under the pointer, and bigger, brighter text.
    .group
    {
        font-size: 13px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 2px;
        color: #c9cdd4;
        // ⚠️ A SHADOW, NOT A BOX: the headings sit straight on the picture, and this keeps them readable on a light one
        text-shadow: 0px 1px 4px rgba( 0, 0, 0, 0.9 );
        margin-top: 20px;
        margin-bottom: 8px;

        .dim { color: #8d939c; }
    }

    // ⚠️ INDENTED AND QUIETER THAN `.opt`, deliberately. The list is a child of the row that
    // opened it, and styling it like a sibling option would make the menu look like it grew four
    // permanent entries — the exact thing collapsing it was meant to avoid.
    // ⚠️ SOLID TOO (2026-10-05), a shade darker than the rows, so the open list still reads over the picture.
    .charlist
    {
        flex-direction: column;
        margin: -2px 0 8px 14px;
        border-left: 2px solid #2a2e36;

        // ⛔ `flex-shrink: 0`, AND WITHOUT IT THE TWELFTH CHARACTER WAS UNREACHABLE. The list is a
        // flex child in a column that is already nearly full, so it was being SQUEEZED rather than
        // capped: measured in the running lobby it got a 216px rect for 223px of rows, and Samuel
        // Stuhlinger — last in the last set — was clipped off the bottom with no scrollbar to say
        // so. A list that silently ends one row early is indistinguishable from a roster that is
        // missing a character, which is exactly how it was reported.
        flex-shrink: 0;

        // ⚠️ THE CAP IS A BACKSTOP, NOT THE LAYOUT. Rows are tight enough that fifteen of them
        // (twelve characters, three headers) fit without it; it only engages if the roster grows
        // again, and then the list scrolls instead of eating the Build and Config rows below.
        max-height: 360px;
        overflow-y: scroll;

        // The set name, not an option — sized and coloured like `.group` so it reads as a
        // heading inside the list rather than a thirteenth thing you can click.
        .charset
        {
            font-size: 12px;
            font-weight: 600;
            text-transform: uppercase;
            letter-spacing: 1.5px;
            color: #8d939c;
            background-color: #0b0c0f;
            padding: 7px 14px 3px 14px;
        }

        .charopt
        {
            padding: 8px 14px;
            font-size: 16px;
            color: #c3c8cf;
            background-color: #0e1013;
            margin-bottom: 2px;
            cursor: pointer;

            &:hover
            {
                background-color: #1c2027;
                color: #fff;
            }

            &.on
            {
                background-color: #14251a;
                color: #cdeccd;
            }

            &.clear { color: #8d939c; font-size: 15px; }
        }
    }

    .opt
    {
        flex-direction: column;
        padding: 14px 18px;
        margin-bottom: 6px;
        background-color: #111317;
        border-left: 3px solid #2a2e36;
        cursor: pointer;

        .opt-label { font-size: 19px; font-weight: 600; color: #f2f4f7; }
        .opt-desc  { font-size: 14px; color: #8d939c; margin-top: 2px; }

        // ⚠️ THE GOLD RULE MARKS THE ROW UNDER THE POINTER, as it marks the previewed map on the map select page
        &:hover
        {
            background-color: #1c2027;
            border-left: 3px solid #ffd479;

            .opt-label { color: #fff; }
            .opt-desc  { color: #b9bec6; }
        }

        &.active
        {
            background-color: #102033;
            border-left: 3px solid #4a9eff;
        }

        // Ready up: the one row the whole lobby is for, so the biggest
        &.strong
        {
            padding: 18px 18px;

            .opt-label { font-size: 22px; }
        }

        &.on
        {
            background-color: #14251a;
            border-left: 3px solid #7ad17a;

            .opt-label { color: #e3f5e3; }
            .opt-desc  { color: #9fd49f; }
        }

        // Nothing to ready up FOR — the map has no spawns placed.
        //
        // ⚠️ pointer-events: none is what makes it unclickable, so the hover
        // sound cannot fire either. The click handler stays wired in the markup
        // on purpose (LobbyCommands.Ready does the refusing) — this is the
        // visible half of a rule enforced elsewhere, not the rule itself.
        //
        // ⚠️ The DESCRIPTION keeps full opacity while the label dims. It is
        // carrying the reason, and a greyed-out control whose explanation is
        // also greyed out is just a broken button.
        &.disabled
        {
            background-color: #221214;
            border-left: 3px solid #d94b4b;
            pointer-events: none;

            .opt-label { opacity: 0.45; }
            .opt-desc { color: #e08a8a; }
        }
    }

    // ── middle: character previews ──────────────────────────────────────────
    .middle
    {
        flex-grow: 1;
        align-items: center;
        justify-content: flex-end;

        .preview
        {
            width: 100%;
            height: 78%;
        }
    }

    // ── right: players ──────────────────────────────────────────────────────
    // ⚠️ 400 WIDE, WAS 330 (2026-10-05): *"player names have little space to exist, so it starts creating more rows"*
    .right
    {
        flex-direction: column;
        margin-left: auto;
        width: 400px;
    }

    .player
    {
        flex-direction: row;
        align-items: center;
        padding: 12px 16px;
        margin-bottom: 6px;
        background-color: rgba( 255, 255, 255, 0.05 );
        border-radius: 4px;

        .tick   { font-size: 15px; color: rgba( 255, 255, 255, 0.3 ); width: 18px; flex-shrink: 0; }

        // ⛔ ONE LINE, AND THE ROOM IS THE NAME'S (2026-10-05). The name took what the state left it and wrapped onto two or three
        // lines. It now takes every pixel the tick and the state don't need and stays on one line. A name too long for 16 px
        // is drawn smaller until it fits: the row sets its size (`NameFit`, `NameRoom` in LobbyMenu.razor). The "…" is a
        // backstop only; at the 8 px floor even Steam's longest name fits.
        .pname
        {
            font-size: 16px;
            color: #fff;
            margin-left: 6px;
            flex-grow: 1;
            flex-shrink: 1;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        .pstate { margin-left: 12px; flex-shrink: 0; white-space: nowrap; font-size: 13px; color: rgba( 255, 255, 255, 0.35 ); }

        // Which one is you. Obvious with one player, not with eight — and the
        // list now shows real names rather than "You" in slot one.
        &.you
        {
            border-left: 2px solid rgba( 255, 255, 255, 0.45 );
            background-color: rgba( 255, 255, 255, 0.09 );
        }

        // Ready is the one state worth reading across a full roster at a
        // glance, so it gets colour rather than only a word.
        &.ready
        {
            background-color: rgba( 122, 209, 122, 0.12 );

            .tick   { color: #7ad17a; }
            .pstate { color: #7ad17a; }
        }
    }
}