/* _content/LiveEventBingo.Shared/Components/Gameplay/ConnectionStatusBanner.razor.rz.scp.css */
/* ADO #11167: the reconnecting hint line. Same shipped warning pair as .refresh-error-notice (tokens only):
   the line is in flow in the gameplay notice slot and never overlays the board. */
.connection-status-banner[b-v1k3b5eu2f] {
    margin: 0 0 var(--meb-space-stack-sm) 0;
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-md);
    background: var(--meb-accent-warning);
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-medium);
    text-align: center;
}
/* _content/LiveEventBingo.Shared/Pages/Common/Components/Cards/BingoFinished.razor.rz.scp.css */
/* ===================================================================
   BingoFinished.razor.css
   Ended-state end-screen. Token-governed; no raw hex/shadow.
   Desktop: leaderboard + thank-you side by side. Mobile: two-tab folder.
   =================================================================== */

.bingo-end[b-f1auvw91im] {
    width: 100%;
}

/* Desktop two-column track owned by <Grid>; mobile tabs by <Tabs>. */

/* ── Game Over thank-you panel ──────────────────────────────────────────
   Column flow, padding, and actions-stack sizing owned by <Stack> props
   (Padding / PaddingBlock / FillBlock) — see BingoFinished.razor.
   #9064: `.bingo-end-go`, `.bingo-end-actions`, and `.bingo-end-tabs` sit on
   CHILD COMPONENTS (Stack/Tabs), so scoped [b-...] rules on them are DEAD —
   do not re-add page CSS on those classes; they remain as semantic hooks only. */

/* APPROVED EXCEPTION (§4.1 atomic rendering): centers a single decorative
   SVG glyph inside a fixed circular badge -- the same atomic icon-in-disc
   pattern as .nav-avatar-anonymous. Not product layout; no layout primitive
   wraps a lone glyph in a disc. */
.bingo-end-check[b-f1auvw91im] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 5rem;
    height: 5rem;
    border-radius: var(--meb-radius-full);
    /* Fixed success-green, NOT --meb-accent-success: the mobile shell rebinds
       --meb-accent-success to the player's mark color (M10), which would tint
       this celebratory check the mark colour instead of green. */
    background: var(--meb-palette-green-bright, var(--meb-accent-success));
}

.bingo-end-check svg[b-f1auvw91im] {
    width: 50%;
    height: 50%;
    fill: none;
    stroke: var(--meb-text-inverse);
    stroke-width: 3;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.bingo-end-title[b-f1auvw91im] {
    margin: 0;
    text-align: center;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h1-mobile, 2rem);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}

.bingo-end-copy[b-f1auvw91im] {
    margin: 0;
    max-width: 36ch;
    text-align: center;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    line-height: var(--meb-font-lineheight-body);
}

/* ---- Accessibility helper (#9059) — visually-hidden live region for copy
   failure announcements; mirrors NewGame's .ng-sr-only.
   TODO(#9058 follow-up): the sr-only/visually-hidden helper is duplicated across
   several components (.ng-sr-only, .sr-only inline blocks, .visually-hidden);
   consolidate into ONE shared utility in LiveEventBingo.Shared/wwwroot/css/ once
   the App.razor render-blocking CSS budget review (#8553) says where it loads. ---- */
.bingo-end-sr-only[b-f1auvw91im] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Mobile ─────────────────────────────────────────────────────────────
   #9064: no rules needed. The folder <Tabs> root is a block-level div inside
   the plain `.bingo-end--mobile` div (width:100% via `.bingo-end`), so it is
   already full-width; the Game Over panel's larger mobile padding-block is the
   Stack's PaddingBlock="xl" prop in BingoFinished.razor.
   #11111 (S5-T7): the three-tab modifier `.bingo-end-tabs--three` (14px labels,
   8px side padding) is in wwwroot/css/components/tabs.css, not here: it sits on
   the child Tabs root, where a scoped rule would be dead. */
/* _content/LiveEventBingo.Shared/Pages/Common/Components/Cards/BingoLeaderboard.razor.rz.scp.css */
/* ===================================================================
   BingoLeaderboard.razor.css
   Ended-state winners leaderboard. Token-governed; no raw hex/shadow.
   Bespoke list/grid rows, consistent with the in-game leaderboard popup.
   =================================================================== */

/* List column flow owned by <Stack>; only visuals here. */
.bingo-lb-list[b-zqebxs1tzu] {
    width: 100%;
    border-radius: var(--meb-border-radius-lg);
    overflow: hidden;
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
}

/* Row track owned by <Grid TemplateColumns>; only visuals here
   (rank | avatar | name | finish-time). */
.bingo-lb-row[b-zqebxs1tzu] {
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-lg);
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
}

/* Zebra striping — every other row gets a faint section tint. */
.bingo-lb-row:nth-child(even)[b-zqebxs1tzu] {
    background: color-mix(in srgb, var(--meb-surface-section-creator) 8%, var(--meb-surface-card));
}

/* Current viewer's own row — mark-tinted highlight. */
.bingo-lb-row--me[b-zqebxs1tzu] {
    background: color-mix(in srgb, var(--bingo-mark-color, var(--meb-surface-section-creator)) 16%, var(--meb-surface-card));
    font-weight: var(--meb-font-weight-bold);
}

.bingo-lb-rank[b-zqebxs1tzu] {
    font-size: var(--meb-font-size-h3-desktop, 1.25rem);
    font-weight: var(--meb-font-weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--meb-text-default);
    min-width: 2ch;
}

/* APPROVED EXCEPTION (§4.1 atomic rendering): the avatar slot is an atomic
   avatar + crown-badge composite (absolutely-positioned crown over a centered
   disc), mirroring the chat-bubble avatar slot. flex-shrink:0 keeps it from
   collapsing inside the row Grid track. Not product layout. */
.bingo-lb-avatar-slot[b-zqebxs1tzu] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.bingo-lb-crown[b-zqebxs1tzu] {
    position: absolute;
    top: calc(-1 * var(--meb-space-stack-sm));
    left: 50%;
    transform: translateX(-50%) rotate(-12deg);
    width: 1.1rem;
    height: 1.1rem;
    pointer-events: none;
}

.bingo-lb-crown svg[b-zqebxs1tzu] {
    width: 100%;
    height: 100%;
    fill: var(--meb-palette-yellow-bright, var(--meb-accent-secondary));
    stroke: var(--meb-text-default);
    stroke-width: 1;
    stroke-linejoin: round;
}

.bingo-lb-name[b-zqebxs1tzu] {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-semibold);
}

/* Screen-reader-only rank suffix on the name (visual rank is aria-hidden). */
.bingo-lb-rank-sr[b-zqebxs1tzu] {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.bingo-lb-time[b-zqebxs1tzu] {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-weight: var(--meb-font-weight-semibold);
    color: var(--meb-text-default);
}

.bingo-lb-empty[b-zqebxs1tzu] {
    margin: 0;
    padding: var(--meb-space-stack-md);
    color: var(--meb-text-muted);
    text-align: center;
    font-style: italic;
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/BingoGameplay.razor.rz.scp.css */
/* ===================================================================
   BingoGameplay.razor.css
   Scoped styles for the BingoGameplay host page.
   Only §4.1-allowed properties and token-governed values are authored here.
   No @media queries — responsive branching is owned by IsMobile in C#.
   =================================================================== */

/* Call-list grid columns — mobile render path (IsMobile → MobileBingoGameplay).
   ::deep crosses into the MobileBingoGameplay child component boundary. */
[b-22e46d3pp5] .call-list--mobile-cols {
    grid-template-columns: repeat(2, 1fr);
}

/* Call-list grid columns — desktop render path (DesktopBingoGameplay).
   ::deep crosses into the DesktopBingoGameplay child component boundary. */
[b-22e46d3pp5] .call-list--desktop-cols {
    grid-template-columns: repeat(1, 1fr);
}

/* WonByMe win overlay — bounded celebration card that SCALES with the screen
   instead of the SlideInPanel's default full viewport width: min(92vw, 460px)
   (~460px on desktop, shrinking on narrow viewports). Kept `position: fixed`
   and CENTERED so it is always in view regardless of page scroll, and entered
   with an opacity + scale transition rather than the base slide — a centered
   modal never parks off-screen, so it neither hides on scroll nor adds a
   page-wide horizontal scrollbar (the original full-width slide did both).

   Authored here (not the Desktop shell) because the SlideInPanel is a top-level
   child component there with no scoped ancestor for ::deep to hook;
   .main-gameplay-containers IS a scoped ancestor, so the ::deep selector
   resolves. Specificity (scope + panel + overlay class [+ data-open]) beats the
   base data-position / data-direction rules. */
[b-22e46d3pp5] .ui-slidein-panel.bingo-win-overlay {
    position: fixed;
    inset-inline-start: 0;
    inset-inline-end: 0;
    margin-inline: auto;
    width: min(92vw, 460px);
    top: 50%;
    transform: translateY(-50%) scale(0.92);
    visibility: hidden;
}

/* Open: scale up to full. The scale animates via the base .ui-slidein-panel
   `transition: transform` (which the base also disables under
   prefers-reduced-motion), so no new transition/@media is introduced here. */
[b-22e46d3pp5] .ui-slidein-panel.bingo-win-overlay[data-open="true"] {
    transform: translateY(-50%) scale(1);
    visibility: visible;
    pointer-events: auto;
}

/* Reserve the gameplay-area height across the interactive load window (loading overlay -> content) so the
   footer (app-shell-stack in MainLayout) doesn't ride up into the viewport and shift. Desktop's
   .layout-game-stage hugs its content, so the short post-hydration loading state previously let the footer
   move (~0.30 CLS measured on stage). Mirrors mobile's .mobile-root: min-height calc(100dvh - 80px); the
   first-paint skeleton reserves the same height on its own .meb-gameplay-skeleton root, so the footer
   position is stable from first paint through hydration to content. */
.main-gameplay-containers[b-22e46d3pp5] {
    min-height: calc(100dvh - 80px);
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/BingoGameplaySkeleton.razor.rz.scp.css */
/* First-paint join-card placeholder. ALL layout — the gameplay-area height reservation
   (MinHeight="calc(100dvh - 80px)", below the ~72px navbar, matching .main-gameplay-containers), the centering
   (Align/Justify), and the column gaps — is owned by the Stack primitives in the markup (root + inner card
   Stack), now that Stack.MinHeight is wired (ADO #8848). These rules carry visual styling only
   (size/colour/pulse). Tokens only. The pulse is pure CSS and is disabled under prefers-reduced-motion. */

/* Visual styling only — the inner Stack primitive owns the column layout of the placeholder lines. */
.meb-skel-card[b-6svp48frzt] {
    width: 100%;
    max-width: 420px;
    padding: var(--meb-space-stack-xl) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-xl);
    background: var(--meb-surface-card);
    border: var(--meb-border-width-sm) solid var(--meb-border-subtle);
    box-sizing: border-box;
}

.meb-skel-avatar[b-6svp48frzt] {
    width: 72px;
    height: 72px;
    border-radius: var(--meb-border-radius-full);
    background: var(--meb-border-subtle);
}

.meb-skel-line[b-6svp48frzt] {
    width: 100%;
    height: var(--meb-space-stack-md);
    border-radius: var(--meb-radius-md);
    background: var(--meb-border-subtle);
}

.meb-skel-line--title[b-6svp48frzt] {
    width: 50%;
    height: var(--meb-space-stack-lg);
}

.meb-skel-line--sub[b-6svp48frzt] {
    width: 70%;
}

.meb-skel-line--short[b-6svp48frzt] {
    width: 40%;
}

.meb-skel-button[b-6svp48frzt] {
    width: 60%;
    height: var(--meb-space-stack-xl);
    margin-top: var(--meb-space-stack-sm);
    border-radius: var(--meb-border-radius-full);
    background: var(--meb-border-subtle);
}

.meb-skel-avatar[b-6svp48frzt],
.meb-skel-line[b-6svp48frzt],
.meb-skel-button[b-6svp48frzt] {
    animation: meb-skel-pulse-b-6svp48frzt 1.4s ease-in-out infinite;
}

.meb-skel-text[b-6svp48frzt] {
    color: var(--meb-text-muted);
}

@keyframes meb-skel-pulse-b-6svp48frzt {
    0%, 100% {
        opacity: 1;
    }

    50% {
        opacity: 0.55;
    }
}

@media (prefers-reduced-motion: reduce) {

    .meb-skel-avatar[b-6svp48frzt],
    .meb-skel-line[b-6svp48frzt],
    .meb-skel-button[b-6svp48frzt] {
        animation: none;
    }
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/DesktopBingoGameplay.razor.rz.scp.css */
/* ===================================================================
   DesktopBingoGameplay.razor.css
   Scoped layout for the desktop bingo gameplay surface.
   Ported from LayoutCatalog 1301 (port + duplicate per user decision).
   The --bingo-mark-color variable is set on .main-gameplay-containers in
   BingoGameplay.razor and cascades down to the .layout-* consumers
   defined here.
   =================================================================== */

/* ── Stage / mark-color scope ──────────────────────────────────────── */

.layout-game-stage[b-hatm8p9xm7] {
    /* Bridge: the gameplay container sets --bingo-mark-color, the layout-*
       rules read --layout-game-mark-color. Map one to the other here.
       Fallback guards the very first paint where CurrentMarkColor may not
       have resolved yet (InteractiveAuto hydration timing). */
    --layout-game-mark-color: var(--bingo-mark-color, var(--meb-surface-section-creator));
    width: 100%;
    max-width: 100%;
    box-sizing: border-box;
    padding: var(--meb-space-stack-xl);
    /* Mark-tinted gameplay surface — port of .layout-demo--gameplay in 1301.
       The catalog wraps the stage in a separately-tinted card; the live page
       has no analog wrapper, so the tint + border collapse into the stage
       itself. The 28% mix matches 1301 exactly. */
    background: color-mix(in srgb, var(--layout-game-mark-color) 28%, var(--meb-surface-card));
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
    border-radius: var(--meb-border-radius-xl);
    overflow: visible;
    position: relative;
}

/* ── PR2 (Inc2d): "step into the room" JOIN reveal ──────────────────────
   Driven by data-meb-reveal="entering" on the stage -- a Blazor-owned attribute set ONLY
   after a JOIN tap whose board loaded (never on a prerender, never a JS-mutated class). All
   motion is transform/opacity (GPU-safe on every WebView; no color/backdrop animation) and
   lives inside a prefers-reduced-motion: no-preference guard, so reduced-motion users get the
   resting room instantly. Keyframes are global (animation.css). */
@media (prefers-reduced-motion: no-preference) {
    /* The room itself: a subtle zoom + fade. */
    .layout-game-stage[data-meb-reveal="entering"][b-hatm8p9xm7] {
        animation: mebUiAnimRoomEnter 380ms ease-out both;
    }

    /* Header chrome assembles from above, staggered via NAMED selectors (not :nth-child --
       SHARE is conditional and the rail is N=0-suppressed, which would shift indices). */
    .layout-game-stage[data-meb-reveal="entering"] .layout-game-room[b-hatm8p9xm7] {
        animation: mebUiAnimRoomChromeRise 320ms ease-out both;
        animation-delay: 90ms;
    }
    .layout-game-stage[data-meb-reveal="entering"] .layout-game-header-actions[b-hatm8p9xm7] {
        animation: mebUiAnimRoomChromeRise 320ms ease-out both;
        animation-delay: 170ms;
    }
    /* Target the rail by its production class, not data-testid (a test-id rename must not kill the stagger). */
    .layout-game-stage[data-meb-reveal="entering"] .layout-game-player-group[b-hatm8p9xm7] {
        animation: mebUiAnimRoomChromeRise 320ms ease-out both;
        animation-delay: 250ms;
    }

    /* The cross-fade: the held calm card fades out over the entering stage. */
    .layout-game-reveal-overlay[b-hatm8p9xm7] {
        animation: mebUiAnimFadeOut 300ms ease-out both;
    }
}

/* Held calm card overlaying the entering stage (above the stage, below modals; never blocks taps). */
.layout-game-reveal-overlay[b-hatm8p9xm7] {
    position: fixed;
    inset: 0;
    z-index: 50;
    pointer-events: none;
    /* Flex-center the held card to the SAME position as the pre-join WelcomeShell (which centers inside
       .main-gameplay-containers). Without this the fixed overlay top-aligns the card and it snaps up
       ~a nav-height at the start of the cross-fade -- the exact jump the cross-fade is meant to mask. */
    display: flex;
    align-items: center;
    justify-content: center;
    /* Hidden at rest so REDUCED-MOTION users (who get no fade-out animation) never see a stuck opaque
       card over the joined board for the disarm window. The no-preference fade-out below animates
       opacity 1->0 (animation-fill-mode: both), overriding this. */
    opacity: 0;
}

/* Narrow desktop (above the 768px mobile boundary): trim the outer stage
   padding and the board↔tabs gap so the two columns aren't cramped and the
   folder tabs have room. The board stays square; only reclaimed whitespace
   changes. */
@media (max-width: 1100px) {
    .layout-game-stage[b-hatm8p9xm7] {
        padding: var(--meb-space-stack-md);
    }

    .layout-game-stage .layout-game-workspace[b-hatm8p9xm7] {
        gap: var(--meb-space-stack-sm);
    }

    /* Trim cell padding so the small square cells keep a usable line box —
       6-7 char words (Farmer, Station, Balloon) fit instead of orphaning a
       letter; 8+ char words still hyphenate cleanly via the soft hyphens. */
    .layout-board-grid .layout-board-cell[b-hatm8p9xm7] {
        padding: var(--meb-space-stack-xs) var(--meb-space-inline-xs);
    }
}

.layout-game-stage .layout-game-header[b-hatm8p9xm7],
.layout-game-stage .layout-game-workspace[b-hatm8p9xm7] {
    width: 100%;
}

/* ── Header row ────────────────────────────────────────────────────── */

/* 3-col grid (room | actions | players) — matches the 1301 reference
   <Grid TemplateColumns="minmax(0, 1fr) auto minmax(0, 1fr)">. Side
   columns flex equally so the actions stay visually centered even when
   the room name + description are long; the auto middle keeps the
   buttons content-sized. */
.layout-game-header[b-hatm8p9xm7] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
    grid-template-rows: 1fr;
    align-items: center;
    gap: var(--meb-space-stack-md);
    width: 100%;
    margin-bottom: var(--meb-space-stack-lg);
}

.layout-game-room[b-hatm8p9xm7] {
    min-width: 0;
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--meb-space-stack-md);
}

.layout-game-room-details[b-hatm8p9xm7] {
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.layout-game-header-actions[b-hatm8p9xm7] {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-sm);
    min-width: max-content;
}

/* Player rail sits in the right column. AvatarGroup brings its own
   overlap stacking (margin-left: -12px per .avatar-stack) from
   wwwroot/css/components/avatar-group.css — this rule only positions
   the rail flush-right and lets it wrap if 8 avatars would overflow. */
.layout-game-player-group[b-hatm8p9xm7] {
    justify-content: flex-end;
    flex-wrap: wrap;
    min-width: 0;
}

/* The game icon (.layout-game-room-avatar until #10783) is now the shared GameIcon, which owns its
   own box and sizes the inline catalog SVG. */

.layout-game-room-name[b-hatm8p9xm7] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h2-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}

.layout-game-room-description[b-hatm8p9xm7] {
    margin: 0;
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    line-height: var(--meb-font-lineheight-body);
}

/* ── Workspace grid (board + sidepanel) ────────────────────────────── */

.layout-game-workspace[b-hatm8p9xm7] {
    display: grid;
    grid-template-columns: minmax(0, 0.98fr) minmax(0, 1.02fr);
    width: 100%;
    max-width: 100%;
    inline-size: 100%;
    align-self: stretch;
    /* No fixed height. The board card hugs its SQUARE grid (aspect-ratio on
       .layout-board-grid derives height from width — so cells stay square at
       every width and there is no empty band below the grid), and the tabs
       column stretches to match via the grid's default align-items: stretch,
       scrolling internally. aspect-ratio gives the grid a definite height from
       its known width, so the old vw-clamp (and its back-nav reflow guard) is
       no longer needed. */
    min-height: 0;
    overflow: visible;
    gap: var(--meb-space-stack-lg);
}

.layout-game-workspace > *[b-hatm8p9xm7] {
    width: 100%;
    max-width: 100%;
    min-width: 0;
    min-height: 0;
    justify-self: stretch;
}

/* ── Board card ────────────────────────────────────────────────────── */

.layout-board-card[b-hatm8p9xm7] {
    width: 100%;
    min-width: 0;
    /* Hug the heading + square grid (defines the workspace row height). */
    height: auto;
    overflow: hidden;
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
    border-radius: var(--meb-radius-pagination, var(--meb-border-radius-xl));
    background: var(--meb-surface-card);
    box-shadow: none;
    display: flex;
    flex-direction: column;
    gap: var(--meb-space-stack-md);
    padding: var(--meb-space-stack-md);
    box-sizing: border-box;
}

/* The grid wrapper inside the board card takes the remaining height so
   the 5×5 grid fits within the workspace height clamp. */
.layout-board-mark-scope[b-hatm8p9xm7] {
    width: 100%;
    flex: 0 0 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}

/* ── Board heading (ProgressBar + BINGO letters) ───────────────────── */

.layout-board-heading[b-hatm8p9xm7] {
    width: 100%;
    height: 62px;
    --progressbar-color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    /* Light neutral track so the unfilled BINGO letters (muted dark text)
       have enough contrast to read. */
    --progressbar-track: var(--meb-surface-input-filled);
    --progressbar-radius: var(--meb-border-radius-full);
}

.layout-board-heading-letters[b-hatm8p9xm7] {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: 1fr;
    align-items: center;
    align-content: center;
    width: 100%;
    height: 100%;
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h1-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}

.layout-board-heading-letters > span[b-hatm8p9xm7] {
    display: block;
    width: 100%;
    text-align: center;
    /* Default = letters not yet earned: solid dark on the light track for
       readable contrast (muted gray was too low contrast against
       --meb-surface-input-filled). */
    color: var(--meb-text-default);
}

/* Letters earned this round: white on the colored fill. */
.layout-board-heading-letters .current-bingo-progress[b-hatm8p9xm7] {
    color: var(--meb-text-inverse);
    text-shadow: 0 1px 2px color-mix(in srgb, var(--meb-text-default) 25%, transparent);
}

/* ── 5×5 board grid + cells ────────────────────────────────────────── */

.layout-board-grid[b-hatm8p9xm7] {
    /* Fill the board card's available height so the grid bottom meets the
       card bottom — no empty gap below the board. The card height is bounded
       by the workspace height clamp, so this flex chain has a definite height
       and grid-template-rows: repeat(5, 1fr) divides it evenly.

       aspect-ratio was removed from the wrapper (Step 2U had squared it by
       width, which left an empty band below the grid whenever the card was
       taller than the width-derived square). aspect-ratio is on neither the
       grid nor the cell now, so the §13.3 ss2J dual-aspect-ratio reflow bug
       cannot recur. Cells end up slightly taller than wide — the reference
       cells are near-square and the priority here is filling the card. */
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: repeat(5, minmax(0, 1fr));
    width: 100%;
    max-width: 100%;
    /* Square the grid by width so the 5 equal rows make every cell 1:1. The
       card hugs this height (no fill), so cells stay square at every width. */
    aspect-ratio: 1 / 1;
    flex: 0 0 auto;
    min-height: 0;
    margin-inline: auto;
    overflow: hidden;
    border-radius: var(--meb-border-radius-lg);
    background: var(--meb-surface-card);

    /* Interior gridlines: a single quiet neutral divider. Replaced the former
       8-hue Figma rainbow (file XquF2FKJaKOhOy7jYWwMt5, node 293:10201). The
       saturated multi-hue lines were luminance-inconsistent (yellow ~1.5:1
       nearly invisible, navy ~13.7:1 a hard near-black rule), carried no
       information, and inverted the board's visual hierarchy -- the dividers
       shouted over the call text players need to read. A uniform low-contrast
       divider lets the board read as one calm table with the words as the focal
       point. Kept as the 8 --board-line-* slots so the nth-child border
       machinery below is unchanged. Use --meb-border-subtle (a true low-chroma
       neutral), NOT --meb-border-default (pure black). Mirrors the mobile
       shell. */
    --board-line: var(--meb-border-subtle);
    --board-line-v1: var(--board-line);
    --board-line-v2: var(--board-line);
    --board-line-v3: var(--board-line);
    --board-line-v4: var(--board-line);
    --board-line-h1: var(--board-line);
    --board-line-h2: var(--board-line);
    --board-line-h3: var(--board-line);
    --board-line-h4: var(--board-line);
}

.layout-board-cell[b-hatm8p9xm7] {
    /* No aspect-ratio on the cell — Step 2U restored aspect-ratio: 1/1
       on .layout-board-grid (the wrapper) so the cell stays sized by
       grid-template-rows: repeat(5, 1fr) only. The §13.3 ss2J bug came
       from aspect-ratio on BOTH grid AND cell creating a circular
       constraint; keeping it off the cell keeps that chain broken. */
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    min-height: 0;
    padding: 12px 10px;
    /* Base horizontal divider — colored per row below; this is the fallback. */
    border-bottom: var(--meb-border-width-md) solid var(--board-line-h1);
    text-align: center;
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
    background: var(--meb-surface-card);
    position: relative;
    overflow: hidden;
    isolation: isolate;
    cursor: pointer;
    box-sizing: border-box;
    /* Establish a query container so the cell text can auto-size to the cell
       width (cqi units below). */
    container-type: inline-size;
}

.layout-board-cell-text[b-hatm8p9xm7] {
    position: relative;
    z-index: 5;
    /* Full line box so the most words fit on one line in the square cell. */
    width: 100%;
    /* Text fitting: the server pre-inserts soft hyphens into long tokens
       (BoardText.SoftHyphenate), so a word that overflows wraps with a clean
       hyphen ("Camp-ground") instead of orphaning a letter. overflow-wrap:
       break-word stays as the LAST-RESORT break for unhyphenatable tokens
       (hashtags, codes) so they wrap rather than clip inside the cell's
       overflow:hidden. NB: text-wrap:balance is intentionally NOT used here —
       with the abundant soft hyphens it "balances" by breaking words that
       would otherwise fit (e.g. "Des-ert"); soft hyphens must stay a safety
       net that only fires on genuine overflow. Font auto-fits via cqi (12px
       floor / 14px cap); the plain 13px is the no-container-query fallback. */
    overflow-wrap: break-word;
    hyphens: auto;
    font-size: 13px;
    font-size: clamp(12px, 11cqi, 14px);
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-semibold);
}

.layout-board-cell--taken[b-hatm8p9xm7] {
    background: var(--meb-surface-card);
}

.layout-board-cell--taken > .layout-board-cell-text[b-hatm8p9xm7] {
    text-shadow: 0 var(--meb-border-width-sm) 0 color-mix(in srgb, var(--meb-surface-card) 92%, transparent);
}

/* Grid dividers emulate the Figma board: 8 interior lines (4 vertical +
   4 horizontal), each a DISTINCT brand hue — no color repeats across the
   grid. Solid 2px (border-width-md) so every line reads clearly. */

/* Vertical column dividers (left → right) — exact Figma 293:10201 order. */
.layout-board-grid > .layout-board-cell:nth-child(5n + 1)[b-hatm8p9xm7] {
    border-right: var(--meb-border-width-md) solid var(--board-line-v1);
}

.layout-board-grid > .layout-board-cell:nth-child(5n + 2)[b-hatm8p9xm7] {
    border-right: var(--meb-border-width-md) solid var(--board-line-v2);
}

.layout-board-grid > .layout-board-cell:nth-child(5n + 3)[b-hatm8p9xm7] {
    border-right: var(--meb-border-width-md) solid var(--board-line-v3);
}

.layout-board-grid > .layout-board-cell:nth-child(5n + 4)[b-hatm8p9xm7] {
    border-right: var(--meb-border-width-md) solid var(--board-line-v4);
}

/* Horizontal row dividers (top → bottom) — exact Figma 293:10201 order. */
.layout-board-grid > .layout-board-cell:nth-child(-n + 5)[b-hatm8p9xm7] {
    border-bottom: var(--meb-border-width-md) solid var(--board-line-h1);
}

.layout-board-grid > .layout-board-cell:nth-child(n + 6):nth-child(-n + 10)[b-hatm8p9xm7] {
    border-bottom: var(--meb-border-width-md) solid var(--board-line-h2);
}

.layout-board-grid > .layout-board-cell:nth-child(n + 11):nth-child(-n + 15)[b-hatm8p9xm7] {
    border-bottom: var(--meb-border-width-md) solid var(--board-line-h3);
}

.layout-board-grid > .layout-board-cell:nth-child(n + 16):nth-child(-n + 20)[b-hatm8p9xm7] {
    border-bottom: var(--meb-border-width-md) solid var(--board-line-h4);
}

/* Last row + last column carry no interior divider (grid edge). */
.layout-board-grid > .layout-board-cell:nth-child(n + 21)[b-hatm8p9xm7] {
    border-bottom: none;
}

/* Splat overlay — sits under the cell text on taken cells. */
.layout-board-splat[b-hatm8p9xm7],
.layout-board-splat-color[b-hatm8p9xm7] {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    transform: rotate(var(--mark-rotate, 0deg));
    z-index: 2;
}

.layout-board-splat[b-hatm8p9xm7] {
    object-fit: cover;
    object-position: center;
    display: block;
    opacity: 0.12;
}

.layout-board-splat-color[b-hatm8p9xm7] {
    z-index: 3;
    background: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    -webkit-mask-image: url('/_content/LiveEventBingo.Shared/css/assets/images/logos/G4.png');
    -webkit-mask-size: cover;
    -webkit-mask-position: center;
    -webkit-mask-repeat: no-repeat;
    mask-image: url('/_content/LiveEventBingo.Shared/css/assets/images/logos/G4.png');
    mask-size: cover;
    mask-position: center;
    mask-repeat: no-repeat;
    mask-mode: alpha;
    mix-blend-mode: multiply;
    opacity: 0.42;
}

.layout-board-cell--free[b-hatm8p9xm7] {
    font-family: var(--meb-font-family-primary);
    background: var(--meb-surface-card) !important;
    cursor: default;
}

.layout-board-cell--free .layout-board-cell-text[b-hatm8p9xm7] {
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-tight);
}

/* Bingo line highlight (winning row/column/diagonal). */
.layout-board-cell--bingo-line[b-hatm8p9xm7] {
    filter: contrast(0.4);
}

/* Cell tap animation — gateplay calls this when a cell is just taken. */
.layout-board-cell--animate[b-hatm8p9xm7] {
    animation: layout-board-cell-pop-b-hatm8p9xm7 0.6s ease-in-out;
}

@keyframes layout-board-cell-pop-b-hatm8p9xm7 {
    0%   { transform: scale(1) rotate(0deg); }
    30%  { transform: scale(1.15) rotate(4deg); }
    60%  { transform: scale(1.08) rotate(-3deg); }
    100% { transform: scale(1) rotate(0deg); }
}

/* ── Sidepanel shell + folder tabs ─────────────────────────────────── */

.layout-sidepanel-shell[b-hatm8p9xm7] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    justify-items: stretch;
    min-width: 0;
    min-height: 0;
    width: 100%;
    max-width: 100%;
    inline-size: 100%;
    height: 100%;
    max-height: 100%;
    align-self: stretch;
    justify-self: stretch;
    overflow: hidden;
    background: transparent;
    gap: var(--meb-space-stack-md);
}

/* Options tab panel — hosts the ThemePicker + game facts. */
.layout-game-options-panel[b-hatm8p9xm7] {
    display: flex;
    flex-direction: column;
    gap: var(--meb-space-stack-md);
    height: 100%;
    overflow-y: auto;
    padding: var(--meb-space-stack-md);
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
}

.layout-game-options-section[b-hatm8p9xm7] {
    display: flex;
    flex-direction: column;
    gap: var(--meb-space-stack-xs);
}

.layout-game-options-section > p[b-hatm8p9xm7] {
    margin: 0;
    font-size: var(--meb-font-size-body-desktop);
    color: var(--meb-text-default);
}

.layout-game-options-heading[b-hatm8p9xm7] {
    margin: 0;
    color: var(--layout-game-mark-color, var(--meb-accent-primary));
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}

/* The compact ThemePicker grid that used to live here was removed: it was a
 * page-local ::deep control variant reaching into ThemePicker's internals to
 * override its grid and shrink its disc to 44px. Bingo-Template §23 forbids
 * ::deep as a page-local control variant, and §23.3 says to add the prop when a
 * layout need can't be expressed through existing props. It now ships as
 * ThemePicker's own Compact parameter (.ui-themepicker--compact in
 * theme-picker.css) — pass Compact="true" at the usage site.
 *
 * The old 44px disc shrink is deliberately NOT carried over. It was arbitrary;
 * the DS default 48px disc is what lets the colour grid line up with the
 * AvatarPicker grid above it in this panel.
 */

/* #9430 identity block — the avatar catalog's loading note and the inline save error.
   Typography/colour only; the identity row and the picker own their own internals.

   The error selector is deliberately qualified with the parent: `.layout-game-options-section > p`
   above (specificity 0,1,1) styles every value paragraph in this panel and would otherwise BEAT a
   bare .layout-game-identity-error (0,1,0) and repaint the error back to the default text colour. */
.layout-game-identity-caption[b-hatm8p9xm7] {
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.layout-game-options-section > p.layout-game-identity-error[b-hatm8p9xm7] {
    margin: 0;
    color: var(--meb-accent-danger);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

/* Keep Screen Awake row — toggle + descriptive label inline. */
.layout-game-options-awake-row[b-hatm8p9xm7] {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--meb-space-inline-md);
    flex-wrap: wrap;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    line-height: var(--meb-font-lineheight-body);
}

/* Facts grid — 4 columns at desktop, collapses to 2 at narrow widths
   to match the catalog's 900px breakpoint (1301 CSS line 1097). */
.layout-game-options-facts[b-hatm8p9xm7] {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: var(--meb-space-stack-md) var(--meb-space-inline-md);
    width: 100%;
}

@media (max-width: 900px) {
    .layout-game-options-facts[b-hatm8p9xm7] {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* Step 8B — owner-only Game Controls row. Pause/Resume sits inline with
   Finish so the destructive action is visible alongside the reversible
   one; on narrow viewports the inline collapses into a stack. Uses the
   shared Inline-like pattern (gap + flex-wrap) without taking on a
   primitive — the section heading owns vertical rhythm above, and the
   MebButton siblings own their own internal padding per §23.3. */
.layout-game-options-owner-actions[b-hatm8p9xm7] {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--meb-space-inline-md);
    align-items: center;
}

/* #11134 (S6-T3, board OwnerOptions): the Surveys switch hint and its notice line under Game Controls.
   Text only; the row's layout comes from the Stack props in the markup. */
.layout-game-options-surveys-hint[b-hatm8p9xm7] {
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
}

.layout-game-options-surveys-notice[b-hatm8p9xm7] {
    margin: 0;
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-sm-desktop);
}

/* Version footer — sits at the bottom of the Options panel, center-aligned. */
.layout-game-options-version[b-hatm8p9xm7] {
    margin: auto 0 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    line-height: var(--meb-font-lineheight-body);
    text-align: center;
}

/* #9492: the ::deep .meb-toggle block that used to live here is GONE.
 *
 * It hand-rolled a pill switch onto a raw <input type="checkbox"> -- a page-local
 * ::deep control variant, which Bingo-Template §23 forbids (::deep is for crossing
 * nested shared-control boundaries, never for styling a control from the page).
 * It was a documented Step 2Z porting artifact (Gameplay-Flow.md:943): mobile had
 * already adopted the DS <Toggle> primitive for the same control, and desktop kept
 * a copy of mobile's old rules instead.
 *
 * The Awake row now renders <Toggle>, exactly as MobileBingoGameplay does, so the
 * chrome comes from the DS component and its own token resolution. One control, one
 * implementation, no ::deep. The row's own flex layout below is untouched -- that
 * styles a plain div this shell renders, not a control's internals.
 *
 * Visual note: the DS Toggle uses its own resolved "toggle" tokens, so the checked
 * state no longer picks up --layout-game-mark-color. That tint was part of the hack;
 * losing it is what makes the two shells agree.
 */

/* Folder tab visual contract — see 1301 reference. */
.layout-game-stage[b-hatm8p9xm7]  .layout-game-tabs {
    display: flex;
    flex: 0 0 100%;
    width: 100%;
    max-width: 100%;
    inline-size: 100%;
    min-width: 100%;
    height: 100%;
    max-height: 100%;
    min-height: 0;
    overflow: hidden;
    box-sizing: border-box;
    /* Size containment so the tab content (long chat / call list / log) can't
       inflate the workspace grid row. The row height is defined by the SQUARE
       board card; the tabs column stretches to match it and its panels scroll
       internally. Without this, a long chat stretches the board (reintroducing
       the oversized-board / empty-band problem) since the workspace has no
       fixed height (it hugs the square grid). */
    contain: size;
    --tabs-horizontal-inset: var(--meb-space-stack-none);
    --tabs-radius: var(--meb-border-radius-xl);
    --tabs-border-width: var(--meb-border-width-sm);
    --tabs-segmented-tab-background: var(--meb-surface-card);
    --tabs-segmented-tab-active-background: var(--meb-surface-card);
    --tabs-segmented-tab-hover-background: var(--meb-surface-card);
    --tabs-segmented-body-background: var(--meb-surface-card);
}

/* Use flex column (not grid) so the visible TabPanel slots between the
   header and the bottom regardless of how many hidden panels precede it
   in source order. grid-template-rows breaks when multiple TabPanels
   are siblings of the header — only one is visible at a time. */
[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabs-surface {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 100%;
    min-width: 100%;
    height: 100%;
    max-height: 100%;
    min-height: 0;
    overflow: hidden;
    box-sizing: border-box;
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabs-header,
[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tablist {
    width: 100%;
    max-width: 100%;
    min-width: 100%;
    box-sizing: border-box;
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabs-header {
    flex: 0 0 auto;
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tablist {
    display: grid;
    /* minmax(0,1fr), not the 100px tab-width floor, so the tabs always
       share the tablist width and shrink to fit narrow desktop widths instead
       of overflowing (the 4th tab was clipping at ~769px where 4×100px > the
       ~323px tabs column).
       #11132 (S6-polish, E2E-B11, UI-14): one column per tab, in one row, whatever the tab
       count. The old fixed repeat(4, ...) pushed the 5th tab (the owner's Options, or a
       player's Options once the Poll tab shows) onto an implicit second row. */
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tab {
    width: 100%;
    min-width: 0;
    max-width: none;
    flex: none;
    box-sizing: border-box;
    background: var(--meb-surface-card);
    position: relative;
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tab:hover,
[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tab:active,
[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tab.active {
    background: var(--meb-surface-card);
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tab.active::after {
    background: var(--meb-surface-card);
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabs-body {
    width: 100%;
    max-width: 100%;
    min-width: 100%;
    box-sizing: border-box;
    box-shadow: none;
    background: var(--meb-surface-card);
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabs-body[hidden] {
    display: none !important;
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabs-body:not([hidden]) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    max-height: 100%;
    overflow: hidden;
}

[b-hatm8p9xm7] .layout-game-tabs.variant-folder .ui-tabpanel {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 100%;
    min-width: 0;
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
    max-height: 100%;
    overflow: hidden;
    background: transparent;
    box-sizing: border-box;
    padding: var(--meb-space-stack-md);
}

/* Unread chat badge — sits to the right of the Chat tab label. */
.layout-game-tab-badge[b-hatm8p9xm7] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    margin-inline-start: var(--meb-space-inline-xs);
    padding: 0 6px;
    border-radius: var(--meb-border-radius-full);
    background: var(--meb-accent-danger, #ff3d3d);
    color: var(--meb-text-inverse);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-bold);
}

/* ── Chat panel ────────────────────────────────────────────────────── */

.layout-game-chat-panel[b-hatm8p9xm7] {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
    gap: var(--meb-space-stack-sm);
}

.layout-game-chat-list[b-hatm8p9xm7] {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Vertical gap between chat message bubbles — flex column owns spacing
       for this scrollable dynamic content container (approved exception). */
    display: flex;
    flex-direction: column;
    gap: var(--meb-space-stack-sm);
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-xs);
}

/* ChatBubble tail — ported from the 1301 catalog reference
   (LayoutCatalog.razor.css ~1468-1492). The shared chat-bubble.css ships a
   1px transparent border, symmetric corners, and a tail lifted by the border
   width. The catalog perfects the gameplay-surface tail (flush bottom +
   squared tail-side corner) but scopes those rules to .layout-demo--gameplay,
   so they never reached the live shell — the tail read "off." Port them here.
   - border: none → painted bottom edge == box bottom edge
   - squared tail-side corner → triangle meets the bubble cleanly
   - tail bottom: 0 → triangle flush with the bubble's bottom */
.layout-game-chat-list[b-hatm8p9xm7]  .ui-chat-bubble-surface {
    border: none;
}

.layout-game-chat-list[b-hatm8p9xm7]  .ui-chat-bubble-surface--other {
    border-radius: var(--meb-border-radius-lg) var(--meb-border-radius-lg) var(--meb-border-radius-lg) 0;
}

.layout-game-chat-list[b-hatm8p9xm7]  .ui-chat-bubble-surface--current {
    border-radius: var(--meb-border-radius-lg) var(--meb-border-radius-lg) 0 var(--meb-border-radius-lg);
}

.layout-game-chat-list[b-hatm8p9xm7]  .ui-chat-bubble-surface--other::before,
.layout-game-chat-list[b-hatm8p9xm7]  .ui-chat-bubble-surface--current::after {
    bottom: 0;
}

/* Chat input shell — Step 2W replaced plain chat rows with ChatBubble, so
   the legacy .layout-game-chat-row / -time / -username / -text rules are
   gone. This shell now hosts an <Input Variant="Outlined"> + absolutely-
   positioned ghost <MebButton> per the 1301 reference (catalog rules
   ~lines 2023-2091). */
.layout-game-chat-input-shell[b-hatm8p9xm7] {
    position: relative;
    flex: 0 0 auto;
    width: 100%;
    padding: var(--meb-space-stack-none) var(--meb-space-inline-md) var(--meb-space-stack-md);
    --layout-game-chat-input-height: calc(var(--meb-size-touch-target) + var(--meb-space-stack-md));
}

.layout-game-chat-input-shell[b-hatm8p9xm7]  .ui-input-wrapper {
    width: 100%;
}

.layout-game-chat-input-shell[b-hatm8p9xm7]  .ui-input {
    min-height: var(--layout-game-chat-input-height);
    padding-right: calc(var(--meb-size-touch-target) + var(--meb-space-inline-sm));
    border-radius: var(--meb-border-radius-full);
}

/* Ghost send button overlays the right side of the input, color follows
   the mark color so the affordance restyles when ThemePicker fires. */
[b-hatm8p9xm7] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost {
    position: absolute;
    top: calc((var(--layout-game-chat-input-height) / 2) + var(--meb-space-stack-none));
    right: calc(var(--meb-space-inline-md) + var(--meb-space-inline-md));
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--meb-size-touch-target);
    min-width: var(--meb-size-touch-target);
    height: var(--meb-size-touch-target);
    min-height: var(--meb-size-touch-target);
    padding: 0;
    border: 0;
    border-radius: var(--meb-border-radius-full);
    background: transparent;
    box-shadow: none;
    color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    line-height: var(--meb-font-lineheight-tight);
    transform: translateY(-50%);
}

[b-hatm8p9xm7] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost:hover,
[b-hatm8p9xm7] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost:active {
    background: transparent;
    box-shadow: none;
    color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
}

[b-hatm8p9xm7] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost:focus-visible {
    box-shadow: var(--meb-button-ghost-focus-shadow);
}

[b-hatm8p9xm7] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost .meb-btn__label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    line-height: 0;
}

[b-hatm8p9xm7] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost .layout-game-chat-send-svg {
    display: block;
    flex: 0 0 auto;
    width: 19px;
    height: 16px;
    color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
}

/* ── Call list / players / game-log panels ────────────────────────── */

.layout-game-list-panel[b-hatm8p9xm7] {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    height: 100%;
    gap: var(--meb-space-stack-sm);
}

.layout-game-list-scroll[b-hatm8p9xm7] {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: var(--meb-space-stack-xs) var(--meb-space-inline-xs);
}

.layout-game-list-count[b-hatm8p9xm7] {
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-desktop);
    text-align: center;
}

/* ── Call list search shell (port of 1301 layout-game-search-shell) ──── */

.layout-game-search-shell[b-hatm8p9xm7] {
    position: relative;
    width: 100%;
}

.layout-game-search-icon[b-hatm8p9xm7] {
    position: absolute;
    top: 50%;
    left: 16px;
    z-index: 2;
    transform: translateY(-50%);
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-desktop);
    pointer-events: none;
}

.layout-game-search-shell[b-hatm8p9xm7]  .ui-input-wrapper {
    width: 100%;
}

.layout-game-search-shell[b-hatm8p9xm7]  .ui-input {
    min-height: 48px;
    padding-left: 42px;
    border-radius: var(--meb-border-radius-full);
}

/* Call list as Chip pills — taken chips use the mark color, available chips
   use a neutral surface. Inline-wrap with start-aligned packing matches
   1301 layout-game-calllist-chips behavior. */
.layout-game-calllist-chips[b-hatm8p9xm7] {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-start;
    justify-content: flex-start;
    gap: var(--meb-space-inline-xs);
    padding-bottom: var(--meb-space-stack-md);
}

.layout-game-calllist-chips[b-hatm8p9xm7]  .layout-game-chip {
    flex: 0 1 auto;
    width: fit-content;
    max-width: 100%;
    min-height: var(--meb-size-touch-target);
    justify-content: center;
}

.layout-game-calllist-chips[b-hatm8p9xm7]  .layout-game-chip--selected {
    background: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    color: var(--meb-text-inverse);
    border-color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
}

.layout-game-calllist-chips[b-hatm8p9xm7]  .layout-game-chip--default {
    background: var(--meb-accent-secondary);
    color: var(--meb-text-default);
    border-color: var(--meb-border-default);
}

.layout-game-player-row[b-hatm8p9xm7] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--meb-space-inline-sm);
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    margin-bottom: var(--meb-space-stack-xs);
    border-radius: var(--meb-border-radius-lg);
    background: color-mix(in srgb, var(--layout-game-mark-color) 18%, var(--meb-surface-card));
    color: var(--meb-text-default);
}

.layout-game-player-row--bingo[b-hatm8p9xm7] {
    background: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    color: var(--meb-text-inverse);
}

.layout-game-player-avatar[b-hatm8p9xm7] {
    width: 32px;
    height: 32px;
    border-radius: var(--meb-border-radius-full);
    background: var(--meb-surface-card);
    overflow: hidden;
    flex: 0 0 auto;
}

/* SVG sizing fix: the player avatar
   MarkupString is an inline <svg viewBox="0 0 180 180"> with no intrinsic
   size, so we scale it to the wrapper here. */
.layout-game-player-avatar > svg[b-hatm8p9xm7] {
    width: 100%;
    height: 100%;
    display: block;
}

.layout-game-player-name[b-hatm8p9xm7] {
    flex: 1;
    min-width: 0;
    font-weight: var(--meb-font-weight-semibold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.layout-game-player-status[b-hatm8p9xm7] {
    flex: 0 0 auto;
    font-weight: var(--meb-font-weight-semibold);
    text-transform: uppercase;
    font-size: var(--meb-font-size-body-sm-desktop);
}

/* ── Game Log (port of 1301 layout-game-log-*) ────────────────────────
   3-col grid per row: marker | time | message. Event color flows in
   via --layout-game-log-event-color (set per-row by the page). The
   --bingo variant no longer fills the row background; the marker dot
   is the kind indicator now (matches 1301 exactly). */

.layout-game-log-note[b-hatm8p9xm7] {
    width: 100%;
    margin: 0 0 var(--meb-space-stack-sm) 0;
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.layout-game-log-row[b-hatm8p9xm7] {
    display: grid;
    width: 100%;
    max-width: 100%;
    inline-size: 100%;
    min-width: 0;
    box-sizing: border-box;
    grid-template-columns: auto minmax(4.75rem, auto) minmax(0, 1fr);
    gap: var(--meb-space-inline-xs);
    align-items: center;
    padding: var(--meb-space-stack-xs) 0;
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-sm-desktop);
}

.layout-game-log-marker[b-hatm8p9xm7] {
    align-self: center;
    width: var(--meb-space-inline-sm);
    height: var(--meb-space-inline-sm);
    border-radius: var(--meb-border-radius-full);
    background: var(--layout-game-log-event-color, var(--meb-accent-secondary));
}

.layout-game-log-time[b-hatm8p9xm7] {
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-semibold);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-tight);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.layout-game-log-message[b-hatm8p9xm7] {
    display: flex;
    flex-wrap: wrap;
    gap: 0 var(--meb-space-inline-xs);
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
    margin: 0;
    color: var(--meb-text-default);
    line-height: var(--meb-font-lineheight-tight);
}

/* ── Pre-start welcome shell ─────────────────────────────────────────── */
/* PR1 (Inc2d): the pre-start shell chrome (.layout-game-prestart / -card / -avatar)
   moved to WelcomeShell.razor(.css), now the SOLE renderer of that markup -- both the
   calm PublicVisitor branch and the in-stage Creator/Player welcome use <WelcomeShell>.
   Removed here to avoid a dead, drift-prone duplicate. The min-height floor that kept
   the stage from collapsing is preserved in WelcomeShell.razor.css (same
   --layout-game-workspace-height var + clamp fallback). */

/* The pre-start card BODY (title / description / start-time / welcome copy /
   actions / joined affirmation) moved to the shared WelcomeCardBody component
   and its scoped CSS — see WelcomeCardBody.razor(.css). The host keeps only the
   outer section + card + avatar above. */

/* ── Leaderboard popup (Step 5) ─────────────────────────────────────── */

/* Opened from the player-rail avatar / overflow click. One row per player
   with avatar, name, ProgressBar, and X / GridSize score. Sorted by
   MaxBingoLine descending in the markup. */
.layout-game-leaderboard-list[b-hatm8p9xm7] {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    width: 100%;
    min-width: 280px;
    max-width: 560px;
}

/* Row track: rank | avatar | name | BINGO pill. Rows touch for zebra striping. */
.layout-game-leaderboard-row[b-hatm8p9xm7] {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--meb-space-inline-md);
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
}

/* Zebra striping (faint section tint on alternating rows). */
.layout-game-leaderboard-row:nth-child(odd)[b-hatm8p9xm7] {
    background: color-mix(in srgb, var(--meb-surface-section-creator) 8%, var(--meb-surface-card));
}

/* The current viewer's own row -- mark-tinted highlight. Doubled class so it
   beats the :nth-child(odd) zebra rule (equal specificity, declared later);
   otherwise an odd-positioned me-row renders identical to a zebra stripe. */
.layout-game-leaderboard-row.layout-game-leaderboard-row--me[b-hatm8p9xm7] {
    background: color-mix(in srgb, var(--bingo-mark-color, var(--meb-surface-section-creator)) 16%, var(--meb-surface-card));
}

.layout-game-leaderboard-rank[b-hatm8p9xm7] {
    font-size: var(--meb-font-size-h3-desktop, 1.25rem);
    font-weight: var(--meb-font-weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--meb-text-default);
    min-width: 2ch;
}

/* APPROVED EXCEPTION (§4.1 atomic rendering): avatar + crown-badge composite,
   same pattern as the chat-bubble / ended-leaderboard avatar slots. */
.layout-game-leaderboard-avatar-slot[b-hatm8p9xm7] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.layout-game-leaderboard-crown[b-hatm8p9xm7] {
    position: absolute;
    top: calc(-1 * var(--meb-space-stack-sm));
    left: 50%;
    transform: translateX(-50%) rotate(-12deg);
    width: 1.2rem;
    height: 1.2rem;
    pointer-events: none;
}

.layout-game-leaderboard-crown svg[b-hatm8p9xm7] {
    width: 100%;
    height: 100%;
    fill: var(--meb-palette-yellow-bright, var(--meb-accent-secondary));
    stroke: var(--meb-text-default);
    stroke-width: 1;
    stroke-linejoin: round;
}

.layout-game-leaderboard-name[b-hatm8p9xm7] {
    min-width: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-bold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.layout-game-leaderboard-empty[b-hatm8p9xm7] {
    padding: var(--meb-space-stack-md);
    color: var(--meb-text-muted);
    text-align: center;
    font-style: italic;
}

/* Logs tab empty-state — same muted/centered/italic language as the
   leaderboard placeholder so an event-less game reads as intentional. */
.layout-game-log-empty[b-hatm8p9xm7] {
    padding: var(--meb-space-stack-md);
    color: var(--meb-text-muted);
    text-align: center;
    font-style: italic;
}

/* ── Paused indicator (Step 8A) ─────────────────────────────────────── */

/* Read-only "Game paused by the host" banner. Flipped on by the server
   pushing Game.IsPaused = true via send_game_paused (D19, pending). Mark-
   tinted surface so it reads as a system message in the player's color
   without being so loud it looks like an error. */
/* Step 8C — D21 paused board state. Replaces the entire .layout-board-card
   when Game.IsPaused (Figma 293:13133 / 293:13274). Card chrome is the
   same as the active board card so the visual rhythm is preserved;
   contents are role-tagged (Creator gets the Resume button, Player gets
   reassurance copy). Side panel stays accessible per D19. */
.layout-board-paused-state[b-hatm8p9xm7] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-stack-md);
    padding: var(--meb-space-stack-xl) var(--meb-space-inline-lg);
    text-align: center;
    min-height: 480px;
}

.layout-board-paused-icon[b-hatm8p9xm7] {
    font-size: 4rem;
    line-height: 1;
    color: var(--layout-game-mark-color, var(--meb-text-default));
    opacity: 0.85;
}

.layout-board-paused-title[b-hatm8p9xm7] {
    margin: 0;
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h2-desktop);
    font-weight: var(--meb-font-weight-bold);
    color: var(--meb-text-default);
}

.layout-board-paused-body[b-hatm8p9xm7] {
    margin: 0;
    max-width: 36ch;
    font-size: var(--meb-font-size-body-md-desktop);
    color: var(--meb-text-default);
    line-height: var(--meb-font-lineheight-body);
}

.layout-board-paused-actions[b-hatm8p9xm7] {
    margin-top: var(--meb-space-stack-sm);
    display: flex;
    flex-direction: row;
    gap: var(--meb-space-inline-md);
}

.layout-game-paused[b-hatm8p9xm7] {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-sm);
    width: 100%;
    margin-bottom: var(--meb-space-stack-md);
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-md);
    background: color-mix(in srgb, var(--layout-game-mark-color, var(--meb-accent-info)) 22%, var(--meb-surface-card));
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-semibold);
}

.layout-game-paused-icon[b-hatm8p9xm7] {
    flex: 0 0 auto;
    font-size: 1.25rem;
    line-height: 1;
}

.layout-game-paused-text[b-hatm8p9xm7] {
    min-width: 0;
}

/* ── Empty state (Customize button) ────────────────────────────────── */

.layout-game-empty[b-hatm8p9xm7] {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--meb-space-stack-md) 0;
}

/* ── Legacy class shims (kept for modals + loading overlay) ────────── */

/* Loading overlay shown while _gamePlayService.Game is null. */
.meb-loading-overlay[b-hatm8p9xm7] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 60vh;
    padding: var(--meb-space-stack-xl);
    background: transparent;
}

.meb-loading-text[b-hatm8p9xm7] {
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h3-desktop);
    font-weight: var(--meb-font-weight-semibold);
}

/* Session superseded modal — content/message/actions */
.session-superseded-content[b-hatm8p9xm7] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--meb-space-stack-md);
    padding: var(--meb-space-stack-md);
    text-align: center;
}

.superseded-message[b-hatm8p9xm7] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.superseded-actions[b-hatm8p9xm7] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-md);
}

/* #8016: thin inline notice when RefreshGameState fails silently.
   User has working state — banner is informational, not blocking.
   Cleared automatically on next successful refresh. */
.refresh-error-notice[b-hatm8p9xm7] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-sm);
    margin: 0 0 var(--meb-space-stack-sm) 0;
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-md);
    background: var(--meb-accent-warning);
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-medium);
    text-align: center;
}

/* PR3 Inc1: cold-load chat cap -- thin muted notice that older chat history isn't shown
   (the cold load returns only the most-recent messages). Informational, not blocking. */
.layout-game-chat-history-notice[b-hatm8p9xm7] {
    margin: 0 0 var(--meb-space-stack-sm) 0;
    padding-block: var(--meb-space-stack-sm);
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
    text-align: center;
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/MobileBingoGameplay.razor.rz.scp.css */
/* ===================================================================
   MobileBingoGameplay.razor.css
   Scoped layout for the mobile bingo gameplay surface.
   Ported from LayoutCatalog 1302 (port + duplicate per user decision).
   Follows Bingo-Template §23.3: primitives own layout; cosmetics live
   here. Where sizing constraints absolutely require page CSS on a
   primitive's class (chat list height chain, board grid, scrollbar
   gutters), the deviation is comment-justified — same pattern as the
   catalog's own 1302 rules.

   Replaces the previous file which was a giant commented-out merge
   conflict block (all rules were inside /* ... */ comments).
   =================================================================== */

/* ── Page root ────────────────────────────────────────────────────── */

.mobile-root[b-6ya3axky8z] {
    /* Claim near-viewport height so the BottomPill anchors at the bottom
       even when a tab panel is sparse. 100dvh (dynamic viewport height)
       handles mobile browser UI; subtracting an approx 80px navbar gives
       the gameplay area its own surface. SiteFooter still lives below
       in MainLayout but is below the fold by design — user can scroll
       past the gameplay to reach it.
       .mobile-root is a plain <main> element, not a primitive, so
       display:flex/flex-direction here is fine per §23.3. */
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 100%;
    min-height: calc(100dvh - 80px);
    padding: var(--meb-space-stack-md) var(--meb-space-inline-md);
    box-sizing: border-box;
    /* Mark-colored gameplay surface. User explicitly asked: "The
       background should match the user's selected color." Use direct
       mark color rather than color-mix so the bg unambiguously reads
       as the user's palette.
       !important defends against any cascade rule that might
       otherwise override (e.g., the global `html, body` cream bg in
       app.css, or scoped-CSS specificity quirks the M6 iteration
       couldn't pin down without DevTools). After repeated UI checks
       showed the stage staying cream, this is the surest path to
       making the bg honor the inline --bingo-mark-color variable. */
    background-color: var(--bingo-mark-color, var(--meb-surface-section-creator)) !important;
    /* Bridge: BingoGameplay.razor sets --bingo-mark-color on
       .main-gameplay-containers; the catalog-derived rules in this file
       reference --layout-game-mark-color (1302's contract). Alias here so
       the cascade keeps resolving. */
    --layout-game-mark-color: var(--bingo-mark-color, var(--meb-surface-section-creator));
}

/* ── PR2 (Inc2d): "step into the room" JOIN reveal (mobile) ─────────────
   Mobile's OWN choreography (not desktop shrunk): the room fades in (opacity only -- a 2-4%
   scale is imperceptible at 390px, and the mobile stage is 100% mark-color so the color would
   "punch in" under a scale). The held calm card cross-fading OUT on top is what masks the
   instant M8-M9 body-color flip. transform/opacity only, inside a no-preference guard;
   data-meb-reveal is Blazor-owned and JOIN-scoped. Keyframes are global (animation.css). */
@media (prefers-reduced-motion: no-preference) {
    .mobile-root[data-meb-reveal="entering"][b-6ya3axky8z] {
        animation: mebUiAnimFadeIn 300ms ease-out both;
    }

    .layout-game-reveal-overlay[b-6ya3axky8z] {
        animation: mebUiAnimFadeOut 300ms ease-out both;
    }
}

/* Held calm card overlaying the entering stage (above the stage, below modals; never blocks taps). */
.layout-game-reveal-overlay[b-6ya3axky8z] {
    position: fixed;
    inset: 0;
    z-index: 50;
    pointer-events: none;
    /* Flex-center the held card to the SAME position as the pre-join WelcomeShell -- otherwise the fixed
       overlay top-aligns the card and it snaps up at the start of the cross-fade (hostile-review Critical). */
    display: flex;
    align-items: center;
    justify-content: center;
    /* Hidden at rest so REDUCED-MOTION users (no fade-out animation) never see a stuck opaque card over
       the joined board for the disarm window. The no-preference fade-out below animates 1->0. */
    opacity: 0;
}

/* Tabs BottomPill body / panel height propagation. The shared CSS at
   wwwroot/css/components/tabs.css:419 makes .variant-bottompill itself
   flex:1 1 auto, but ui-tabs-body and ui-tabpanel inside don't get the
   flex column treatment (only variant-folder does — see
   DesktopBingoGameplay.razor.css:637-655 for the desktop equivalent).
   Without these rules the chat list / call list scroll / log list
   collapse to 0 height. ::deep crosses into the Tabs primitive's
   internals — accepted per the existing desktop precedent. */
[b-6ya3axky8z] .layout-mobile-tabs.variant-bottompill .ui-tabs-body {
    width: 100%;
    box-sizing: border-box;
    background: transparent;
}

[b-6ya3axky8z] .layout-mobile-tabs.variant-bottompill .ui-tabs-body[hidden] {
    display: none !important;
}

[b-6ya3axky8z] .layout-mobile-tabs.variant-bottompill .ui-tabs-body:not([hidden]) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
}

[b-6ya3axky8z] .layout-mobile-tabs.variant-bottompill .ui-tabpanel {
    display: flex;
    flex-direction: column;
    width: 100%;
    flex: 1 1 auto;
    min-height: 0;
    overflow: hidden;
    background: transparent;
    box-sizing: border-box;
}

/* Force a minimum visible height on the active tab panel even when the
   flex chain would otherwise let it collapse to content height. The
   previous M2 attempt at flex propagation worked for the Game tab
   (whose content has its own intrinsic height via the 5x5 board) but
   the chat / call list / logs / options panels were still collapsing
   because their content is Fill-flex-based and depends on the parent
   having a definite height. 50vh is a pragmatic floor that's larger
   than any plausible empty-content gap but small enough that the
   BottomPill stays anchored near the bottom on most viewports. */
[b-6ya3axky8z] .layout-mobile-tabs.variant-bottompill .ui-tabs-body:not([hidden]),
[b-6ya3axky8z] .layout-mobile-tabs.variant-bottompill .ui-tabpanel:not([hidden]) {
    min-height: 50vh;
}

/* M4 — White content card wrapper around each TabPanel's content.
   Catalog 1302 pattern (.layout-mobile-figma-card at LayoutCatalog
   line 1404). This is what creates the "green game stage" signature:
   the mark-tinted .mobile-root bg only shows around the white cards,
   creating contrast that makes the tint look saturated.
   Plain <div> wrapper, not a primitive — page CSS fully owns it
   per §23.3. */
.layout-mobile-tab-card[b-6ya3axky8z] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    padding: var(--meb-space-stack-md) var(--meb-space-inline-sm);
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
    border-radius: var(--meb-border-radius-xl);
    background: var(--meb-surface-card);
    overflow: hidden;
    box-sizing: border-box;
}

/* ── Header row ───────────────────────────────────────────────────── */

/* The game icon (.layout-mobile-room-avatar until #10783) is now the shared GameIcon, which owns its
   own box and sizes the inline catalog SVG. */

/* #9554: the identity block is a flex item, so its default min-width:auto pins it to the title's
   MIN-CONTENT width. An unbroken game name has no break opportunity, so min-content is the whole
   string -- the nowrap header row then grew ~256px past .mobile-root and the browser zoomed the
   entire page out to fit (measured innerWidth 682 on a 390px device: board, chat and rail all
   shrank, not just the header). min-width:0 restores the shrink floor; the title below owns HOW it
   degrades. Row flow, gap and alignment stay owned by <Inline> -- this sets only the floor.
   Same shape as .layout-mobile-leaderboard-detail further down this file, and the #9494 game-row
   pattern (.gl-mtext / .gl-row-text). */
.layout-mobile-room[b-6ya3axky8z] {
    min-width: 0;
}

.layout-mobile-game-title[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h3-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);

    /* #9554: wrap to at most three lines rather than dictating the row's width.
       overflow-wrap:anywhere is the load-bearing half -- it creates break opportunities INSIDE an
       unbroken token, which is what actually collapses min-content. min-width:0 alone would still
       leave a ~566px-wide single word. Three lines to match #9494's game-row clamp, so a game name
       degrades the same way wherever it is shown. Wrapping beats ellipsising per the same #9494
       ruling -- a truncated game name is worse than a taller row -- and the clamp is a ceiling, not
       a target: short names still render on one line and the header keeps its 64px min-height. */
    min-width: 0;
    max-width: 100%;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}

/* Share button cosmetics — sized via ActionButton's own contract. */
[b-6ya3axky8z] .layout-mobile-share-button {
    min-width: var(--meb-size-touch-target);
    min-height: var(--meb-size-touch-target);
}

/* ── Player rail ──────────────────────────────────────────────────── */

.layout-mobile-player-rail[b-6ya3axky8z] {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    /* Reserve vertical space even when Game.Players is empty so the
       layout doesn't jolt when the first player joins. 40px matches the
       avatar size set by the AvatarGroup ::deep rules below. */
    min-height: 40px;
}

.layout-mobile-player-rail[b-6ya3axky8z]::-webkit-scrollbar {
    display: none;
}

[b-6ya3axky8z] .layout-mobile-player-group {
    width: max-content;
    min-width: max-content;
}

[b-6ya3axky8z] .layout-mobile-player-group .layout-game-player-avatar,
[b-6ya3axky8z] .layout-mobile-player-group .ui-avatar {
    width: 40px;
    height: 40px;
    min-width: 40px;
    min-height: 40px;
    border: var(--meb-border-width-sm) solid color-mix(in srgb, var(--meb-border-default) 40%, transparent);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-bold);
}

/* ── Unread chat badge ────────────────────────────────────────────── */

.layout-game-tab-badge[b-6ya3axky8z] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    margin-inline-start: var(--meb-space-inline-xs);
    padding: 0 5px;
    border-radius: var(--meb-border-radius-full);
    background: var(--meb-accent-danger, var(--meb-surface-section-creator));
    color: var(--meb-text-inverse);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-bold);
}

/* #11111 (S5-T4): the poll Sheet header chip ("Not anonymous", "Answer sent", "Closed"). */
.layout-mobile-poll-sheet-chip[b-6ya3axky8z] {
    margin-inline-start: var(--meb-space-inline-sm);
    padding: var(--meb-space-stack-xxs) var(--meb-space-inline-sm);
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
    border-radius: var(--meb-border-radius-full);
    color: var(--meb-text-default);
    font-size: var(--text-xs);
    font-weight: var(--meb-font-weight-bold);
    white-space: nowrap;
}

/* ── Chat panel ───────────────────────────────────────────────────── */

/* Sizing on a Stack class — comment-justified per Bingo-Template §23.3
   "where sizing absolutely needs page CSS." Without it, the chat list
   collapses to content height and the input shell pushes off-screen.
   Catalog precedent at layout-1302 lines 1431-1440. */
[b-6ya3axky8z] .layout-mobile-chat-panel {
    width: 100%;
    min-height: 0;
    overflow: hidden;
}

[b-6ya3axky8z] .layout-mobile-chat-list {
    padding-right: var(--meb-space-inline-xs);
}

/* Chat bubble row sizing for the 1302 mobile variant. Crosses into
   ChatBubble's wrapper via ::deep. */
.mobile-root[b-6ya3axky8z]  .layout-mobile-chat-list .ui-chat-bubble-row {
    margin-bottom: var(--meb-space-stack-sm);
}

/* ADO #10563 triad round 5 (#2): round 4 moved the width cap from
   .ui-chat-bubble-surface onto .ui-chat-bubble-stack in the shared
   chat-bubble.css (mobile: max-width 86%), but this page-local override was
   never migrated -- it kept max-width:84% on the SURFACE, which now
   resolves against the ALREADY-86%-capped stack and compounds to
   roughly 72% of the row, visibly narrowing the mobile chat bubble.
   No design record ties 84% to this page specifically (Layout 1302's
   mobile variant) as distinct from the shared 86% used everywhere
   else; treating the divergence as stale drift from the pre-round-4
   surface-based cap rather than a deliberate choice, so this page no
   longer overrides the width cap at all -- it inherits the single
   shared 86% cap from .ui-chat-bubble-stack in chat-bubble.css. Only
   the visual (non-layout) overrides stay here.
   ADO #10563 triad round 8 (#5): corrected a stale claim in the paragraph
   above -- this page does NOT render under a max-width:640px query. This
   page IS the mobile render structure (ScreenSizeService.MobileBreakpoint /
   CT-01 selects it below 768px), and the shared 86% cap this page inherits
   is now keyed off .mobile-root (this page's own <main> root class), not a
   viewport media query -- see chat-bubble.css's "Mobile" section. */
.mobile-root[b-6ya3axky8z]  .layout-mobile-chat-list .ui-chat-bubble-surface {
    font-size: var(--meb-font-size-body-sm-desktop);
    border: none;
}

.mobile-root[b-6ya3axky8z]  .layout-mobile-chat-list .ui-chat-bubble-surface--other {
    border-radius: var(--meb-border-radius-lg) var(--meb-border-radius-lg) var(--meb-border-radius-lg) 0;
}

.mobile-root[b-6ya3axky8z]  .layout-mobile-chat-list .ui-chat-bubble-surface--current {
    border-radius: var(--meb-border-radius-lg) var(--meb-border-radius-lg) 0 var(--meb-border-radius-lg);
}

.mobile-root[b-6ya3axky8z]  .layout-mobile-chat-list .ui-chat-bubble-surface--other::before,
.mobile-root[b-6ya3axky8z]  .layout-mobile-chat-list .ui-chat-bubble-surface--current::after {
    bottom: 0;
}

/* Chat input shell — Input primitive + ghost MebButton overlay. Same
   pattern as desktop Step 2AA. */
.layout-mobile-chat-input-shell[b-6ya3axky8z] {
    position: relative;
    flex: 0 0 auto;
    width: 100%;
    padding: var(--meb-space-stack-sm) 0 0 0;
    --layout-mobile-chat-input-height: calc(var(--meb-size-touch-target) + var(--meb-space-stack-sm));
}

.layout-mobile-chat-input-shell[b-6ya3axky8z]  .ui-input-wrapper {
    width: 100%;
}

.layout-mobile-chat-input-shell[b-6ya3axky8z]  .ui-input {
    min-height: var(--layout-mobile-chat-input-height);
    padding-right: calc(var(--meb-size-touch-target) + var(--meb-space-inline-sm));
    border-radius: var(--meb-border-radius-full);
    border-color: var(--meb-border-default);
    box-shadow: var(--meb-shadow-button);
}

[b-6ya3axky8z] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost {
    position: absolute;
    top: calc((var(--layout-mobile-chat-input-height) / 2) + var(--meb-space-stack-sm));
    right: var(--meb-space-inline-sm);
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--meb-size-touch-target);
    min-width: var(--meb-size-touch-target);
    height: var(--meb-size-touch-target);
    min-height: var(--meb-size-touch-target);
    padding: 0;
    border: 0;
    border-radius: var(--meb-border-radius-full);
    background: transparent;
    box-shadow: none;
    color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    line-height: var(--meb-font-lineheight-tight);
    transform: translateY(-50%);
}

[b-6ya3axky8z] button.layout-game-chat-send-button.meb-btn.meb-btn--ghost .layout-game-chat-send-svg {
    display: block;
    width: 19px;
    height: 16px;
}

/* ── Call list (chips) ────────────────────────────────────────────── */

[b-6ya3axky8z] .layout-mobile-calllist-panel {
    width: 100%;
}

[b-6ya3axky8z] .layout-mobile-calllist-scroll {
    padding-right: var(--meb-space-inline-xs);
}

[b-6ya3axky8z] .layout-mobile-calllist-chips {
    align-content: flex-start;
    justify-content: flex-start;
}

[b-6ya3axky8z] .layout-mobile-calllist-chips .layout-mobile-chip {
    width: fit-content;
    min-height: 35px;
    padding: var(--meb-space-stack-xs) var(--meb-space-inline-sm);
    border: var(--meb-border-width-sm) solid var(--meb-border-default) !important;
    border-radius: var(--meb-border-radius-full) !important;
    box-shadow: var(--meb-shadow-button) !important;
    font-size: var(--meb-font-size-body-sm-desktop);
    white-space: nowrap;
}

[b-6ya3axky8z] .layout-mobile-calllist-chips .layout-game-chip--selected {
    background: var(--layout-game-mark-color) !important;
    color: var(--meb-text-inverse) !important;
}

/* ── Board (game tab) ─────────────────────────────────────────────── */

[b-6ya3axky8z] .layout-mobile-board-surface {
    width: 100%;
    min-height: 0;
    overflow: hidden;
}

.layout-mobile-board-heading[b-6ya3axky8z] {
    width: 100%;
    height: 64px;
    flex: 0 0 auto;
    --progressbar-color: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    /* Light neutral track so the unfilled BINGO letters sit on a visible
       rail (catalog 1302's heading shows a neutral pill track behind
       I/N/G/O when only B is filled). Without the explicit track the
       ProgressBar fall-through reveals the page background and the
       letters appear to float. */
    --progressbar-track: var(--meb-surface-input-filled);
    --progressbar-radius: var(--meb-border-radius-full);
}

.layout-mobile-board-letters[b-6ya3axky8z] {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: 1fr;
    align-items: center;
    align-content: center;
    width: 100%;
    height: 100%;
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h2-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}

.layout-mobile-board-letters > span[b-6ya3axky8z] {
    display: block;
    width: 100%;
    text-align: center;
    color: var(--meb-text-default);
}

.layout-mobile-board-letters .current-bingo-progress[b-6ya3axky8z] {
    color: var(--meb-text-inverse);
    text-shadow: 0 1px 2px color-mix(in srgb, var(--meb-text-default) 25%, transparent);
}

/* Mark-scope is a plain div hosting the grid; flex column to allow
   the grid to claim remaining height. */
.layout-board-mark-scope[b-6ya3axky8z] {
    width: 100%;
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
}

/* Mobile board grid — aspect-ratio on the wrapper (Step 2U pattern).
   max-width=420 keeps the grid sane on tablets. */
.layout-mobile-board-grid[b-6ya3axky8z] {
    display: grid;
    grid-template-columns: repeat(5, minmax(0, 1fr));
    grid-template-rows: repeat(5, minmax(0, 1fr));
    width: 100%;
    max-width: 420px;
    aspect-ratio: 1 / 1;
    max-height: 100%;
    min-height: 0;
    margin-inline: auto;
    overflow: hidden;
    border-radius: var(--meb-border-radius-lg);
    background: var(--meb-surface-card);

    /* Interior gridlines: a single quiet neutral divider. Replaced the former
       8-hue Figma rainbow (node 293:10201). The saturated multi-hue lines were
       luminance-inconsistent (yellow ~1.5:1 nearly invisible, navy ~13.7:1 a
       hard near-black rule), carried no information, and inverted the board's
       visual hierarchy -- the dividers shouted over the call text players need
       to read. A uniform low-contrast divider lets the board read as one calm
       table with the words as the focal point. Kept as the 8 --board-line-*
       slots so the nth-child border machinery below is unchanged. Use
       --meb-border-subtle (a true low-chroma neutral), NOT --meb-border-default
       (pure black). */
    --board-line: var(--meb-border-subtle);
    --board-line-v1: var(--board-line);
    --board-line-v2: var(--board-line);
    --board-line-v3: var(--board-line);
    --board-line-v4: var(--board-line);
    --board-line-h1: var(--board-line);
    --board-line-h2: var(--board-line);
    --board-line-h3: var(--board-line);
    --board-line-h4: var(--board-line);
}

/* Cell rules — port of the desktop layout-board-cell contract,
   tightened for mobile (smaller font, less padding). */
.layout-mobile-board-grid .layout-board-cell[b-6ya3axky8z] {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    min-height: 0;
    padding: var(--meb-space-stack-xs) var(--meb-space-inline-xs);
    /* Base horizontal divider — colored per row below (fallback). */
    border-bottom: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-h1);
    text-align: center;
    font-size: 11px;
    line-height: var(--meb-font-lineheight-tight);
    background: var(--meb-surface-card);
    position: relative;
    overflow: hidden;
    isolation: isolate;
    cursor: pointer;
    box-sizing: border-box;
    /* Query container so cell text auto-sizes to the cell (cqi below). */
    container-type: inline-size;
}

/* Exact Figma grid lines at 1px — verticals (cols 1-4) + horizontals
   (rows 1-4); last column/row carry no interior divider. */
.layout-mobile-board-grid .layout-board-cell:nth-child(5n + 1)[b-6ya3axky8z] {
    border-right: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-v1);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(5n + 2)[b-6ya3axky8z] {
    border-right: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-v2);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(5n + 3)[b-6ya3axky8z] {
    border-right: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-v3);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(5n + 4)[b-6ya3axky8z] {
    border-right: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-v4);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(-n + 5)[b-6ya3axky8z] {
    border-bottom: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-h1);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(n + 6):nth-child(-n + 10)[b-6ya3axky8z] {
    border-bottom: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-h2);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(n + 11):nth-child(-n + 15)[b-6ya3axky8z] {
    border-bottom: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-h3);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(n + 16):nth-child(-n + 20)[b-6ya3axky8z] {
    border-bottom: calc(var(--meb-border-width-sm) * 1.5) solid var(--board-line-h4);
}

.layout-mobile-board-grid .layout-board-cell:nth-child(n + 21)[b-6ya3axky8z] {
    border-bottom: none;
}

.layout-mobile-board-grid .layout-board-cell-text[b-6ya3axky8z] {
    position: relative;
    z-index: 5;
    /* Full content width as the line box so the most common long single words
       (Helicopter, Motorcycle, ~10 chars) land on one line in the 65px cell.
       Words denser than the cell still wrap; hyphenation would clean that up
       but isn't reliable across engines, so break-word remains the fallback. */
    width: 100%;
    /* Text fitting — mirror of desktop, tuned for the smaller mobile cell:
       hyphens kills mid-word orphan breaks; overflow-wrap:break-word is the
       last-resort break for unhyphenatable tokens so they wrap instead of
       clipping; cqi clamp auto-fits (11px floor / 13px cap); plain 11px is the
       fail-safe fallback. */
    overflow-wrap: break-word;
    hyphens: auto;
    font-size: 11px;
    font-size: clamp(11px, 15cqi, 13px);
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-semibold);
    /* No text-wrap:balance — see DesktopBingoGameplay.razor.css: it over-breaks
       words that would fit when soft hyphens are present. */
}

.layout-mobile-board-grid .layout-board-cell--taken[b-6ya3axky8z] {
    background: var(--meb-surface-card);
}

.layout-mobile-board-grid .layout-board-cell--free[b-6ya3axky8z] {
    font-family: var(--meb-font-family-primary);
    background: var(--meb-surface-card) !important;
    cursor: default;
}

.layout-mobile-board-grid .layout-board-cell--free .layout-board-cell-text[b-6ya3axky8z] {
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-tight);
}

.layout-mobile-board-grid .layout-board-cell--bingo-line[b-6ya3axky8z] {
    filter: contrast(0.4);
}

.layout-mobile-board-grid .layout-board-cell--animate[b-6ya3axky8z] {
    animation: layout-mobile-board-cell-pop-b-6ya3axky8z 0.6s ease-in-out;
}

@keyframes layout-mobile-board-cell-pop-b-6ya3axky8z {
    0%   { transform: scale(1) rotate(0deg); }
    30%  { transform: scale(1.15) rotate(4deg); }
    60%  { transform: scale(1.08) rotate(-3deg); }
    100% { transform: scale(1) rotate(0deg); }
}

/* Splat overlay — same two-layer pattern as desktop. */
.layout-mobile-board-grid .layout-board-splat[b-6ya3axky8z],
.layout-mobile-board-grid .layout-board-splat-color[b-6ya3axky8z] {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    transform: rotate(var(--mark-rotate, 0deg));
    z-index: 2;
}

.layout-mobile-board-grid .layout-board-splat[b-6ya3axky8z] {
    object-fit: cover;
    display: block;
    opacity: 0.12;
}

.layout-mobile-board-grid .layout-board-splat-color[b-6ya3axky8z] {
    z-index: 3;
    background: var(--layout-game-mark-color, var(--meb-surface-section-creator));
    -webkit-mask-image: url('/_content/LiveEventBingo.Shared/css/assets/images/logos/G4.png');
    -webkit-mask-size: cover;
    -webkit-mask-position: center;
    -webkit-mask-repeat: no-repeat;
    mask-image: url('/_content/LiveEventBingo.Shared/css/assets/images/logos/G4.png');
    mask-size: cover;
    mask-position: center;
    mask-repeat: no-repeat;
    mask-mode: alpha;
    mix-blend-mode: multiply;
    opacity: 0.42;
}

/* ── Logs tab ─────────────────────────────────────────────────────── */

[b-6ya3axky8z] .layout-mobile-log-panel {
    color: var(--meb-text-default);
}

[b-6ya3axky8z] .layout-mobile-logs-list {
    padding-right: var(--meb-space-inline-xs);
    box-sizing: border-box;
}

.layout-game-log-note[b-6ya3axky8z] {
    width: 100%;
    margin: 0 0 var(--meb-space-stack-sm) 0;
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.layout-mobile-log-row[b-6ya3axky8z] {
    display: grid;
    grid-template-columns: auto minmax(4.5rem, auto) minmax(0, 1fr);
    align-items: center;
    gap: var(--meb-space-inline-xs);
    width: 100%;
    min-width: 0;
    padding: var(--meb-space-stack-xs) 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
    box-sizing: border-box;
}

.layout-mobile-log-marker[b-6ya3axky8z] {
    align-self: center;
    width: var(--meb-space-inline-sm);
    height: var(--meb-space-inline-sm);
    border-radius: var(--meb-border-radius-full);
    background: var(--layout-game-log-event-color, var(--meb-accent-secondary));
}

.layout-mobile-log-time[b-6ya3axky8z] {
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-semibold);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.layout-mobile-log-message[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-text-default);
    overflow-wrap: anywhere;
}

/* ── Options tab ──────────────────────────────────────────────────── */

[b-6ya3axky8z] .layout-mobile-options-panel {
    padding-right: var(--meb-space-inline-xs);
    box-sizing: border-box;
}

.layout-mobile-options-heading[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-text-heading-brand, var(--meb-text-default));
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-tight);
}

.layout-mobile-options-value[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-regular);
    line-height: var(--meb-font-lineheight-body);
}

/* #9430 identity block — the avatar catalog's loading note and the inline save error.
   Typography/colour only; the identity row and the picker own their own internals.
   Unlike desktop, mobile styles its values via an explicit .layout-mobile-options-value
   class rather than a bare `> p` selector, so these need no parent qualification. */
.layout-mobile-identity-caption[b-6ya3axky8z] {
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.layout-mobile-identity-error[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-accent-danger);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.layout-mobile-awake-sub[b-6ya3axky8z] {
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
}

/* #11134 (S6-T3, board OwnerOptions): the Surveys switch hint and its notice line under Game Controls.
   Text only; the row's layout comes from the Stack props in the markup. */
.layout-mobile-options-surveys-hint[b-6ya3axky8z] {
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
}

.layout-mobile-options-surveys-notice[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-sm-desktop);
}

.layout-mobile-options-version[b-6ya3axky8z] {
    margin: var(--meb-space-stack-md) 0 0 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    text-align: center;
}

/* ── Leaderboard popup (Step 5 mobile) ────────────────────────────── */

.layout-mobile-leaderboard-list[b-6ya3axky8z] {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    width: 100%;
    min-width: 240px;
}

/* Row track: rank | avatar | (name + BINGO pill stacked). Rows touch for zebra. */
.layout-mobile-leaderboard-row[b-6ya3axky8z] {
    display: grid;
    grid-template-columns: auto auto minmax(0, 1fr);
    align-items: center;
    gap: var(--meb-space-inline-md);
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
}

.layout-mobile-leaderboard-row:nth-child(odd)[b-6ya3axky8z] {
    background: color-mix(in srgb, var(--meb-surface-section-creator) 8%, var(--meb-surface-card));
}

/* Doubled class so the "your row" tint beats the :nth-child(odd) zebra rule
   (equal specificity, declared later) -- otherwise an odd-positioned me-row
   renders identical to a zebra stripe and the highlight disappears. */
.layout-mobile-leaderboard-row.layout-mobile-leaderboard-row--me[b-6ya3axky8z] {
    background: color-mix(in srgb, var(--bingo-mark-color, var(--meb-surface-section-creator)) 16%, var(--meb-surface-card));
}

.layout-mobile-leaderboard-rank[b-6ya3axky8z] {
    font-size: var(--meb-font-size-h3-desktop, 1.25rem);
    font-weight: var(--meb-font-weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--meb-text-default);
    min-width: 2ch;
}

/* APPROVED EXCEPTION (§4.1 atomic rendering): avatar + crown-badge composite. */
.layout-mobile-leaderboard-avatar-slot[b-6ya3axky8z] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}

.layout-mobile-leaderboard-crown[b-6ya3axky8z] {
    position: absolute;
    top: calc(-1 * var(--meb-space-stack-sm));
    left: 50%;
    transform: translateX(-50%) rotate(-12deg);
    width: 1.1rem;
    height: 1.1rem;
    pointer-events: none;
}

.layout-mobile-leaderboard-crown svg[b-6ya3axky8z] {
    width: 100%;
    height: 100%;
    fill: var(--meb-palette-yellow-bright, var(--meb-accent-secondary));
    stroke: var(--meb-text-default);
    stroke-width: 1;
    stroke-linejoin: round;
}

/* Name + pill column flow owned by <Stack>; only the min-width here. */
.layout-mobile-leaderboard-detail[b-6ya3axky8z] {
    min-width: 0;
}

.layout-mobile-leaderboard-name[b-6ya3axky8z] {
    min-width: 0;
    max-width: 100%;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-bold);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.layout-mobile-leaderboard-empty[b-6ya3axky8z] {
    padding: var(--meb-space-stack-md);
    color: var(--meb-text-muted);
    text-align: center;
    font-style: italic;
}

/* Logs tab empty-state — same muted/centered/italic language as the
   leaderboard placeholder so an event-less game reads as intentional. */
.layout-mobile-log-empty[b-6ya3axky8z] {
    padding: var(--meb-space-stack-md);
    color: var(--meb-text-muted);
    text-align: center;
    font-style: italic;
}

/* ── Pre-start welcome shell ─────────────────────────────────────────── */
/* PR1 (Inc2d): the pre-start shell (.layout-mobile-prestart) moved to
   WelcomeShell.razor(.css) -- now the SOLE renderer of that markup (the calm
   PublicVisitor branch and the in-stage Creator/Player welcome both use
   <WelcomeShell>). The card BODY had already moved to the shared WelcomeCardBody
   (its --mobile variant carries the card chrome). Removed here to avoid a dead,
   drift-prone duplicate. */

/* Step 8C — D21 paused board state. Replaces the entire Game-tab Stack
   when Game.IsPaused (Figma 341:4249). Card chrome is the same
   .layout-mobile-tab-card wrapper outside; the inner Stack swaps content.
   Other tabs (Chat / Call list / Logs / Options) remain functional. */
.layout-mobile-board-paused-state[b-6ya3axky8z] {
    padding: var(--meb-space-stack-xl) var(--meb-space-inline-md);
    text-align: center;
    justify-content: center;
    min-height: 50vh;
}

.layout-mobile-board-paused-icon[b-6ya3axky8z] {
    font-size: 3.5rem;
    line-height: 1;
    color: var(--layout-game-mark-color, var(--meb-text-default));
    opacity: 0.85;
}

.layout-mobile-board-paused-title[b-6ya3axky8z] {
    margin: 0;
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h2-mobile);
    font-weight: var(--meb-font-weight-bold);
    color: var(--meb-text-default);
}

.layout-mobile-board-paused-body[b-6ya3axky8z] {
    margin: 0;
    max-width: 32ch;
    font-size: var(--meb-font-size-body-md-mobile);
    color: var(--meb-text-default);
    line-height: var(--meb-font-lineheight-body);
}

/* ── Pause indicator (Mobile M1 mirror of Step 8A) ────────────────── */

.layout-game-paused[b-6ya3axky8z] {
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-sm);
    width: 100%;
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-md);
    background: color-mix(in srgb, var(--layout-game-mark-color, var(--meb-accent-info)) 22%, var(--meb-surface-card));
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-semibold);
}

.layout-game-paused-icon[b-6ya3axky8z] {
    flex: 0 0 auto;
    font-size: 1.1rem;
    line-height: 1;
}

.layout-game-paused-text[b-6ya3axky8z] {
    min-width: 0;
}

/* ── Loading overlay (when _gamePlayService.Game is null) ─────────── */

.meb-loading-overlay[b-6ya3axky8z] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: 60vh;
    padding: var(--meb-space-stack-xl);
}

.meb-loading-text[b-6ya3axky8z] {
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-h3-desktop);
    font-weight: var(--meb-font-weight-semibold);
}

/* ── Session superseded modal ─────────────────────────────────────── */

.session-superseded-content[b-6ya3axky8z] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--meb-space-stack-md);
    padding: var(--meb-space-stack-md);
    text-align: center;
}

.superseded-message[b-6ya3axky8z] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    line-height: var(--meb-font-lineheight-body);
}

.superseded-actions[b-6ya3axky8z] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-md);
}

/* #8016: thin inline notice when RefreshGameState fails silently.
   User has working state — banner is informational, not blocking.
   Cleared automatically on next successful refresh. */
.refresh-error-notice[b-6ya3axky8z] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-inline-sm);
    margin: 0 0 var(--meb-space-stack-sm) 0;
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-md);
    background: var(--meb-accent-warning);
    color: var(--meb-text-default);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-medium);
    text-align: center;
}

/* PR3 Inc1: cold-load chat cap -- thin muted notice that older chat history isn't shown
   (the cold load returns only the most-recent messages). Informational, not blocking. */
.layout-mobile-chat-history-notice[b-6ya3axky8z] {
    margin: 0 0 var(--meb-space-stack-sm) 0;
    padding-block: var(--meb-space-stack-sm);
    color: var(--meb-text-muted);
    font-size: var(--meb-font-size-body-sm-desktop);
    text-align: center;
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/PlayerIdentityRow.razor.rz.scp.css */
/* ── #9138 Options identity row — typography + colour ONLY ─────────────────
   ALL layout is owned by the Stack / Inline primitives in PlayerIdentityRow.razor.
   Nothing here touches display / flex / gap / overflow / align on a primitive's
   class. No raw hex, no raw shadow, no raw border literal.

   This row owns its own typography rather than borrowing either shell's, and that
   is deliberate: desktop's .layout-game-options-section styles its VALUES via a
   bare `> p` direct-child selector. Borrowing it would make the row's text depend
   on which shell mounted it -- and a scoped rule from either shell could never
   reach these elements anyway (a .razor.css b-xxxxx attribute never crosses into a
   child component). Shells own <section>/<h4>; the row owns everything inside it. */

.options-identity-name[b-2bmwdb4osm] {
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-tight);
    color: var(--meb-text-default);

    /* Sizing on our own leaf, not layout — same stance as .welcome-nametag-editor.
       A 50-char name must not push the CHANGE button off the row. */
    max-width: 18em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.options-identity-caption[b-2bmwdb4osm] {
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
    color: var(--meb-text-muted);
}

/* Unrenamed anon: amplify the affordance the way .welcome-nametag--default does,
   but in the settings register. The welcome card SHOUTS (accent on a sticker that
   pops and wiggles); Options WHISPERS — accent the caption only, never the row
   chrome. Same instinct, correct volume for a panel that also holds a destructive
   Finish Game button. */
.options-identity-caption--default[b-2bmwdb4osm] {
    color: var(--meb-accent-primary);
    font-weight: var(--meb-font-weight-semibold);
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/PlayGamePlaceholder.razor.rz.scp.css */
/* PR1 (Inc2d): the pre-join shell chrome moved to WelcomeShell.razor.css, which now
   owns .layout-game-prestart / -card / -avatar / .layout-mobile-prestart. This component
   renders <WelcomeShell> + <WelcomeCardBody> (each with its own scoped CSS) and the
   caller's @Fallback, so it has no scoped-styled elements of its own. */
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/WelcomeCardBody.razor.rz.scp.css */
/* ── Shared pre-start welcome / join card BODY — typography + chrome ───────
   LAYOUT (column, action row, gaps, alignment) is owned by the Stack / Inline
   primitives in WelcomeCardBody.razor, NOT this stylesheet (Bingo-Template
   §23.3). These rules carry ONLY type scale, color, and card chrome — no
   display / flex / gap / align. Migrated from the per-element rules that lived
   in DesktopBingoGameplay.razor.css (.layout-game-prestart-*) and
   MobileBingoGameplay.razor.css (.layout-mobile-prestart-*); the two hosts
   differed only by these per-element values, now keyed by variant here.

   The host keeps its own OUTER-wrapper CSS: the desktop section + card + avatar
   and the mobile section. Desktop carries the card chrome on its outer card;
   mobile has no separate card element, so the chrome lives on the --mobile
   variant below instead. */

.welcome-card[b-gfmc3j2lx0] {
    width: 100%;
    box-sizing: border-box;
    text-align: center;
}

/* Mobile: this body IS the card (no separate avatar/card element), so it carries
   the chrome the old .layout-mobile-prestart-body Stack carried. Desktop's chrome
   stays on the host's .layout-game-prestart-card. */
.welcome-card--mobile[b-gfmc3j2lx0] {
    max-width: 480px;
    padding: var(--meb-space-stack-xl) var(--meb-space-inline-md);
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
    border-radius: var(--meb-border-radius-xl);
    background: var(--meb-surface-card);
}

/* ── Title ─────────────────────────────────────────────────────────────── */
.welcome-card-title[b-gfmc3j2lx0] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}
.welcome-card--desktop .welcome-card-title[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-h2-desktop);
}
.welcome-card--mobile .welcome-card-title[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-h3-desktop);
}

/* ── Description ───────────────────────────────────────────────────────── */
.welcome-card-description[b-gfmc3j2lx0] {
    margin: 0;
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    line-height: var(--meb-font-lineheight-body);
}
.welcome-card--desktop .welcome-card-description[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-desktop);
}
.welcome-card--mobile .welcome-card-description[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-sm-desktop);
}

/* ── Start-time line ───────────────────────────────────────────────────── */
.welcome-card-starttime[b-gfmc3j2lx0] {
    margin: 0;
    color: var(--layout-game-mark-color, var(--meb-text-default));
    font-family: var(--meb-font-family-primary);
    line-height: var(--meb-font-lineheight-tight);
}
.welcome-card--desktop .welcome-card-starttime[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-h3-desktop, var(--meb-font-size-body-desktop));
}
.welcome-card--mobile .welcome-card-starttime[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-desktop);
}

/* ── Welcome heading (Figma 293-1457 / 293-1347) ───────────────────────── */
.welcome-card-welcome-title[b-gfmc3j2lx0] {
    margin: 0;
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
}
.welcome-card--desktop .welcome-card-welcome-title[b-gfmc3j2lx0] {
    margin-top: var(--meb-space-stack-sm);
    font-size: var(--meb-font-size-h3-desktop, var(--meb-font-size-body-desktop));
}
.welcome-card--mobile .welcome-card-welcome-title[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-h3-desktop);
}

/* ── Welcome message ───────────────────────────────────────────────────── */
.welcome-card-welcome-message[b-gfmc3j2lx0] {
    margin: 0;
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    line-height: var(--meb-font-lineheight-body);
}
.welcome-card--desktop .welcome-card-welcome-message[b-gfmc3j2lx0] {
    max-width: 46ch;
    font-size: var(--meb-font-size-body-desktop);
}
.welcome-card--mobile .welcome-card-welcome-message[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-sm-desktop);
}

/* ── Free-to-play disclaimer (#9038) ───────────────────────────────────────
   Small-print line echoing the post-join Options-tab statement into this SSR
   welcome card. Muted/caption treatment mirrors .welcome-card-description's
   token choices; no layout props (Stack above owns the vertical gap). */
.welcome-card-disclaimer[b-gfmc3j2lx0] {
    margin: 0;
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    line-height: var(--meb-font-lineheight-body);
    font-size: var(--meb-font-size-body-sm-desktop);
}
.welcome-card-disclaimer a[b-gfmc3j2lx0] {
    color: inherit;
    text-decoration: underline;
}

/* ── Sign in / Create an account (#10836) ──────────────────────────────────
   Type and color only; the row and the two no-wrap prompt units are Inline
   primitives in the markup. The prompts read muted like the description; the
   links use the default text color (not a brand link color) so contrast holds on
   every card theme, with weight + underline marking them as links. Vertical
   padding on the anchor gives each link a pointer target of at least the WCAG
   2.5.8 minimum height; no horizontal padding, so the Inline gap alone sets the
   space between the prompt and its link. */
.welcome-card-account-links[b-gfmc3j2lx0] {
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    line-height: var(--meb-font-lineheight-body);
    text-align: center;
}
.welcome-card--desktop .welcome-card-account-links[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-desktop);
}
.welcome-card--mobile .welcome-card-account-links[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-sm-mobile);
}
.welcome-card-account-link[b-gfmc3j2lx0] {
    display: inline-block;
    padding: var(--meb-space-stack-xs) 0;
    color: var(--meb-text-default);
    font-weight: var(--meb-font-weight-semibold);
    text-decoration: underline;
}

/* ── Actions ───────────────────────────────────────────────────────────────
   Flex/gap/direction/alignment are owned by the Inline (desktop) / Stack
   (mobile) primitive that wraps these buttons. The only things left here are a
   small top spacing nudge and the mobile width cap (sizing, not layout). */
.welcome-card-actions[b-gfmc3j2lx0] {
    margin-top: var(--meb-space-stack-sm);
}
.welcome-card--mobile .welcome-card-actions[b-gfmc3j2lx0] {
    width: 100%;
    max-width: 320px;
}

/* ── "You've joined" affirmation (chip chrome) ─────────────────────────────
   A leaf pill — inline-block (not flex) keeps this pure tokenized chrome; for a
   single text run there is no cross-axis to align, so no layout primitive is
   warranted. Centered text covers the wrap case on the narrow mobile column. */
.welcome-card-joined[b-gfmc3j2lx0] {
    display: inline-block;
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
    border-radius: var(--meb-border-radius-full);
    background: color-mix(in srgb, var(--layout-game-mark-color, var(--meb-surface-section-creator)) 18%, var(--meb-surface-card));
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);
    font-weight: var(--meb-font-weight-semibold);
    text-align: center;
}
.welcome-card--desktop .welcome-card-joined[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-desktop);
}
.welcome-card--mobile .welcome-card-joined[b-gfmc3j2lx0] {
    font-size: var(--meb-font-size-body-sm-desktop);
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/WelcomeNameTag.razor.rz.scp.css */
/* ── 9105 + 9430 identity badge — chrome + typography ──────────────────────
   PAGE layout (the badge/caption column, the editor's field/buttons column) is
   owned by the Stack / Inline primitives in WelcomeNameTag.razor. The rules
   here are the control's own chrome: the sticker's INTERNAL composition (strip
   over a disc|name row — a leaf control arranging its own parts, exactly as
   MebButton's internal inline-flex does), tokens-only color/border/shadow, the
   sticker tilt, and the one-shot entrance animation. No raw hex anywhere.

   9430: the sticker is now a CONTAINER, not a button — it holds two tap targets
   (the avatar disc button and the name button), and a button cannot nest
   buttons. The sticker visual (tilt / shadow / pop / hover-straighten) stays on
   the container; the focus ring and cursor move to the two inner buttons. */

.welcome-nametag[b-r13j5pxm0b] {
    /* leaf-control internal composition: header strip over body */
    display: inline-flex;
    flex-direction: column;
    align-items: stretch;
    padding: 0;
    overflow: hidden;

    min-width: 190px;
    max-width: 100%;

    background: var(--meb-surface-card);
    border: var(--meb-border-width-md) solid var(--meb-border-default);
    border-radius: var(--meb-border-radius-sm);
    box-shadow: var(--meb-shadow-surface-comic-sm);
    color: var(--meb-text-default);
    font-family: var(--meb-font-family-primary);

    /* sticker tilt — the "stuck-on name tag" read; straightens on hover so the
       control also reads as pressable */
    transform: rotate(-1.5deg);
    transition: transform var(--meb-motion-fast), box-shadow var(--meb-motion-fast);

    /* one-shot entrance: pop in, then a single wiggle to call the eye. Runs once
       per DOM insertion; suppressed after first interaction (--settled) and
       under prefers-reduced-motion below. */
    animation:
        welcome-nametag-pop-b-r13j5pxm0b .45s ease-out .25s backwards,
        welcome-nametag-wiggle-b-r13j5pxm0b .7s ease-in-out 1.15s 1;
}

/* Straighten when the pointer is over EITHER inner button (the whole sticker
   reads as pressable), same feel as the pre-9430 single-button hover. */
.welcome-nametag:hover[b-r13j5pxm0b] {
    transform: rotate(0deg);
    box-shadow: var(--meb-shadow-surface-comic);
}

.welcome-nametag--settled[b-r13j5pxm0b] {
    animation: none;
}

.welcome-nametag-strip[b-r13j5pxm0b] {
    background: var(--meb-accent-primary);
    color: var(--meb-text-inverse);
    font-size: var(--meb-font-size-body-sm-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-tight);
    letter-spacing: .16em;
    text-transform: uppercase;
    text-align: center;
    padding: var(--meb-space-stack-xs) var(--meb-space-inline-md);
}

/* 9430: disc | name row. The disc is a fixed leading column; the name block
   takes the rest. */
.welcome-nametag-body[b-r13j5pxm0b] {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--meb-space-inline-md);
    padding: var(--meb-space-stack-sm) var(--meb-space-inline-md);
}

/* 9430: avatar disc tap target — button reset, the nested Avatar carries the
   visual weight. */
.welcome-nametag-avatar-btn[b-r13j5pxm0b] {
    -webkit-appearance: none;
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    cursor: pointer;
    flex: 0 0 auto;
    border-radius: var(--meb-border-radius-full);
    outline: none;
}

.welcome-nametag-avatar-btn:focus-visible[b-r13j5pxm0b] {
    outline: var(--meb-focus-ring-width) solid var(--meb-accent-primary);
    outline-offset: var(--meb-focus-ring-offset);
}

/* 9430: name block tap target — button reset, name over the change affordance,
   left-aligned so the two lines stack under the name. */
.welcome-nametag-nameblock[b-r13j5pxm0b] {
    -webkit-appearance: none;
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
    outline: none;

    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--meb-space-stack-xxs);
    min-width: 0;  /* let the name ellipsis inside a flex row */
    text-align: left;
}

.welcome-nametag-nameblock:focus-visible[b-r13j5pxm0b] {
    outline: var(--meb-focus-ring-width) solid var(--meb-accent-primary);
    outline-offset: var(--meb-focus-ring-offset);
}

/* 9430: the inline avatar picker that expands under the badge on disc tap.
   Sizing cap only (same stance as the editor); AvatarPicker owns its own grid.
   align-items: STRETCH (not center) is load-bearing: the nested AvatarPicker is a
   grid whose auto-fill column count is driven by its width, so a centered (content-
   sized) child collapses to one 56px column. Stretch lets it fill this 300px cap so
   the grid lays out multiple columns. */
.welcome-nametag-avatar-picker[b-r13j5pxm0b] {
    width: 100%;
    max-width: 300px;
    display: flex;
    flex-direction: column;
    gap: var(--meb-space-stack-sm);
    align-items: stretch;
}

.welcome-nametag-name[b-r13j5pxm0b] {
    font-size: var(--meb-font-size-h3-desktop);
    font-weight: var(--meb-font-weight-bold);
    line-height: var(--meb-font-lineheight-heading);
    max-width: 14em;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.welcome-nametag-change[b-r13j5pxm0b] {
    display: inline-flex;
    align-items: center;
    gap: var(--meb-space-stack-xs);
    font-size: var(--meb-font-size-body-sm-desktop);
    color: var(--meb-text-muted);
}

/* Default-named (unrenamed anon): amplify the affordance — accent copy pulls
   first-timers into picking a real name without blocking the join. */
.welcome-nametag--default .welcome-nametag-change[b-r13j5pxm0b] {
    color: var(--meb-accent-primary);
    font-weight: var(--meb-font-weight-semibold);
}

.welcome-nametag-caption[b-r13j5pxm0b] {
    margin: 0;
    color: var(--meb-text-muted);
    font-family: var(--meb-font-family-primary);
    font-size: var(--meb-font-size-body-sm-desktop);
    line-height: var(--meb-font-lineheight-body);
}

/* Editor width cap: sizing, not layout (same stance as .welcome-card-actions);
   the Stack/Inline primitives inside own the actual layout. */
.welcome-nametag-editor[b-r13j5pxm0b] {
    width: 100%;
    max-width: 300px;
}

@keyframes welcome-nametag-pop-b-r13j5pxm0b {
    0% {
        transform: scale(.85) rotate(-1.5deg);
        opacity: 0;
    }
    60% {
        transform: scale(1.06) rotate(-1.5deg);
        opacity: 1;
    }
    100% {
        transform: scale(1) rotate(-1.5deg);
    }
}

@keyframes welcome-nametag-wiggle-b-r13j5pxm0b {
    0%, 100% { transform: rotate(-1.5deg); }
    25% { transform: rotate(1deg); }
    55% { transform: rotate(-2.6deg); }
    80% { transform: rotate(0deg); }
}

@media (prefers-reduced-motion: reduce) {
    .welcome-nametag[b-r13j5pxm0b] {
        animation: none;
        transition: none;
    }
}
/* _content/LiveEventBingo.Shared/Pages/Common/Pages/BingoGameFolder/WelcomeShell.razor.rz.scp.css */
/* PR1 (Inc2d): the calm pre-join shell chrome, now OWNED here (was duplicated in
   PlayGamePlaceholder.razor.css and the live-shell copies in
   Desktop/MobileBingoGameplay.razor.css -- scoped CSS doesn't cross component
   boundaries, so the one component that renders this markup carries the styles and
   every caller gets them for free). The card BODY is the shared WelcomeCardBody,
   which keeps its own scoped typography/chrome. */

/* ── Desktop shell ─────────────────────────────────────────────────────── */
.layout-game-prestart[b-v6n0u152o7] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    min-height: var(--layout-game-workspace-height, clamp(620px, 54vw, 760px));
    padding: var(--meb-space-stack-lg);
    box-sizing: border-box;
}

.layout-game-prestart-card[b-v6n0u152o7] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--meb-space-stack-lg);
    width: 100%;
    max-width: 640px;
    padding: var(--meb-space-stack-xl);
    border: var(--meb-border-width-sm) solid var(--meb-border-default);
    border-radius: var(--meb-border-radius-xl);
    background: var(--meb-surface-card);
    box-sizing: border-box;
    text-align: center;
}

/* The game icon (.layout-game-prestart-avatar until #10783) is now the shared GameIcon, which owns
   its own box. */

/* ── Mobile shell ──────────────────────────────────────────────────────── */
.layout-mobile-prestart[b-v6n0u152o7] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: var(--meb-space-stack-lg) var(--meb-space-inline-md);
    box-sizing: border-box;
}

/* Standalone (Fill="true") mobile shell pins a viewport floor so the page doesn't
   collapse to card height (footer riding up mid-screen). The in-stage mobile welcome
   omits Fill -- .mobile-root already provides this floor -- so it doesn't double up.
   Mirrors the desktop clamp floor on .layout-game-prestart above. */
.layout-mobile-prestart[data-fill="true"][b-v6n0u152o7] {
    min-height: calc(100dvh - 80px);
}
