/* /Components/Board/BeliefOverlay.razor.rz.scp.css */
/* BeliefOverlay scoped styles. Component-scoped: Blazor rewrites class names with a
   per-component scope id so nothing here leaks into ChessBoard / Admin / Game.

   The overlay SVG is layered on top of the underlying ChessBoard SVG via the parent
   wrapper's position: relative + this SVG's position: absolute. Pointer events fall
   through to the underlying squares (board interactions stay possible) — the
   spectator can't move pieces today, but the click-to-isolate sidebar mechanism in
   AdminObserve depends on the sidebar receiving the clicks, not the overlay. */

.belief-overlay-svg[b-nthqf02see] {
    /* Positioned over the sibling ChessBoard SVG. The parent (.observe-board-with-belief)
       sets position: relative; we absolutely-position to top:0 / left:0 with width/height
       matching the parent. The viewBox is identical to ChessBoard's so coordinate
       arithmetic round-trips. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: auto;
    pointer-events: none;
}

/* The "eye" badge above last-seen markers. SVG <text> font-size is set via the
   attribute (NOT the CSS property) per feedback_svg_text_fontsize. We use CSS only
   for color and weight, not size. */
.belief-turn-badge[b-nthqf02see] {
    fill: #1f2329;
    font-weight: 700;
    paint-order: stroke;
    stroke: rgba(255, 255, 255, 0.88);
    stroke-width: 0.04;
    stroke-linejoin: round;
}

.belief-cloud-label[b-nthqf02see] {
    fill: #1f2329;
    font-weight: 600;
    paint-order: stroke;
    stroke: rgba(255, 255, 255, 0.85);
    stroke-width: 0.04;
    stroke-linejoin: round;
}

/* Per-piece hue tint. The translucent piece icons themselves come from the standard
   sprite set (no per-color recolor possible without SVG filter manipulation), so the
   "hue" is applied via a CSS filter that drop-shadows the sprite in the tint color.
   This is intentionally subtle — the cloud is meant to read as "this piece" not
   "this color"; the sidebar's row marker carries the primary association. */
.belief-cloud image[b-nthqf02see],
.belief-last-seen image[b-nthqf02see] {
    /* drop-shadow lets the per-hue color seep around the sprite's outline without
       destroying its readability. The blur is tiny so the silhouette stays sharp. */
    filter: drop-shadow(0 0 0.04px var(--belief-hue, transparent));
}

.belief-cloud-outline rect[b-nthqf02see] {
    stroke: var(--belief-hue, #4a5562);
    fill: var(--belief-hue, transparent);
    fill-opacity: 0.08;
}

/* Eight discrete hues. Picked from a perceptually-spaced palette (Tol's vibrant-ish
   set) so adjacent pieceIds — e.g. the two knights on b1/g1, which differ by 6 file
   columns — stay distinguishable. Saturation kept moderate so overlapping clouds
   don't add into mud. */
.belief-hue-0[b-nthqf02see] { --belief-hue: #2c5cb6; } /* indigo */
.belief-hue-1[b-nthqf02see] { --belief-hue: #d65a4a; } /* terracotta */
.belief-hue-2[b-nthqf02see] { --belief-hue: #2f8f4a; } /* moss */
.belief-hue-3[b-nthqf02see] { --belief-hue: #c98712; } /* amber */
.belief-hue-4[b-nthqf02see] { --belief-hue: #7e3fa8; } /* violet */
.belief-hue-5[b-nthqf02see] { --belief-hue: #178a8a; } /* teal */
.belief-hue-6[b-nthqf02see] { --belief-hue: #9a3a6c; } /* magenta */
.belief-hue-7[b-nthqf02see] { --belief-hue: #4a5562; } /* slate */

/* Focus / dim states driven by the sidebar click-to-isolate. The focused piece's
   cloud goes a bit more opaque AND its hue tint deepens; every other piece's cloud
   fades to a ghost. This is the central UX gesture for keeping a multi-piece board
   readable. */
.belief-dim[b-nthqf02see] {
    opacity: 0.15;
}

.belief-focus image[b-nthqf02see] {
    opacity: 0.85 !important;
}

.belief-focus .belief-cloud-label[b-nthqf02see],
.belief-focus .belief-turn-badge[b-nthqf02see] {
    font-weight: 700;
}
/* /Components/Board/ChessBoard.razor.rz.scp.css */
/* ChessBoard component-scoped styles. Blazor rewrites class names with a per-component scope id. */

.chess-board-container[b-onk0ikqsfh] {
    /* width: 100% fills the flex-column parent (game-layout). Without it,
       margin: auto on the cross axis would override align-items: stretch and
       shrink-wrap the container to the SVG's intrinsic ~300px default.

       Vertical margin is 0: the parent .game-layout uses flex gap to space
       siblings, and the captures strips (when present) use a negative
       margin to sit flush against the board. A non-zero vertical margin
       here would re-introduce the dead-whitespace gap between the captures
       strip and the board that user feedback called out. */
    width: 100%;
    max-width: min(80vmin, 600px);
    margin: 0 auto;
    padding: 0;
    position: relative;
}

.chess-board[b-onk0ikqsfh] {
    width: 100%;
    height: auto;
    display: block;
    background: #ece4d3;
    border: 1px solid #5a4632;
    border-radius: 2px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}

.square[b-onk0ikqsfh] {
    cursor: pointer;
}

.square-light[b-onk0ikqsfh] {
    fill: #f0d9b5;
}

.square-dark[b-onk0ikqsfh] {
    fill: #b58863;
}

/* Currently-selected square: bright highlight. The "highlight" overlay is achieved
   by adjusting the fill rather than stroke -- SVG stroke on a 1x1 rect would land
   inside the cell and clip oddly when many cells are adjacent. */
.square.selected[b-onk0ikqsfh] {
    fill: #f7ec74;
}

/* In-check king square. A loud red wash plus a subtle pulsing animation so the
   player can't miss the "you're in check" signal (issue #11: user thought their
   moves should be legal and didn't realise their king was under attack). */
.square.in-check[b-onk0ikqsfh] {
    fill: #e85b5b;
    animation: bcfow-check-pulse-b-onk0ikqsfh 1.1s ease-in-out infinite;
}

.square.in-check.selected[b-onk0ikqsfh] {
    /* Keep the check tint visible even when the king is selected — selection
       alone shouldn't drown out the "I'm in check" signal. */
    fill: #ec6b6b;
}

@keyframes bcfow-check-pulse-b-onk0ikqsfh {
    0%, 100% { fill-opacity: 1; }
    50%      { fill-opacity: 0.72; }
}

/* Squares the selected piece can legally move to. The fill is a translucent yellow
   so the underlying light/dark distinction is preserved. The actual destination
   marker is the <circle> element rendered above (.legal-target-dot); this class
   exists so we can tweak the visual treatment in one place. */
.square.legal-target[b-onk0ikqsfh] {
    /* Subtle wash so the user perceives the entire square as a target, not just
       the centered dot. */
    fill-opacity: 0.92;
}

.square.legal-target.square-light[b-onk0ikqsfh] {
    fill: #d6eb87;
}

.square.legal-target.square-dark[b-onk0ikqsfh] {
    fill: #8aa550;
}

/* Last-move-from / last-move-to: subtle translucent yellow tint marking the
   origin and destination squares of the most-recently applied ply. The selected
   / legal-target classes still take precedence: an active selection lights the
   square brighter, so the previous move's highlight is overridden when the
   player picks a piece up. Anti-probing: the parent component intentionally
   suppresses these classes on squares that are fogged for the viewer (see
   ChessBoard.razor's LastMoveSquareClass) — fog must not be revealable through
   a UI tint. */
.square.last-move-from[b-onk0ikqsfh],
.square.last-move-to[b-onk0ikqsfh] {
    fill: #f7d674;
    fill-opacity: 0.55;
}

/* When a square is BOTH a last-move highlight AND the active selection, the
   selection wins (brighter yellow, full opacity). Specificity here is equal to
   .square.selected so the source-order rule applies; .square.selected is
   defined earlier above, so we re-state it here to defend against future
   shuffling. */
.square.selected.last-move-from[b-onk0ikqsfh],
.square.selected.last-move-to[b-onk0ikqsfh] {
    fill: #f7ec74;
    fill-opacity: 1;
}

.coord-label[b-onk0ikqsfh] {
    fill: #5a4632;
    /* font-size is set as SVG presentation attribute font-size="0.32" on each <text>
       element rather than here. CSS px units on SVG text trigger minimum-font-size
       enforcement which Chromium then treats as SVG user units, producing enormous
       labels. The SVG attribute bypasses CSS minimum-size enforcement entirely. */
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    font-weight: 500;
    user-select: none;
}

/* Piece sprites are 1x1 <image> elements positioned at (1 + file, 7 - rank).
   pointer-events: none lets clicks fall through to the underlying square <rect>;
   the smooth x/y transition gives free animation when a piece's coords change
   (e.g. after a move push from the server). */
.piece[b-onk0ikqsfh] {
    transition: x 200ms ease, y 200ms ease, opacity 200ms ease;
    pointer-events: none;
}

/* Own pieces that have no legal moves on this turn (pinned, blocked by own
   pieces, every-non-check-resolving piece while in check). Passive visual cue:
   the eye can scan the board and immediately see "here's what I can actually
   move." Still clickable (the underlying <rect> still receives clicks); selecting
   an immobile piece simply shows no destination dots. */
.piece.piece-immobile[b-onk0ikqsfh] {
    opacity: 0.4;
}

/* Legal-destination marker dot. Centered on the target square (cx/cy = file+1.5,
   rank+0.5). pointer-events: none so the click still hits the underlying <rect>.
   Opacity slightly bumped so the dot still reads against the dark fog texture
   (the fog reads as near-opaque deep blue-gray; a 0.35-alpha dark green dot
   washed out against it). */
.legal-target-dot[b-onk0ikqsfh] {
    fill: #5a8a2f;
    fill-opacity: 0.6;
    pointer-events: none;
}

/* Fog-of-war overlay. A 1x1 <rect> drawn on top of each fogged square, filled
   with a turbulent-noise pattern (#fog-pattern in the SVG defs) so the fog
   looks like drifting smoke rather than a solid tint. The pattern is anchored
   to the board's origin and is exactly the board's size, so adjacent fogged
   squares show contiguous portions of the same noise field — no visible seams.
   The fill is opaque enough to fully bury the light/dark check pattern beneath:
   a fogged square reads as FOG FIRST, not as a tinted square.

   pointer-events: none is critical — clicks must pass through to the
   underlying square <rect> so the player can still click into fog to commit
   a move-into-fog. Pieces (own only, opponent ones in fog are filtered out
   server-side) and legal-target dots draw later in z-order and remain
   visible on top. */
.fog-overlay[b-onk0ikqsfh] {
    fill: url(#fog-pattern);
    pointer-events: none;
}

@media (max-width: 480px) {
    .chess-board-container[b-onk0ikqsfh] {
        margin: 1rem auto;
        max-width: 95vmin;
    }
}
/* /Components/Board/PromotionDialog.razor.rz.scp.css */
/* PromotionDialog - fixed overlay + centered card with four piece-choice buttons.
   The chess.com-ish palette (#f0d9b5 / #b58863) ties the card visually to the board. */

.promotion-overlay[b-ojp3hmivpt] {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 1000;
}

.promotion-dialog[b-ojp3hmivpt] {
    background: #f0d9b5;
    border: 2px solid #b58863;
    border-radius: 6px;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
    padding: 0.75rem;
    display: flex;
    flex-direction: row;
    gap: 0.5rem;
}

.promotion-choice[b-ojp3hmivpt] {
    background: #ece4d3;
    border: 1px solid #b58863;
    border-radius: 4px;
    width: 64px;
    height: 64px;
    min-width: 44px;
    min-height: 44px;
    padding: 4px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 120ms ease, transform 80ms ease;
}

    .promotion-choice:hover[b-ojp3hmivpt] {
        background: #f7ec74;
    }

    .promotion-choice:active[b-ojp3hmivpt] {
        transform: scale(0.96);
    }

    .promotion-choice:focus-visible[b-ojp3hmivpt] {
        outline: 2px solid #5a4632;
        outline-offset: 2px;
    }

    .promotion-choice img[b-ojp3hmivpt] {
        width: 100%;
        height: 100%;
        object-fit: contain;
        pointer-events: none;
    }

@media (max-width: 480px) {
    .promotion-dialog[b-ojp3hmivpt] {
        padding: 0.5rem;
        gap: 0.4rem;
    }

    .promotion-choice[b-ojp3hmivpt] {
        width: 56px;
        height: 56px;
    }
}
/* /Components/Pages/Admin.razor.rz.scp.css */
/* Admin page styles. Component-scoped: Blazor rewrites class names with a per-component
   scope id, so nothing here leaks into Home / Lobby / Game. The page has two top-level
   modes:
     1. .admin-login    — single centred card for the token-paste form.
     2. .admin-layout   — tabbed dashboard once the admin-session cookie is present.
*/

/* ============================== Login card ============================== */

.admin-login[b-ad6rra0wyk] {
    max-width: 28rem;
    margin: 2rem auto;
    padding: 1.25rem 1.5rem 1.5rem;
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.admin-login h1[b-ad6rra0wyk] {
    margin: 0 0 0.5rem;
    font-size: 1.4rem;
}

.admin-login .lead[b-ad6rra0wyk] {
    margin: 0 0 0.9rem;
    color: #3a3f47;
    font-size: 0.95rem;
}

.admin-login .lead code[b-ad6rra0wyk] {
    background: #f1f3f5;
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    font-size: 0.85rem;
}

/* Shared field + input styling reused by the create-user form too. */
.field[b-ad6rra0wyk] {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin: 0 0 0.75rem;
}

.field-label[b-ad6rra0wyk] {
    font-size: 0.9rem;
    font-weight: 600;
    color: #1f2329;
}

.field-input[b-ad6rra0wyk] {
    appearance: none;
    width: 100%;
    padding: 0.5rem 0.7rem;
    border: 1px solid #c8ccd0;
    border-radius: 4px;
    font-size: 1rem;
    color: #1f2329;
    background: #ffffff;
}

.field-input:focus[b-ad6rra0wyk] {
    outline: none;
    border-color: #2c5cb6;
    box-shadow: 0 0 0 2px rgba(44, 92, 182, 0.25);
}

.field-inline[b-ad6rra0wyk] {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: #1f2329;
    font-size: 0.9rem;
    cursor: pointer;
}

/* The .cta class on the sign-in button matches the global app.css call-to-action; we
   redeclare here so the login card looks consistent even if app.css is not loaded. */
.cta[b-ad6rra0wyk] {
    display: inline-block;
    padding: 0.55rem 1.2rem;
    background: #2c5cb6;
    color: #ffffff;
    border: none;
    border-radius: 4px;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.95rem;
}

.cta:hover[b-ad6rra0wyk] {
    background: #244c98;
}

/* ============================== Dashboard layout ============================== */

.admin-layout[b-ad6rra0wyk] {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin: 0 auto;
    max-width: 1080px;
}

.admin-header[b-ad6rra0wyk] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.admin-header h1[b-ad6rra0wyk] {
    margin: 0;
    font-size: 1.6rem;
}

.admin-tabs[b-ad6rra0wyk] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.4rem;
}

/* Tab buttons share visual language with .btn-secondary but with a flat-bottom indicator
   for the active tab so the section header below feels visually attached. */
.tab-button[b-ad6rra0wyk] {
    appearance: none;
    border: 1px solid #c8ccd0;
    background: #ffffff;
    color: #1f2329;
    padding: 0.45rem 1rem;
    border-radius: 4px;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.95rem;
}

.tab-button:hover[b-ad6rra0wyk] {
    background: #f1f3f5;
}

.tab-button.active[b-ad6rra0wyk] {
    background: #2c5cb6;
    color: #ffffff;
    border-color: #2c5cb6;
}

.logout-form[b-ad6rra0wyk] {
    margin: 0;
    display: inline;
}

.btn-logout[b-ad6rra0wyk] {
    appearance: none;
    border: 1px solid #c8ccd0;
    background: #ffffff;
    color: #1f2329;
    padding: 0.45rem 0.9rem;
    border-radius: 4px;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.9rem;
}

.btn-logout:hover[b-ad6rra0wyk] {
    background: #f1f3f5;
}

/* ============================== Section wrapper ============================== */

.admin-section[b-ad6rra0wyk] {
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    padding: 0.85rem 1rem 1rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.section-header[b-ad6rra0wyk] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: 0.6rem;
}

.section-header h2[b-ad6rra0wyk] {
    margin: 0;
    font-size: 1.15rem;
}

.filter-controls[b-ad6rra0wyk] {
    display: flex;
    gap: 0.5rem;
    align-items: center;
}

/* ============================== Create-user inline form ============================== */

.create-form[b-ad6rra0wyk] {
    background: #f6f7f9;
    border: 1px solid #d8dadd;
    border-radius: 4px;
    padding: 0.75rem 1rem;
    margin-bottom: 0.75rem;
}

.create-form .field[b-ad6rra0wyk] {
    margin-bottom: 0.5rem;
}

.form-actions[b-ad6rra0wyk] {
    display: flex;
    gap: 0.5rem;
}

/* ============================== Issued-token panel ============================== */

.issued-token[b-ad6rra0wyk] {
    margin-bottom: 0.75rem;
}

.issued-token .token-value[b-ad6rra0wyk] {
    display: inline-block;
    padding: 0.2rem 0.45rem;
    background: rgba(255, 255, 255, 0.7);
    border: 1px solid #a8c0f5;
    border-radius: 3px;
    font-size: 0.95rem;
    word-break: break-all;
    user-select: all;
}

.issued-token .hint[b-ad6rra0wyk] {
    margin: 0.35rem 0 0.5rem;
    font-style: italic;
}

/* ============================== Data tables ============================== */

.data-table[b-ad6rra0wyk] {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.92rem;
}

.data-table thead th[b-ad6rra0wyk] {
    text-align: left;
    padding: 0.5rem 0.6rem;
    background: #eef0f3;
    color: #1f2329;
    border-bottom: 1px solid #d8dadd;
    font-weight: 600;
}

.data-table tbody td[b-ad6rra0wyk] {
    padding: 0.5rem 0.6rem;
    border-bottom: 1px solid #eef0f3;
    color: #1f2329;
    vertical-align: top;
}

.data-table tbody tr:nth-child(odd) td[b-ad6rra0wyk] {
    background: #fafbfc;
}

.data-table tbody tr.row-inactive td[b-ad6rra0wyk] {
    color: #7a7e85;
    background: #f4f4f5;
}

.actions-col[b-ad6rra0wyk] {
    white-space: nowrap;
    text-align: right;
}

.actions-col button + button[b-ad6rra0wyk] {
    margin-left: 0.35rem;
}

/* Nested inner token table is rendered as a sub-row spanning the full grid. Quieter
   background + tighter padding to read as "context for the row above". */
.token-row td[b-ad6rra0wyk] {
    background: #f9fafb !important;
    padding: 0.5rem 0.75rem;
}

.inner-table[b-ad6rra0wyk] {
    background: transparent;
    font-size: 0.88rem;
}

.inner-table thead th[b-ad6rra0wyk] {
    background: #e6e8eb;
}

/* ============================== Status pills ============================== */

.status-active[b-ad6rra0wyk] {
    color: #1f6b3a;
    font-weight: 600;
}

.status-revoked[b-ad6rra0wyk] {
    color: #8a1f1f;
    font-weight: 600;
}

.status-inprogress[b-ad6rra0wyk] {
    background: #2c5cb6;
    color: #ffffff;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
}

.status-whitewins[b-ad6rra0wyk],
.status-blackwins[b-ad6rra0wyk],
.status-draw[b-ad6rra0wyk] {
    background: #eaecef;
    color: #555;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
}

.status-abandoned[b-ad6rra0wyk] {
    background: #fde8e8;
    color: #8a1f1f;
    padding: 0.1rem 0.45rem;
    border-radius: 999px;
    font-size: 0.8rem;
    font-weight: 600;
}

/* ============================== Button palette ============================== */

.btn-primary[b-ad6rra0wyk],
.btn-secondary[b-ad6rra0wyk],
.btn-danger[b-ad6rra0wyk] {
    appearance: none;
    border: none;
    border-radius: 4px;
    padding: 0.45rem 0.9rem;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.9rem;
}

.btn-primary[b-ad6rra0wyk] {
    background: #2c5cb6;
    color: #ffffff;
}

.btn-primary:hover:not(:disabled)[b-ad6rra0wyk] {
    background: #244c98;
}

.btn-primary:disabled[b-ad6rra0wyk] {
    background: #8aa2cf;
    cursor: not-allowed;
}

.btn-secondary[b-ad6rra0wyk] {
    background: #ffffff;
    color: #1f2329;
    border: 1px solid #c8ccd0;
}

.btn-secondary:hover[b-ad6rra0wyk] {
    background: #f1f3f5;
}

.btn-danger[b-ad6rra0wyk] {
    background: #ffffff;
    color: #8a1f1f;
    border: 1px solid #f5a8a8;
}

.btn-danger:hover[b-ad6rra0wyk] {
    background: #fde8e8;
}

.btn-small[b-ad6rra0wyk] {
    padding: 0.3rem 0.6rem;
    font-size: 0.85rem;
}

/* When an anchor adopts the button palette (used for navigation actions like
   "Observe" that should look like buttons but ARE links), strip the default
   underline + inline-spacing so it visually matches sibling <button> elements
   in an actions cell. */
.action-link[b-ad6rra0wyk] {
    display: inline-block;
    text-decoration: none;
    margin-right: 0.35rem;
    vertical-align: middle;
}

.action-link:hover[b-ad6rra0wyk] {
    text-decoration: none;
}

/* ============================== Notices (shared with other pages) ============================== */

.notice[b-ad6rra0wyk] {
    margin: 0 0 0.75rem;
    padding: 0.55rem 0.8rem;
    border-radius: 4px;
    font-size: 0.95rem;
}

.notice-error[b-ad6rra0wyk] {
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
}

.notice-info[b-ad6rra0wyk] {
    background: #e8f0fd;
    border: 1px solid #a8c0f5;
    color: #1f3a8a;
}

.hint[b-ad6rra0wyk] {
    margin: 0;
    color: #6a6f78;
    font-style: italic;
}

/* Inline badges shown to the right of a user's display name in the Users-tab
   table. Each is a small all-caps pill: visually scannable, doesn't disrupt
   the row layout, conveys role/identity without a dedicated column. */
.admin-badge[b-ad6rra0wyk],
.self-badge[b-ad6rra0wyk] {
    display: inline-block;
    margin-left: 0.4rem;
    padding: 0.05rem 0.4rem;
    border-radius: 10px;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    vertical-align: middle;
}

.admin-badge[b-ad6rra0wyk] {
    background: #2c5cb6;
    color: #fff;
}

.self-badge[b-ad6rra0wyk] {
    background: #d4a72c;
    color: #1f2329;
}

/* Display-name anchor on the Users tab — clicking navigates to the Games tab
   filtered to that user. Inherit text color so it doesn't look hyperlinky in
   a dense table, but underline on hover so the affordance is discoverable. */
.user-name-link[b-ad6rra0wyk] {
    color: inherit;
    text-decoration: none;
    border-bottom: 1px dotted #a8b0bd;
}

.user-name-link:hover[b-ad6rra0wyk] {
    color: #2c5cb6;
    border-bottom-color: #2c5cb6;
    text-decoration: none;
}

/* Active-filter banner on the Games tab when ?user= is set. Sits between the
   section header and the table so the admin can see "what filter is currently
   applied" without scanning the dropdown state. */
.active-filter-banner[b-ad6rra0wyk] {
    margin: 0 0 0.75rem;
    padding: 0.5rem 0.75rem;
    background: #fffbe6;
    border: 1px solid #f3e0a3;
    border-left: 4px solid #d4a72c;
    border-radius: 3px;
    font-size: 0.92rem;
    color: #5a4632;
}

.active-filter-banner a[b-ad6rra0wyk] {
    margin-left: 0.5rem;
}

/* ============================== Mobile layout ============================== */

/* Below 480px the dense data tables don't fit. We keep them scrollable horizontally
   rather than reshaping into card layouts — the admin is a power user surface and a
   horizontal scroll is more legible than collapsed rows for tabular data. */
@media (max-width: 480px) {
    .admin-header[b-ad6rra0wyk] {
        flex-direction: column;
        align-items: stretch;
    }

    .admin-tabs[b-ad6rra0wyk] {
        justify-content: stretch;
    }

    .admin-tabs .tab-button[b-ad6rra0wyk],
    .admin-tabs .logout-form[b-ad6rra0wyk],
    .admin-tabs .btn-logout[b-ad6rra0wyk] {
        flex: 1 1 auto;
        text-align: center;
    }

    .section-header[b-ad6rra0wyk] {
        flex-direction: column;
        align-items: stretch;
    }

    .admin-section[b-ad6rra0wyk] {
        overflow-x: auto;
    }

    .data-table[b-ad6rra0wyk] {
        min-width: 38rem;
    }

    .actions-col[b-ad6rra0wyk] {
        text-align: left;
    }
}

/* Activity dashboard (issue #9). Site-wide summary cards in a responsive grid above
   the per-user table. Each card shows one rollup metric — label on top, big value
   underneath. */
.activity-summary[b-ad6rra0wyk] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
    gap: 0.75rem;
    margin: 0 0 1.25rem;
}

.summary-card[b-ad6rra0wyk] {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    padding: 0.7rem 0.9rem;
    background: #f1f3f5;
    border: 1px solid #d8dadd;
    border-radius: 6px;
}

.summary-label[b-ad6rra0wyk] {
    font-size: 0.78rem;
    font-weight: 600;
    color: #4b525c;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.summary-value[b-ad6rra0wyk] {
    font-size: 1.25rem;
    font-weight: 700;
    color: #1f2329;
}

.header-actions[b-ad6rra0wyk] {
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.field-checkbox[b-ad6rra0wyk] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin: 0.5rem 0;
}
/* /Components/Pages/AdminObserve.razor.rz.scp.css */
/* AdminObserve scoped styles. Component-scoped: Blazor rewrites class names with a
   per-component scope id, so nothing here leaks into Admin / Game / Lobby. Page layout
   only — the ChessBoard component carries its own styling for the SVG board itself,
   and we deliberately do not restyle it from here. */

.observe-layout[b-ipc5gqu9nc] {
    /* Phase 3 widened the layout so the belief sidebar fits at desktop widths; the
       white/black/truth POVs still center on a narrow board because the .observe-
       belief-layout below caps its own width independently. */
    max-width: 72rem;
    margin: 1rem auto;
    padding: 0 1rem 2rem;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

.observe-header[b-ipc5gqu9nc] {
    margin: 0 0 1rem;
}

.observe-header h1[b-ipc5gqu9nc] {
    margin: 0 0 0.25rem;
    font-size: 1.4rem;
}

.observe-subline[b-ipc5gqu9nc] {
    margin: 0 0 0.85rem;
    color: #3a3f47;
    font-size: 0.9rem;
}

.observe-subline code[b-ipc5gqu9nc] {
    background: #f1f3f5;
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    font-size: 0.85rem;
}

/* Phase 2 live indicator — small pill that lights up once the SignalR observer
   connection succeeds. Visible alongside the status/ply summary so an admin can
   tell at a glance whether they're watching live state or a stale SSR snapshot. */
.observe-live-tag[b-ipc5gqu9nc] {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    background: #2c5cb6;
    color: #ffffff;
    border-radius: 999px;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    vertical-align: 0.05em;
}

/* POV switcher: three pill-shaped links forming a segmented control. Each is a
   real anchor so the URL changes on click and the page can re-render with the
   correct snapshot from the SSR pass — no JS / SignalR needed for Phase 1. */
.observe-pov[b-ipc5gqu9nc] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0 0 0.75rem;
}

.observe-pov-pill[b-ipc5gqu9nc] {
    display: inline-block;
    padding: 0.4rem 0.95rem;
    background: #ffffff;
    color: #1f2329;
    border: 1px solid #c8ccd0;
    border-radius: 999px;
    font-size: 0.9rem;
    text-decoration: none;
    line-height: 1.2;
    cursor: pointer;
}

.observe-pov-pill:hover[b-ipc5gqu9nc] {
    background: #f1f3f5;
}

.observe-pov-pill-active[b-ipc5gqu9nc],
.observe-pov-pill-active:hover[b-ipc5gqu9nc] {
    background: #2c5cb6;
    color: #ffffff;
    border-color: #2c5cb6;
    cursor: default;
}

.observe-last-move[b-ipc5gqu9nc] {
    margin: 0.5rem 0 0;
    font-size: 0.95rem;
    color: #1f2329;
}

.observe-last-move code[b-ipc5gqu9nc] {
    background: #f1f3f5;
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    font-size: 0.9rem;
}

.observe-fogged[b-ipc5gqu9nc] {
    color: #6a727b;
    font-style: italic;
}

.observe-footer[b-ipc5gqu9nc] {
    margin-top: 1.25rem;
    text-align: center;
}

.observe-footer .game-link[b-ipc5gqu9nc] {
    color: #2c5cb6;
    text-decoration: none;
    font-size: 0.95rem;
}

.observe-footer .game-link:hover[b-ipc5gqu9nc] {
    text-decoration: underline;
}

/* Error / "cannot observe" panel — used for the not-authed and load-error branches.
   Mirrors the admin-login card visually so a returning admin recognises the chrome. */
.observe-error[b-ipc5gqu9nc] {
    max-width: 32rem;
    margin: 2rem auto;
    padding: 1.25rem 1.5rem 1.5rem;
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.observe-error h1[b-ipc5gqu9nc] {
    margin: 0 0 0.5rem;
    font-size: 1.3rem;
}

.observe-error p[b-ipc5gqu9nc] {
    margin: 0 0 0.5rem;
    color: #3a3f47;
}

.observe-error a[b-ipc5gqu9nc] {
    color: #2c5cb6;
}

/* ================================================================================
   Phase 3 — AI BeliefState view.

   The belief layout is a two-column grid at desktop widths: board on the left, sidebar
   on the right. Below the mobile break the sidebar drops underneath the board so the
   8x8 grid still fits a 360px phone screen.

   The board-with-belief wrapper is position:relative so the BeliefOverlay's absolute
   SVG layers exactly on top of the ChessBoard's SVG. We don't restyle the underlying
   ChessBoard component from here — it carries its own scoped styles.
   ================================================================================ */

.observe-belief-layout[b-ipc5gqu9nc] {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 22rem;
    gap: 1.25rem;
    align-items: flex-start;
}

.observe-board-with-belief[b-ipc5gqu9nc] {
    position: relative;
    /* Cap the board's width so the overlay's SVG (which uses width: 100% inside this
       wrapper) follows. Matches the ChessBoard component's max-width. */
    max-width: min(80vmin, 600px);
    margin: 0 auto;
    width: 100%;
}

.observe-belief-sidebar[b-ipc5gqu9nc] {
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    padding: 0.85rem 1rem 1rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
    /* Cap the sidebar's height so the page doesn't grow taller than the board on
       small games; the list scrolls if it overflows. */
    max-height: min(80vmin, 600px);
    overflow-y: auto;
}

.observe-belief-sidebar-title[b-ipc5gqu9nc] {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
    color: #1f2329;
}

.observe-belief-believer[b-ipc5gqu9nc] {
    display: block;
    font-size: 0.8rem;
    font-weight: 400;
    color: #6a727b;
    margin-top: 0.15rem;
}

.observe-belief-clear-focus[b-ipc5gqu9nc] {
    display: inline-block;
    margin: 0 0 0.6rem;
    padding: 0.25rem 0.6rem;
    background: #f1f3f5;
    border: 1px solid #c8ccd0;
    border-radius: 4px;
    font-size: 0.8rem;
    color: #1f2329;
    cursor: pointer;
}

.observe-belief-clear-focus:hover[b-ipc5gqu9nc] {
    background: #e5e7eb;
}

.observe-belief-list[b-ipc5gqu9nc] {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.observe-belief-row[b-ipc5gqu9nc] {
    margin: 0;
    padding: 0;
}

.observe-belief-row-button[b-ipc5gqu9nc] {
    display: flex;
    width: 100%;
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.45rem 0.55rem;
    background: transparent;
    border: 1px solid transparent;
    border-radius: 4px;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.observe-belief-row-button:hover[b-ipc5gqu9nc] {
    background: #f7f8fa;
}

.observe-belief-row-button:disabled[b-ipc5gqu9nc] {
    cursor: default;
    opacity: 1; /* the row's gray-out is handled by .row-captured below, not the disabled state */
}

.observe-belief-row-icon[b-ipc5gqu9nc] {
    /* Unicode chess glyphs render larger than the surrounding text — pin the size and
       align to top so the row stays a fixed height regardless of the secondary lines. */
    font-size: 1.6rem;
    line-height: 1;
    flex: 0 0 1.6rem;
    text-align: center;
    /* The hue tint comes through as a left border accent — using border-left rather
       than text-color keeps the glyph itself readable in black against any hue. */
    border-left: 3px solid var(--belief-hue, #c8ccd0);
    padding-left: 0.4rem;
}

.observe-belief-row-text[b-ipc5gqu9nc] {
    display: flex;
    flex-direction: column;
    gap: 0.05rem;
    min-width: 0;
}

.observe-belief-row-primary[b-ipc5gqu9nc] {
    font-size: 0.92rem;
    color: #1f2329;
    font-weight: 600;
}

.observe-belief-row-start[b-ipc5gqu9nc] {
    font-weight: 400;
    font-size: 0.82rem;
    color: #6a727b;
    margin-left: 0.25rem;
}

.observe-belief-row-secondary[b-ipc5gqu9nc] {
    font-size: 0.78rem;
    color: #3a3f47;
    line-height: 1.3;
}

.observe-belief-row-captured .observe-belief-row-primary[b-ipc5gqu9nc],
.observe-belief-row-captured .observe-belief-row-icon[b-ipc5gqu9nc],
.observe-belief-row-captured .observe-belief-row-secondary[b-ipc5gqu9nc] {
    color: #9aa1a8;
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

.observe-belief-row-focused[b-ipc5gqu9nc] {
    background: #eaf1fb;
    border-radius: 4px;
}

.observe-belief-row-focused .observe-belief-row-button[b-ipc5gqu9nc] {
    border-color: #2c5cb6;
}

/* Hue palette — must match BeliefOverlay.razor.css. Sidebar uses border-left accent
   instead of fill, so the same palette acts as the per-piece color anchor. */
.observe-belief-hue-0[b-ipc5gqu9nc] { --belief-hue: #2c5cb6; }
.observe-belief-hue-1[b-ipc5gqu9nc] { --belief-hue: #d65a4a; }
.observe-belief-hue-2[b-ipc5gqu9nc] { --belief-hue: #2f8f4a; }
.observe-belief-hue-3[b-ipc5gqu9nc] { --belief-hue: #c98712; }
.observe-belief-hue-4[b-ipc5gqu9nc] { --belief-hue: #7e3fa8; }
.observe-belief-hue-5[b-ipc5gqu9nc] { --belief-hue: #178a8a; }
.observe-belief-hue-6[b-ipc5gqu9nc] { --belief-hue: #9a3a6c; }
.observe-belief-hue-7[b-ipc5gqu9nc] { --belief-hue: #4a5562; }

.observe-belief-unavailable[b-ipc5gqu9nc] {
    max-width: 36rem;
    margin: 1rem auto;
    padding: 1rem 1.25rem;
    background: #fffdf3;
    border: 1px solid #e6dca6;
    border-radius: 6px;
    color: #1f2329;
}

.observe-belief-unavailable p[b-ipc5gqu9nc] {
    margin: 0.25rem 0;
    font-size: 0.92rem;
}

.observe-belief-unavailable a[b-ipc5gqu9nc] {
    color: #2c5cb6;
}

/* Mobile tightening: collapse the POV pills onto two rows comfortably, shrink the
   ply / status header. Matches the responsive break used in Game.razor.css. */
@media (max-width: 720px) {
    /* Drop the belief sidebar underneath the board so the 8x8 grid still fits a
       narrow phone screen. The sidebar inherits its max-width from the layout. */
    .observe-belief-layout[b-ipc5gqu9nc] {
        grid-template-columns: minmax(0, 1fr);
    }
    .observe-belief-sidebar[b-ipc5gqu9nc] {
        max-height: none;
    }
}

@media (max-width: 640px) {
    .observe-layout[b-ipc5gqu9nc] {
        padding: 0 0.5rem 1rem;
    }
    .observe-header h1[b-ipc5gqu9nc] {
        font-size: 1.2rem;
    }
    .observe-pov-pill[b-ipc5gqu9nc] {
        padding: 0.35rem 0.7rem;
        font-size: 0.85rem;
    }
}
/* /Components/Pages/Game.razor.rz.scp.css */
/* Game page layout. Component-scoped: Blazor rewrites class names with a per-component
   scope id, so these styles never leak into ChessBoard or other pages. */

/* Atmospheric flanks: two fixed-position panels left and right of the viewport
   showing the player's encampment and the fog-shrouded opponent. Hidden below
   ~1200px viewport so they never compete with the board on narrower screens.

   background-position aligns each panel so its FOG edge faces inward toward
   the board: flank-left.jpg's fog dissolves on its right side, flank-right.jpg's
   fog dissolves on its left. With background-size: cover and position-right /
   position-left, the inward fog edge is what visually meets the board area.

   pointer-events: none keeps clicks falling through (the panels are pure chrome,
   no interactivity). z-index: 0 + position:relative + z-index:1 on the content
   below ensures clicks and styles land correctly. */
/* The flank panels now extend right up to the board's edge so the player
   feels enveloped in the medieval theme rather than seeing a strip of page
   background between the art and the board.

   Width formula: 50vw - (half the board's max width + a small gap). The
   board is centered with max-width 600px (see .game-layout below), so its
   horizontal extent is from (50vw - 300px) to (50vw + 300px). Setting each
   flank's width to (50vw - 316px) leaves a thin 16px breathing strip on
   either side of the board — close enough to feel framed by the art, far
   enough that the flank art doesn't crowd the squares. */
.game-flank[b-8nw994av26] {
    position: fixed;
    top: 0;
    bottom: 0;
    width: calc(50vw - 316px);
    background-size: cover;
    background-repeat: no-repeat;
    z-index: 0;
    pointer-events: none;
    display: none;
}

.game-flank-left[b-8nw994av26] {
    left: 0;
    background-image: url('/img/art/flank-left.jpg');
    background-position: right center;
}

.game-flank-right[b-8nw994av26] {
    right: 0;
    background-image: url('/img/art/flank-right.jpg');
    background-position: left center;
}

@media (min-width: 1200px) {
    .game-flank[b-8nw994av26] {
        display: block;
    }
}

.game-layout[b-8nw994av26] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 1rem;
    margin: 0 auto;
    /* The board's own CSS caps its width; the surrounding column matches that visual
       width on desktop and uses 100% of the container on narrower viewports. */
    max-width: min(80vmin, 600px);

    /* Sit above the fixed atmospheric flanks (which paint at z-index: 0) so the
       game UI is never visually occluded by background art. */
    position: relative;
    z-index: 1;
}

/* Captures strip — small piece icons in a horizontal row above and below the
   board showing the opposing color's pieces that have been taken. Convention:
   the trophy case for each player is on THEIR side of the board (top strip
   shows what the opponent has taken from the viewer; bottom shows what the
   viewer has taken from the opponent). Empty until first capture, in which
   case the @if guards above keep the strip element absent — no empty rows. */
.captures-strip[b-8nw994av26] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.18rem;
    min-height: 1.6rem;
    padding: 0.3rem 0.4rem;
    margin: 0;
    /* Translucent banner so the medieval flank art shows through. Border
       echoes the board's. */
    background: rgba(31, 35, 41, 0.55);
    border: 1px solid rgba(90, 70, 50, 0.6);
    border-radius: 3px;
}

/* Negative margins cancel the .game-layout flex gap (1rem) on the edge
   adjacent to the board, so the captures banner sits flush against the
   board with no dead white space — the user'"'"'s eye stays on the play
   area rather than getting pulled away to the gap. */
.captures-top[b-8nw994av26] {
    margin-bottom: -1rem;
}

.captures-bottom[b-8nw994av26] {
    margin-top: -1rem;
}

.captured-piece[b-8nw994av26] {
    width: 1.4rem;
    height: 1.4rem;
    /* Subtle drop-shadow so the small icons read against the translucent
       dark banner background. */
    filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.6));
}

@media (max-width: 480px) {
    .captured-piece[b-8nw994av26] {
        width: 1.15rem;
        height: 1.15rem;
    }
}

.game-loading[b-8nw994av26] {
    text-align: center;
    color: #555;
    padding: 2rem 0;
    position: relative;
    z-index: 1;
}

.game-error[b-8nw994av26] {
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
    padding: 0.75rem 1rem;
    border-radius: 4px;
    margin: 1.5rem auto;
    max-width: 36rem;
    position: relative;
    z-index: 1;
}

/* Status header above the board.
   Phase 2 is intentionally minimal — Phase 6 polish is the one to make this look pretty. */
.game-status[b-8nw994av26] {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 0.6rem 0.9rem;
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.game-status p[b-8nw994av26] {
    margin: 0;
}

.game-status-line[b-8nw994av26] {
    font-weight: 600;
    color: #1f2329;
}

.game-status-turn[b-8nw994av26] {
    /* Reserved for Phase 6 polish — colour by side, etc. */
}

/* "Check!" badge in the status line, shown when the snapshot's InCheck is true
   (issue #11). Loud red so the user can't miss it. Sits inline next to the
   "Your move (White)" label. */
.game-status-check[b-8nw994av26] {
    display: inline-block;
    margin-left: 0.6rem;
    padding: 0.1rem 0.55rem;
    background: #c93030;
    color: #ffffff;
    font-weight: 700;
    font-size: 0.9rem;
    letter-spacing: 0.04em;
    border-radius: 4px;
    text-transform: uppercase;
}

.game-status-last[b-8nw994av26] {
    font-size: 0.9rem;
    color: #555;
}

.game-status-last code[b-8nw994av26] {
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.95em;
    background: #f1f3f5;
    padding: 0.05rem 0.35rem;
    border-radius: 3px;
}

/* "hidden in fog" placeholder shown when the per-viewer snapshot filter
   suppressed an opponent's last-move SAN because the destination is fogged
   for this viewer (issue #7). Italicised + muted so it reads as metadata
   rather than a move name. */
.game-status-last .game-status-fogged[b-8nw994av26] {
    color: #7a8089;
    font-style: italic;
}

.game-status-banner[b-8nw994av26] {
    font-size: 1.1rem;
    font-weight: 700;
    text-align: center;
    padding: 0.4rem 0.6rem;
    border-radius: 4px;
}

.game-status-end[b-8nw994av26] {
    background: #1f2329;
    color: #fff;
}

.game-status-error[b-8nw994av26] {
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
    padding: 0.4rem 0.6rem;
    border-radius: 4px;
    font-size: 0.9rem;
}

.game-actions[b-8nw994av26] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    justify-content: center;
    padding: 0.5rem 0;
    position: relative;
    z-index: 1;
}

.game-actions .cta[b-8nw994av26] {
    /* Pull the global .cta margin off — the action row controls spacing now. */
    margin-top: 0;
}

.game-link[b-8nw994av26] {
    font-size: 0.95rem;
    color: #2c5cb6;
}

@media (max-width: 480px) {
    .game-layout[b-8nw994av26] {
        gap: 0.75rem;
        max-width: 100%;
    }

    .game-status[b-8nw994av26] {
        padding: 0.5rem 0.75rem;
    }
}

/* ===========================================================================
   Issue #15 Phase 4 — spectator share panel.

   The panel sits below the post-game actions footer, visible only when the
   game is in a terminal status. Card-style chrome matching the .game-status
   line above so the post-game state reads as a cohesive bottom-of-board block.
   =========================================================================== */

.share-panel[b-8nw994av26] {
    margin-top: 1rem;
    padding: 1rem 1.1rem;
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.share-panel-title[b-8nw994av26] {
    margin: 0 0 0.4rem;
    font-size: 1.05rem;
    color: #1f2329;
}

.share-panel-blurb[b-8nw994av26] {
    margin: 0 0 0.75rem;
    font-size: 0.9rem;
    color: #3a3f47;
}

.share-panel-mint[b-8nw994av26] {
    display: inline-block;
    padding: 0.45rem 0.95rem;
    background: #2c5cb6;
    color: #ffffff;
    border: 1px solid #2c5cb6;
    border-radius: 4px;
    font-size: 0.9rem;
    cursor: pointer;
}

.share-panel-mint:disabled[b-8nw994av26] {
    background: #8da6cf;
    border-color: #8da6cf;
    cursor: not-allowed;
}

.share-panel-mint:hover:not(:disabled)[b-8nw994av26] {
    background: #224a96;
}

.share-panel-just-minted[b-8nw994av26] {
    margin-top: 0.75rem;
    padding: 0.6rem 0.75rem;
    background: #f1f7e8;
    border: 1px solid #b8c98a;
    border-radius: 4px;
}

.share-panel-just-minted-label[b-8nw994av26] {
    margin: 0 0 0.35rem;
    font-size: 0.85rem;
    color: #1f2329;
}

.share-panel-just-minted-url[b-8nw994av26] {
    display: block;
    word-break: break-all;
    background: #ffffff;
    padding: 0.35rem 0.5rem;
    border: 1px solid #c8ccd0;
    border-radius: 3px;
    font-size: 0.85rem;
    color: #1f2329;
}

.share-panel-error[b-8nw994av26] {
    margin: 0.6rem 0 0;
    color: #a4262c;
    font-size: 0.9rem;
}

.share-panel-list[b-8nw994av26] {
    list-style: none;
    margin: 0.85rem 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}

.share-panel-row[b-8nw994av26] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.4rem 0.55rem;
    background: #f8f9fa;
    border: 1px solid #e2e4e7;
    border-radius: 4px;
    font-size: 0.85rem;
}

.share-panel-row-revoked[b-8nw994av26] {
    opacity: 0.55;
}

.share-panel-row-meta[b-8nw994av26] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    color: #1f2329;
}

.share-panel-row-id[b-8nw994av26] {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    color: #3a3f47;
}

.share-panel-row-badge[b-8nw994av26] {
    display: inline-block;
    padding: 0.05rem 0.4rem;
    border-radius: 999px;
    font-size: 0.7rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

.share-panel-row-badge-fresh[b-8nw994av26] {
    background: #e2efff;
    color: #2c5cb6;
}

.share-panel-row-badge-used[b-8nw994av26] {
    background: #f1f7e8;
    color: #4a6a1a;
}

.share-panel-row-badge-revoked[b-8nw994av26] {
    background: #f2dada;
    color: #a4262c;
}

.share-panel-row-created[b-8nw994av26] {
    color: #6a727b;
    font-size: 0.8rem;
}

.share-panel-row-revoke[b-8nw994av26] {
    padding: 0.25rem 0.6rem;
    background: #ffffff;
    color: #a4262c;
    border: 1px solid #d8b6b9;
    border-radius: 4px;
    font-size: 0.8rem;
    cursor: pointer;
}

.share-panel-row-revoke:hover[b-8nw994av26] {
    background: #fdf0f1;
}
/* /Components/Pages/Home.razor.rz.scp.css */
/* Home page styles. Component-scoped: Blazor rewrites class names with a per-component
   scope id, so these never leak into other pages. The active-games list is the Phase 5
   async-play surface — the most prominent visual element after the hero copy. */

/* Atmospheric home background. Mirrors the .login-hero pattern: position:fixed so
   the image escapes the .container's max-width, z-index 0 so site chrome (header
   /footer at z-index 2 per app.css) and the hero content (z-index 1 below) paint
   on top. Hidden on phones to keep the games list full-width.
   The background-color is the FALLBACK shown when home-hero.jpg is missing — the
   page still has a coherent dark atmosphere instead of bare white.

   Light gradient overlay: just enough to keep the hero copy readable in the
   upper-left where the image is naturally dark, while letting the warm candlelight
   on the right of the painting shine through at near-source intensity. The hero
   text carries its own text-shadow for additional contrast insurance. */
.home-hero[b-ctzjaut1b8] {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        linear-gradient(to bottom,
            rgba(8, 10, 16, 0.22) 0%,
            rgba(8, 10, 16, 0.32) 100%),
        url('/img/art/home-hero.jpg') center/cover no-repeat #1a1612;
}

@media (max-width: 800px) {
    .home-hero[b-ctzjaut1b8] {
        display: none;
    }
}

/* Hero text + games list sit above the fixed background. The hero readability
   gradient on .home-hero handles contrast against the parchment art; we additionally
   give the hero text a soft text-shadow so it stays legible if a generated image
   has unexpectedly bright spots. */
.hero[b-ctzjaut1b8] {
    position: relative;
    z-index: 1;
}

@media (min-width: 801px) {
    .hero h1[b-ctzjaut1b8],
    .hero .lead[b-ctzjaut1b8] {
        color: #f4ede0;
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
    }
    .hero .lead[b-ctzjaut1b8] {
        color: #d8d0c2;
    }
}

/* W/L/D pills row. Subtle, but the colour cues (greenish win / muted loss /
   neutral draw) carry tone without a screen-reader penalty — the text labels
   are the source of truth. */
.record[b-ctzjaut1b8] {
    display: flex;
    gap: 0.5rem;
    margin: 0.5rem 0 1.25rem;
}

.record-pill[b-ctzjaut1b8] {
    display: inline-block;
    padding: 0.2rem 0.7rem;
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    background: rgba(255, 255, 255, 0.08);
    border: 1px solid rgba(255, 255, 255, 0.15);
    color: #f0eee8;
}

.record-wins[b-ctzjaut1b8]   { background: rgba(70, 140, 90, 0.35); border-color: rgba(120, 200, 140, 0.55); }
.record-losses[b-ctzjaut1b8] { background: rgba(160, 70, 80, 0.28); border-color: rgba(220, 120, 130, 0.45); }
.record-draws[b-ctzjaut1b8]  { background: rgba(120, 120, 130, 0.3); border-color: rgba(180, 180, 190, 0.45); }

.active-games[b-ctzjaut1b8] {
    margin: 1.5rem 0 1rem;
    padding: 0;
}

.active-games h2[b-ctzjaut1b8] {
    font-size: 1.15rem;
    margin: 0 0 0.5rem;
    color: #1f2329;
}

.active-games .hint[b-ctzjaut1b8] {
    color: #555;
    font-size: 0.95rem;
    margin: 0.25rem 0 0;
}

/* Each row is an anchor styled like a flat button. Three columns:
   opponent name (flex-grow), turn pill (fixed width), last-activity (right-aligned). */
.active-game-row[b-ctzjaut1b8] {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.9rem;
    margin: 0.4rem 0;
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 4px;
    color: #1f2329;
    text-decoration: none;
    transition: background-color 0.12s ease, border-color 0.12s ease;
}

.active-game-row:hover[b-ctzjaut1b8] {
    background: #f1f4fa;
    border-color: #2c5cb6;
    text-decoration: none;
}

.active-game-row .opponent[b-ctzjaut1b8] {
    flex: 1 1 auto;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.active-game-row .turn[b-ctzjaut1b8] {
    flex: 0 0 auto;
    font-size: 0.85rem;
    font-weight: 600;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    text-transform: uppercase;
    letter-spacing: 0.03em;
}

/* "Your turn" pops — it's the call to action.
   "Their turn" is subdued so the eye lands on the rows that need attention. */
.active-game-row .turn.your-turn[b-ctzjaut1b8] {
    background: #2c5cb6;
    color: #ffffff;
}

.active-game-row .turn.their-turn[b-ctzjaut1b8] {
    background: #eaecef;
    color: #555;
}

.active-game-row .last-activity[b-ctzjaut1b8] {
    flex: 0 0 auto;
    color: #777;
    font-size: 0.85rem;
}

/* Per-row stat pills (ply count, capture count). Compact, neutral background so
   they sit between the turn pill and the timestamp without competing with either.
   The title attribute carries the long-form label for hover discoverability. */
.stat-pill[b-ctzjaut1b8] {
    flex: 0 0 auto;
    font-size: 0.78rem;
    font-weight: 500;
    color: #4b525c;
    background: #f1f3f5;
    border: 1px solid #d8dadd;
    border-radius: 999px;
    padding: 0.1rem 0.55rem;
    letter-spacing: 0.02em;
}

/* Recent finishes — terminal games. Layout matches active-game-row except the
   row is NOT a link (the game is done; the row exists for memory, not action).
   Subtler styling than active so the eye prioritises actionable rows above it. */
.recent-completed[b-ctzjaut1b8] {
    margin: 1.5rem 0 1rem;
}

.recent-completed h2[b-ctzjaut1b8] {
    font-size: 1.05rem;
    margin: 0 0 0.5rem;
    color: #1f2329;
}

.recent-row[b-ctzjaut1b8] {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 0.9rem;
    margin: 0.35rem 0;
    background: rgba(255, 255, 255, 0.92);
    border: 1px solid #e1e3e7;
    border-radius: 4px;
    color: #3a3f47;
}

.recent-row .opponent[b-ctzjaut1b8] {
    flex: 1 1 auto;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.recent-row .last-activity[b-ctzjaut1b8] {
    color: #777;
    font-size: 0.85rem;
}

/* Outcome pill: caller-relative result. Green for wins, muted red for losses,
   neutral grey for draws. Sits at the FRONT of the row so the eye reads outcome
   first ("how did this end?") before the rest of the metadata. */
.outcome[b-ctzjaut1b8] {
    flex: 0 0 auto;
    font-size: 0.78rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.15rem 0.6rem;
    border-radius: 999px;
}

.outcome-won[b-ctzjaut1b8]  { background: #d8efe0; color: #1f5a3b; border: 1px solid #a8d8b8; }
.outcome-lost[b-ctzjaut1b8] { background: #f4d6d8; color: #761c25; border: 1px solid #e0a8ad; }
.outcome-drew[b-ctzjaut1b8] { background: #e6e8eb; color: #3a3f47; border: 1px solid #c8ccd0; }

/* ============================== Async game invitations ============================== */

/* Loud incoming-invitation cards. Yellow-ish wash so the eye lands here first
   on a fresh sign-in; the recipient should never miss that a friend is
   waiting on them. */
.pending-invitations[b-ctzjaut1b8] {
    margin-bottom: 1.5rem;
}

.pending-invitations h2[b-ctzjaut1b8] {
    margin: 0 0 0.5rem;
    font-size: 1.15rem;
    color: #5a4632;
}

.invitation-card[b-ctzjaut1b8] {
    background: #fffbe6;
    border: 1px solid #f3e0a3;
    border-left: 4px solid #d4a72c;
    border-radius: 4px;
    padding: 0.75rem 1rem;
    margin-bottom: 0.5rem;
}

.invitation-text[b-ctzjaut1b8] {
    margin: 0 0 0.3rem;
}

.invitation-meta[b-ctzjaut1b8] {
    margin: 0 0 0.6rem;
    font-size: 0.85rem;
    color: #6a6f78;
}

.invitation-note[b-ctzjaut1b8] {
    color: #3a3f47;
    font-style: italic;
}

.invitation-actions[b-ctzjaut1b8] {
    display: flex;
    gap: 0.5rem;
}

.btn-accept[b-ctzjaut1b8],
.btn-decline[b-ctzjaut1b8] {
    padding: 0.45rem 1rem;
    border-radius: 3px;
    font-weight: 600;
    font-size: 0.92rem;
    cursor: pointer;
    border: 1px solid transparent;
    font-family: inherit;
}

.btn-accept[b-ctzjaut1b8] {
    background: #2c5cb6;
    color: #fff;
}

.btn-accept:hover:not(:disabled)[b-ctzjaut1b8] {
    background: #244c98;
}

.btn-accept:disabled[b-ctzjaut1b8] {
    opacity: 0.55;
    cursor: not-allowed;
}

.btn-decline[b-ctzjaut1b8] {
    background: #fff;
    color: #6a6f78;
    border-color: #c8ccd0;
}

.btn-decline:hover:not(:disabled)[b-ctzjaut1b8] {
    background: #f1f4fa;
}

/* Outgoing list — flatter, less attention-grabbing. */
.outgoing-invitations[b-ctzjaut1b8] {
    margin: 1rem 0;
}

.outgoing-invitations h2[b-ctzjaut1b8] {
    margin: 0 0 0.4rem;
    font-size: 1rem;
    color: #3a3f47;
}

.outgoing-row[b-ctzjaut1b8] {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    padding: 0.45rem 0.65rem;
    background: #f6f7f9;
    border: 1px solid #e1e3e6;
    border-radius: 3px;
    margin-bottom: 0.35rem;
    font-size: 0.92rem;
}

.outgoing-row .opponent[b-ctzjaut1b8] {
    font-weight: 600;
}

.outgoing-row .color-tag[b-ctzjaut1b8] {
    margin-left: 0.4rem;
    font-size: 0.78rem;
    color: #6a6f78;
    font-weight: 400;
}

.outgoing-row .last-activity[b-ctzjaut1b8] {
    color: #6a6f78;
    font-size: 0.82rem;
    margin-left: auto;
}

.btn-cancel-small[b-ctzjaut1b8] {
    padding: 0.25rem 0.65rem;
    background: #fff;
    color: #761c25;
    border: 1px solid #e0a8ad;
    border-radius: 3px;
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    font-family: inherit;
}

.btn-cancel-small:hover:not(:disabled)[b-ctzjaut1b8] {
    background: #fdf3f4;
}

/* Inline challenge form, toggled from the CTA row. */
.challenge-form[b-ctzjaut1b8] {
    margin-top: 0.75rem;
    padding: 0.9rem 1rem;
    background: #f6f7f9;
    border: 1px solid #d8dadd;
    border-radius: 4px;
}

.challenge-form .field[b-ctzjaut1b8] {
    display: block;
    margin-bottom: 0.6rem;
}

.challenge-form .field-label[b-ctzjaut1b8] {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: #3a3f47;
    margin-bottom: 0.2rem;
}

.challenge-form .field-input[b-ctzjaut1b8] {
    width: 100%;
    padding: 0.4rem 0.55rem;
    font-size: 0.95rem;
    border: 1px solid #c5c8cd;
    border-radius: 3px;
    font-family: inherit;
}

.challenge-form .color-fieldset[b-ctzjaut1b8] {
    border: none;
    padding: 0;
    margin: 0 0 0.6rem;
}

.challenge-form .color-fieldset legend[b-ctzjaut1b8] {
    padding: 0;
    margin-bottom: 0.2rem;
}

.color-radio[b-ctzjaut1b8] {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-right: 0.9rem;
    font-size: 0.92rem;
}

.challenge-form .form-actions[b-ctzjaut1b8] {
    display: flex;
    gap: 0.5rem;
}

.notice[b-ctzjaut1b8] {
    margin-top: 0.6rem;
    padding: 0.55rem 0.8rem;
    border-radius: 4px;
    font-size: 0.95rem;
}

.notice-error[b-ctzjaut1b8] {
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
}

.notice-info[b-ctzjaut1b8] {
    background: #e8f0fd;
    border: 1px solid #a8c0f5;
    color: #1f3a8a;
}

/* CTA row: primary action ("Start a new game") plus secondary ("Open Lobby").
   Distinct colours so the eye knows which is the recommended path. */
.cta-row[b-ctzjaut1b8] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    margin-top: 1rem;
}

.btn-primary[b-ctzjaut1b8],
.btn-secondary[b-ctzjaut1b8] {
    display: inline-block;
    padding: 0.6rem 1.25rem;
    border-radius: 4px;
    font-weight: 600;
    text-decoration: none;
}

.btn-primary[b-ctzjaut1b8] {
    background: #2c5cb6;
    color: #ffffff;
}

.btn-primary:hover[b-ctzjaut1b8] {
    background: #244c98;
    text-decoration: none;
}

.btn-secondary[b-ctzjaut1b8] {
    background: #ffffff;
    color: #2c5cb6;
    border: 1px solid #2c5cb6;
}

.btn-secondary:hover[b-ctzjaut1b8] {
    background: #f1f4fa;
    text-decoration: none;
}

/* Phone layout: stack the row vertically so each piece of info has room. The turn pill
   sits to the right of the opponent name; the timestamp drops to its own line. Stat
   pills (ply / captures) wrap to a third line where needed. */
@media (max-width: 480px) {
    .active-game-row[b-ctzjaut1b8],
    .recent-row[b-ctzjaut1b8] {
        flex-wrap: wrap;
        padding: 0.55rem 0.75rem;
    }

    .active-game-row .last-activity[b-ctzjaut1b8],
    .recent-row .last-activity[b-ctzjaut1b8] {
        flex-basis: 100%;
        margin-top: 0.15rem;
    }

    .cta-row[b-ctzjaut1b8] {
        flex-direction: column;
        align-items: stretch;
    }

    .btn-primary[b-ctzjaut1b8],
    .btn-secondary[b-ctzjaut1b8] {
        text-align: center;
    }
}

/* Section headings + inline hints sitting directly on the dark home-hero
   background (no card behind them). On desktop the default near-black /
   dark-gray colors disappear against the parchment art — lift to the same
   cream-on-dark palette the hero h1 uses so the upper section reads as
   one cohesive overlay.

   This block sits at the END of the file deliberately: it overrides the
   default rules (.active-games h2, etc.) which appear EARLIER in source
   order. Putting these rules inside the existing 801px @media block near
   the top of the file left them with equal specificity to the defaults
   that follow, so the cascade picked the defaults and the headings stayed
   dark — fixed by moving the override here.

   Card-backgrounded elements (.active-game-row, .recent-row,
   .invitation-card, .outgoing-row) keep their dark text — their cards
   provide their own light surfaces so contrast is fine there. */
@media (min-width: 801px) {
    .active-games h2[b-ctzjaut1b8],
    .recent-completed h2[b-ctzjaut1b8],
    .pending-invitations h2[b-ctzjaut1b8],
    .outgoing-invitations h2[b-ctzjaut1b8] {
        color: #f4ede0;
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
    }

    .active-games .hint[b-ctzjaut1b8] {
        color: #d8d0c2;
        text-shadow: 0 1px 2px rgba(0, 0, 0, 0.55);
    }
}
/* /Components/Pages/Lobby.razor.rz.scp.css */
/* Lobby page layout. Component-scoped: Blazor rewrites class names with a per-component
   scope id, so these styles never leak into Game / Home / ChessBoard. */

/* Atmospheric flanks — same fog/encampment art used on the game page.
   Hidden below 1200px viewport so the lobby roster keeps its full width
   on narrower screens. pointer-events:none keeps clicks falling through
   to the lobby UI; z-index 0 + position:relative+z-index:1 on the lobby
   content ensures the section is never visually occluded. */
.lobby-flank[b-hczxoofbaq] {
    position: fixed;
    top: 0;
    bottom: 0;
    width: 22vw;
    background-size: cover;
    background-repeat: no-repeat;
    z-index: 0;
    pointer-events: none;
    display: none;
}

.lobby-flank-left[b-hczxoofbaq] {
    left: 0;
    background-image: url('/img/art/flank-left.jpg');
    background-position: right center;
}

.lobby-flank-right[b-hczxoofbaq] {
    right: 0;
    background-image: url('/img/art/flank-right.jpg');
    background-position: left center;
}

@media (min-width: 1200px) {
    .lobby-flank[b-hczxoofbaq] {
        display: block;
    }
}

.lobby[b-hczxoofbaq] {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    margin: 0 auto;
    max-width: 720px;
    /* Lift above the fixed flanks so the lobby content paints on top. */
    position: relative;
    z-index: 1;
}

.lobby-header h1[b-hczxoofbaq] {
    margin: 0 0 0.25rem;
    font-size: 1.6rem;
}

.lobby-header .lead[b-hczxoofbaq] {
    margin: 0;
    color: #3a3f47;
}

/* Invitation sections: highlight with a subtle tinted background so the user's eye
   pulls toward an actionable item without the page screaming for attention. */
.invitations[b-hczxoofbaq] {
    background: #fff8e1;
    border: 1px solid #f0d97a;
    border-radius: 6px;
    padding: 0.75rem 1rem;
}

.invitations.outgoing[b-hczxoofbaq] {
    background: #f1f3f5;
    border-color: #d8dadd;
}

.invitations h2[b-hczxoofbaq] {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
    color: #1f2329;
}

.invitation-card[b-hczxoofbaq] {
    background: #fff;
    border: 1px solid #e2c96a;
    border-radius: 4px;
    padding: 0.6rem 0.8rem;
    margin-bottom: 0.5rem;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
}

.invitation-card:last-child[b-hczxoofbaq] {
    margin-bottom: 0;
}

.invitation-card.outgoing[b-hczxoofbaq] {
    border-color: #c8ccd0;
    background: #ffffff;
    opacity: 0.85;
}

.invitation-text[b-hczxoofbaq] {
    margin: 0;
    flex: 1 1 16rem;
    color: #1f2329;
}

.invitation-actions[b-hczxoofbaq] {
    display: flex;
    gap: 0.5rem;
}

/* Lobby roster. Each user is a single row with their name on the left and the
   invite controls (radios + button) on the right. The row collapses to a stack on
   narrow viewports — see the mobile media query at the bottom of this file. */
.lobby-users[b-hczxoofbaq] {
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    padding: 0.75rem 1rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.lobby-users h2[b-hczxoofbaq] {
    margin: 0 0 0.5rem;
    font-size: 1.05rem;
    color: #1f2329;
}

.user-row[b-hczxoofbaq] {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.55rem 0;
    border-bottom: 1px solid #eef0f3;
}

.user-row:last-child[b-hczxoofbaq] {
    border-bottom: none;
}

.user-info[b-hczxoofbaq] {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 12rem;
}

.display-name[b-hczxoofbaq] {
    font-weight: 600;
    color: #1f2329;
}

.joined-at[b-hczxoofbaq] {
    font-size: 0.85rem;
    color: #6a6f78;
}

/* Invite controls live in a <fieldset> so a sent invitation can be disabled wholesale
   via the disabled attribute. Strip the default fieldset chrome — we want it to look
   like a plain horizontal cluster. */
.invite-controls[b-hczxoofbaq] {
    border: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem;
}

.invite-controls[disabled] .invite-radio[b-hczxoofbaq],
.invite-controls[disabled] .btn-invite[b-hczxoofbaq] {
    opacity: 0.6;
    cursor: not-allowed;
}

.invite-radio[b-hczxoofbaq] {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.9rem;
    color: #1f2329;
    cursor: pointer;
}

.invite-radio input[type="radio"][b-hczxoofbaq] {
    margin: 0;
}

/* Button palette. .btn-invite is the primary action on a row; .btn-accept and
   .btn-decline are the invitation-card actions. Colors mirror app.css's .cta /
   secondary palette for visual continuity. */
.btn-invite[b-hczxoofbaq],
.btn-accept[b-hczxoofbaq] {
    appearance: none;
    border: none;
    border-radius: 4px;
    padding: 0.45rem 0.9rem;
    background: #2c5cb6;
    color: #fff;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.9rem;
}

.btn-invite:hover:not(:disabled)[b-hczxoofbaq],
.btn-accept:hover:not(:disabled)[b-hczxoofbaq] {
    background: #244c98;
}

.btn-invite:disabled[b-hczxoofbaq] {
    background: #8aa2cf;
    cursor: not-allowed;
}

.btn-decline[b-hczxoofbaq] {
    appearance: none;
    border: 1px solid #c8ccd0;
    border-radius: 4px;
    padding: 0.45rem 0.9rem;
    background: #ffffff;
    color: #1f2329;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.9rem;
}

.btn-decline:hover:not(:disabled)[b-hczxoofbaq] {
    background: #f1f3f5;
}

/* "No other players in lobby" hint. Quiet, italic, just enough to signal the empty
   state without looking broken. */
.hint[b-hczxoofbaq] {
    margin: 0;
    color: #6a6f78;
    font-style: italic;
}

/* Footer with the "back to home" link. Single link for v1; Phase 6 may add a sign-out
   shortcut here. */
.lobby-actions[b-hczxoofbaq] {
    display: flex;
    justify-content: center;
    padding: 0.25rem 0 0.5rem;
}

.lobby-link[b-hczxoofbaq] {
    font-size: 0.95rem;
    color: #2c5cb6;
}

/* Notice banners. The same .notice / .notice-error / .notice-info classes are used by
   Login and NewGame; redeclaring them here keeps the lobby usable even if the global
   stylesheet does not (yet) define them. Component-scoped means these only apply to
   notices rendered by THIS page. */
.notice[b-hczxoofbaq] {
    margin: 0;
    padding: 0.5rem 0.75rem;
    border-radius: 4px;
    font-size: 0.95rem;
}

.notice-error[b-hczxoofbaq] {
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
}

.notice-info[b-hczxoofbaq] {
    background: #e8f0fd;
    border: 1px solid #a8c0f5;
    color: #1f3a8a;
}

/* Accessibility helper: hide visually but keep available to screen readers. */
.visually-hidden[b-hczxoofbaq] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Mobile layout. The 360px target width hits this rule; we stack the user-row's two
   halves vertically and let the radio cluster wrap naturally inside its fieldset. */
@media (max-width: 480px) {
    .lobby[b-hczxoofbaq] {
        gap: 1rem;
    }

    .user-row[b-hczxoofbaq] {
        flex-direction: column;
        align-items: stretch;
        gap: 0.5rem;
    }

    .user-info[b-hczxoofbaq] {
        min-width: 0;
    }

    .invite-controls[b-hczxoofbaq] {
        justify-content: flex-start;
    }

    .invitation-card[b-hczxoofbaq] {
        flex-direction: column;
        align-items: stretch;
    }

    .invitation-actions[b-hczxoofbaq] {
        justify-content: flex-start;
    }
}
/* /Components/Pages/Login.razor.rz.scp.css */
/* Login page — full-viewport hero background with a translucent dark card
   floated to the left where the artwork leaves negative space.

   The hero lives as a position:fixed element so it escapes the layout's
   .container (max-width 960px) and covers the viewport edge-to-edge. The site
   header (dark) and footer (light) still paint above it; the form card sits
   in normal flow inside .container, lifted via z-index. */

.login-hero[b-cq0pkfag6g] {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        linear-gradient(to right,
            rgba(8, 10, 16, 0.92) 0%,
            rgba(8, 10, 16, 0.65) 25%,
            rgba(8, 10, 16, 0.20) 50%,
            transparent 70%),
        url('/img/art/login-hero.jpg') center/cover no-repeat #0c0c10;
}

section.login[b-cq0pkfag6g] {
    /* Sit above the fixed hero in stacking order; default static blocks would
       paint below a positioned fixed sibling without this lift. */
    position: relative;
    z-index: 1;

    /* Fill the vertical space the layout gives us (.site-main is min-height
       100vh - 7rem). Vertical-center the card; align it left so the knight
       sits unobscured on the right of the hero. */
    min-height: calc(100vh - 7rem);
    display: flex;
    align-items: center;
}

.login-card[b-cq0pkfag6g] {
    max-width: 28rem;
    width: 100%;
    padding: 2rem 2rem 1.75rem;
    background: rgba(18, 20, 26, 0.78);
    /* Soft frosted-glass feel so the hero's tones bleed through subtly. */
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 6px;
    color: #f0eee8;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
}

.login-card h1[b-cq0pkfag6g] {
    margin: 0 0 0.75rem;
    color: #f7f4ec;
    font-weight: 600;
}

.login-card .lead[b-cq0pkfag6g] {
    color: #c8c4bb;
    margin: 0 0 1.25rem;
    font-size: 0.95rem;
    line-height: 1.55;
}

.login-card .field[b-cq0pkfag6g] {
    display: block;
    margin: 0 0 1rem;
}

.login-card .field-label[b-cq0pkfag6g] {
    display: block;
    color: #c8c4bb;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    margin-bottom: 0.4rem;
}

.login-card .field-input[b-cq0pkfag6g] {
    width: 100%;
    padding: 0.7rem 0.9rem;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 4px;
    color: #f0eee8;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.95rem;
    transition: border-color 150ms ease, background 150ms ease;
}

.login-card .field-input:focus[b-cq0pkfag6g] {
    outline: none;
    border-color: #6a8ed0;
    background: rgba(255, 255, 255, 0.1);
}

.login-card .cta[b-cq0pkfag6g] {
    display: block;
    width: 100%;
    margin: 0.75rem 0 0;
    padding: 0.75rem 1rem;
    background: #6a8ed0;
    color: #0f1320;
    border: none;
    border-radius: 4px;
    font-weight: 700;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background 150ms ease;
}

.login-card .cta:hover[b-cq0pkfag6g] {
    background: #88a6e0;
    text-decoration: none;
}

.login-card .notice[b-cq0pkfag6g] {
    padding: 0.6rem 0.8rem;
    margin: 0 0 1rem;
    border-radius: 4px;
    font-size: 0.9rem;
}

.login-card .notice-info[b-cq0pkfag6g] {
    background: rgba(80, 130, 200, 0.18);
    border: 1px solid rgba(120, 170, 230, 0.35);
    color: #cfdef6;
}

.login-card .notice-error[b-cq0pkfag6g] {
    background: rgba(200, 70, 80, 0.18);
    border: 1px solid rgba(230, 110, 120, 0.45);
    color: #f6cfd3;
}

/* Narrow viewports: shift the hero focal point further right so the knight
   stays visible behind the now-full-width card, and reduce the gradient
   coverage so the right side of the image is still readable. */
@media (max-width: 640px) {
    section.login[b-cq0pkfag6g] {
        align-items: flex-start;
        padding-top: 2rem;
    }
    .login-card[b-cq0pkfag6g] {
        max-width: 100%;
    }
    .login-hero[b-cq0pkfag6g] {
        background-position: 75% center;
    }
}
/* /Components/Pages/Observe.razor.rz.scp.css */
/* Observe scoped styles. Issue #15 Phase 4 — public spectator page reached via
   share link, intentionally mirroring AdminObserve.razor's visual language so a
   participant who is also admin sees the same chrome on both pages. Component-
   scoped: Blazor rewrites class names with a per-component scope id, so we have
   to repeat the rules rather than reference AdminObserve.razor.css's. */

.observe-layout[b-1uugjgdp17] {
    max-width: 72rem;
    margin: 1rem auto;
    padding: 0 1rem 2rem;
    display: flex;
    flex-direction: column;
    align-items: stretch;
}

.observe-header[b-1uugjgdp17] {
    margin: 0 0 1rem;
}

.observe-header h1[b-1uugjgdp17] {
    margin: 0 0 0.25rem;
    font-size: 1.4rem;
}

.observe-subline[b-1uugjgdp17] {
    margin: 0 0 0.85rem;
    color: #3a3f47;
    font-size: 0.9rem;
}

/* POV switcher — same pill chrome as AdminObserve. */
.observe-pov[b-1uugjgdp17] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin: 0 0 0.75rem;
}

.observe-pov-pill[b-1uugjgdp17] {
    display: inline-block;
    padding: 0.4rem 0.95rem;
    background: #ffffff;
    color: #1f2329;
    border: 1px solid #c8ccd0;
    border-radius: 999px;
    font-size: 0.9rem;
    text-decoration: none;
    line-height: 1.2;
    cursor: pointer;
}

.observe-pov-pill:hover[b-1uugjgdp17] {
    background: #f1f3f5;
}

.observe-pov-pill-active[b-1uugjgdp17],
.observe-pov-pill-active:hover[b-1uugjgdp17] {
    background: #2c5cb6;
    color: #ffffff;
    border-color: #2c5cb6;
    cursor: default;
}

.observe-last-move[b-1uugjgdp17] {
    margin: 0.5rem 0 0;
    font-size: 0.95rem;
    color: #1f2329;
}

.observe-last-move code[b-1uugjgdp17] {
    background: #f1f3f5;
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    font-size: 0.9rem;
}

.observe-fogged[b-1uugjgdp17] {
    color: #6a727b;
    font-style: italic;
}

.observe-board-with-belief[b-1uugjgdp17] {
    position: relative;
    max-width: min(80vmin, 600px);
    margin: 0 auto;
    width: 100%;
}

.observe-footer[b-1uugjgdp17] {
    margin-top: 1.25rem;
    text-align: center;
}

.observe-footnote[b-1uugjgdp17] {
    color: #6a727b;
    font-size: 0.85rem;
    margin: 0;
}

.observe-error[b-1uugjgdp17] {
    max-width: 32rem;
    margin: 2rem auto;
    padding: 1.25rem 1.5rem 1.5rem;
    background: #ffffff;
    border: 1px solid #d8dadd;
    border-radius: 6px;
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
}

.observe-error h1[b-1uugjgdp17] {
    margin: 0 0 0.5rem;
    font-size: 1.3rem;
}

.observe-error p[b-1uugjgdp17] {
    margin: 0 0 0.5rem;
    color: #3a3f47;
}

.observe-belief-unavailable[b-1uugjgdp17] {
    max-width: 36rem;
    margin: 1.5rem auto;
    padding: 1rem 1.25rem;
    background: #fff8e1;
    border: 1px solid #f0c244;
    border-radius: 6px;
    color: #3a3f47;
}

.observe-belief-unavailable p[b-1uugjgdp17] {
    margin: 0 0 0.5rem;
}

.observe-belief-unavailable a[b-1uugjgdp17] {
    color: #2c5cb6;
}
/* /Components/Pages/Welcome.razor.rz.scp.css */
.welcome[b-5ubuslhaed] {
    max-width: 38rem;
    margin: 1rem auto 0;
}

.welcome h1[b-5ubuslhaed] {
    font-size: 1.75rem;
    margin: 0 0 1rem;
}

.welcome .lead[b-5ubuslhaed] {
    font-size: 1.1rem;
    color: #3a3f47;
}

.welcome-fallback[b-5ubuslhaed] {
    text-align: center;
    padding: 3rem 1rem;
}

.welcome-bookmark[b-5ubuslhaed] {
    margin: 1.5rem 0 1.5rem;
    padding: 1.25rem;
    background: #fff;
    border: 1px solid #d8dadd;
    border-left: 4px solid #2c5cb6;
    border-radius: 4px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}

.welcome-bookmark-label[b-5ubuslhaed] {
    display: block;
    font-size: 0.85rem;
    font-weight: 600;
    color: #4a5160;
    margin-bottom: 0.4rem;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

.welcome-bookmark-row[b-5ubuslhaed] {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
}

.welcome-bookmark-input[b-5ubuslhaed] {
    flex: 1 1 auto;
    min-width: 0;
    padding: 0.5rem 0.75rem;
    font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
    font-size: 0.85rem;
    color: #1f2329;
    background: #f6f7f9;
    border: 1px solid #c5c8cd;
    border-radius: 3px;
}

.welcome-bookmark-input:focus[b-5ubuslhaed] {
    outline: 2px solid #2c5cb6;
    outline-offset: 0;
    border-color: #2c5cb6;
}

.welcome-qr-row[b-5ubuslhaed] {
    display: flex;
    gap: 1rem;
    align-items: center;
    margin: 1rem 0 0;
    padding: 0.75rem;
    background: #f6f7f9;
    border-radius: 3px;
    border: 1px solid #e1e3e6;
}

.welcome-qr[b-5ubuslhaed] {
    flex: 0 0 auto;
    image-rendering: pixelated;
    background: #fff;
    padding: 6px;
    border-radius: 3px;
    border: 1px solid #d8dadd;
}

.welcome-qr-instructions[b-5ubuslhaed] {
    flex: 1 1 auto;
    font-size: 0.9rem;
    color: #3a3f47;
}

.welcome-qr-instructions p[b-5ubuslhaed] {
    margin: 0;
}

.welcome-tip[b-5ubuslhaed] {
    margin: 0.75rem 0 0;
    font-size: 0.95rem;
    color: #3a3f47;
}

.welcome-warning[b-5ubuslhaed] {
    margin: 0.5rem 0 0;
    font-size: 0.9rem;
    color: #7a4030;
    background: #fdf3e9;
    border: 1px solid #f0c19a;
    padding: 0.55rem 0.75rem;
    border-radius: 3px;
}

/* Local copies of btn-primary / btn-secondary. The classes are defined per-page
   elsewhere (Home.razor.css, Admin.razor.css) — we keep our own copies here so
   the welcome page renders correctly even if a future refactor unifies button
   styles into a global stylesheet. */
.btn-primary[b-5ubuslhaed] {
    display: inline-block;
    padding: 0.55rem 1.15rem;
    background: #2c5cb6;
    color: #fff;
    border: 1px solid #2c5cb6;
    border-radius: 3px;
    font-weight: 600;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background 120ms ease;
}

.btn-primary:hover[b-5ubuslhaed] {
    background: #244c98;
    text-decoration: none;
}

.btn-secondary[b-5ubuslhaed] {
    display: inline-block;
    padding: 0.5rem 1rem;
    background: #fff;
    color: #2c5cb6;
    border: 1px solid #2c5cb6;
    border-radius: 3px;
    font-weight: 600;
    font-size: 0.9rem;
    cursor: pointer;
    font-family: inherit;
    transition: background 120ms ease;
    white-space: nowrap;
}

.btn-secondary:hover[b-5ubuslhaed] {
    background: #eef2fa;
}

.cta-row[b-5ubuslhaed] {
    margin-top: 1.25rem;
}

@media (max-width: 520px) {
    .welcome-bookmark-row[b-5ubuslhaed] {
        flex-direction: column;
    }

    .welcome-bookmark-input[b-5ubuslhaed] {
        font-size: 0.78rem;
    }

    .btn-secondary[b-5ubuslhaed] {
        align-self: flex-end;
    }

    .welcome-qr-row[b-5ubuslhaed] {
        flex-direction: column;
        text-align: center;
    }
}
/* /Components/ReportBugButton.razor.rz.scp.css */
/* Report-bug affordance (issue #2). Small floating button on the Game.razor page
   plus the modal it opens. Component-scoped so the global .cta / .btn-* styles
   don't compete. */

.report-bug-btn[b-fvnflhmwq9] {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 10;
    background: rgba(40, 44, 54, 0.85);
    color: #f0eee8;
    border: 1px solid rgba(255, 255, 255, 0.15);
    border-radius: 999px;
    padding: 0.45rem 1rem;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.25);
    transition: background 120ms ease, transform 120ms ease;
}

.report-bug-btn:hover[b-fvnflhmwq9] {
    background: rgba(60, 66, 80, 0.95);
    transform: translateY(-1px);
}

/* Overlay backing the modal. Sits below the modal, above the page. Click to dismiss. */
.report-bug-overlay[b-fvnflhmwq9] {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.45);
    z-index: 100;
    cursor: pointer;
}

.report-bug-modal[b-fvnflhmwq9] {
    position: fixed;
    z-index: 101;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(90vw, 28rem);
    background: #ffffff;
    border-radius: 6px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
    padding: 1.25rem 1.5rem 1rem;
    color: #1f2329;
}

.report-bug-modal h2[b-fvnflhmwq9] {
    margin: 0 0 0.5rem;
    font-size: 1.15rem;
}

.report-bug-help[b-fvnflhmwq9] {
    margin: 0 0 0.85rem;
    font-size: 0.9rem;
    color: #4b525c;
    line-height: 1.45;
}

.report-bug-label[b-fvnflhmwq9] {
    display: block;
    font-size: 0.78rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #4b525c;
    margin-bottom: 0.3rem;
}

.report-bug-textarea[b-fvnflhmwq9] {
    width: 100%;
    padding: 0.5rem 0.65rem;
    border: 1px solid #c8ccd0;
    border-radius: 4px;
    font-family: inherit;
    font-size: 0.92rem;
    resize: vertical;
    min-height: 4.5rem;
}

.report-bug-textarea:focus[b-fvnflhmwq9] {
    outline: none;
    border-color: #2c5cb6;
    box-shadow: 0 0 0 2px rgba(44, 92, 182, 0.18);
}

.report-bug-error[b-fvnflhmwq9] {
    margin: 0.7rem 0 0;
    padding: 0.5rem 0.7rem;
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
    border-radius: 4px;
    font-size: 0.85rem;
}

.report-bug-actions[b-fvnflhmwq9] {
    display: flex;
    gap: 0.6rem;
    justify-content: flex-end;
    margin-top: 1rem;
}

.report-bug-cancel[b-fvnflhmwq9],
.report-bug-confirm[b-fvnflhmwq9] {
    padding: 0.45rem 1rem;
    border-radius: 4px;
    font-weight: 600;
    font-size: 0.9rem;
    cursor: pointer;
    border: 1px solid transparent;
    font-family: inherit;
}

.report-bug-cancel[b-fvnflhmwq9] {
    background: #ffffff;
    color: #4b525c;
    border-color: #c8ccd0;
}

.report-bug-cancel:hover:not(:disabled)[b-fvnflhmwq9] {
    background: #f1f3f5;
}

.report-bug-confirm[b-fvnflhmwq9] {
    background: #2c5cb6;
    color: #ffffff;
}

.report-bug-confirm:hover:not(:disabled)[b-fvnflhmwq9] {
    background: #244c98;
}

.report-bug-confirm:disabled[b-fvnflhmwq9],
.report-bug-cancel:disabled[b-fvnflhmwq9] {
    opacity: 0.6;
    cursor: not-allowed;
}
/* /Components/Toast.razor.rz.scp.css */
/* Toast - fixed-position transient notification card.

   Position: bottom-center on desktop, slightly raised so it doesn't crash into
   the viewport edge on mobile virtual keyboards. The card is high-contrast and
   shadowed so it floats above the board / status header without ambiguity. */

.toast[b-i0vdmamqan] {
    position: fixed;
    left: 50%;
    bottom: 1.5rem;
    transform: translateX(-50%);
    min-width: 16rem;
    max-width: 90vw;
    padding: 0.65rem 1.1rem;
    border-radius: 6px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.22);
    cursor: pointer;
    z-index: 1100;
    /* Subtle entrance: fade + slide up. animation runs once on mount so a new
       toast appearing after one dismissed feels alive without being noisy. */
    animation: toast-in-b-i0vdmamqan 160ms ease-out;
    user-select: none;
    font-size: 0.95rem;
    line-height: 1.3;
}

.toast-message[b-i0vdmamqan] {
    /* Block-level so long messages wrap naturally without breaking the card. */
    display: block;
}

/* Variants. Colors track the existing palette in Game.razor.css for visual
   consistency: error mirrors .game-status-error, info uses the cool blue from
   .game-link, warning sits between the two. */
.toast.toast-error[b-i0vdmamqan] {
    background: #fde8e8;
    border: 1px solid #f5a8a8;
    color: #8a1f1f;
}

.toast.toast-warning[b-i0vdmamqan] {
    background: #fff5d6;
    border: 1px solid #f0d27e;
    color: #6a4b00;
}

.toast.toast-info[b-i0vdmamqan] {
    background: #e8f0fb;
    border: 1px solid #a8c0e6;
    color: #1f3c7a;
}

@keyframes toast-in-b-i0vdmamqan {
    from {
        opacity: 0;
        transform: translate(-50%, 8px);
    }
    to {
        opacity: 1;
        transform: translate(-50%, 0);
    }
}

@media (max-width: 480px) {
    .toast[b-i0vdmamqan] {
        bottom: 1rem;
        min-width: 0;
        width: calc(100% - 2rem);
        font-size: 0.9rem;
        padding: 0.55rem 0.9rem;
    }
}
