/* CrowdGames portal shell (tasks 11/12/13). Dark theme matching the games themselves (see
   games/penguin/lines/line-1/index.html's palette) - this is the public-facing product page, not
   /admin, so it gets real visual treatment rather than the bare-bones scaffold styling below. */

:root {
    --bg: #0b0714;
    --bg-panel: #14101f;
    --bg-raised: #1c1630;
    --border: #3a2d58;
    --border-soft: #241b3a;
    --text: #e8e2ff;
    --text-dim: #a99fd0;
    --text-faint: #7c72a5;
    --accent: #8f5bff;
    --accent-strong: #7b4dff;
    --up: #5cc8ff;
    --down: #ff6b81;
    --header-h: 3.25rem;
    --tabbar-h: 3rem;
}

* {
    box-sizing: border-box;
}

html, body {
    height: 100%;
}

body {
    font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
    margin: 0;
    padding: 0;
    color: var(--text);
    background: var(--bg);
}

a {
    color: var(--up);
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ---- Legacy scaffold classes (kept for /admin's plain-page chrome and any page besides Home) ---- */

.site-header {
    padding: 1rem 1.5rem;
    border-bottom: 1px solid var(--border);
}

.logo-link {
    font-weight: 700;
    font-size: 1.25rem;
    color: var(--text);
    text-decoration: none;
}

.site-main {
    max-width: 720px;
    margin: 0 auto;
    padding: 2rem 1.5rem;
}

.site-footer {
    max-width: 720px;
    margin: 0 auto;
    padding: 1rem 1.5rem 2rem;
    border-top: 1px solid var(--border);
    color: var(--text-dim);
    font-size: 0.8rem;
}

/* ==================================================================================================
   Portal shell (task 11)
   ================================================================================================== */

.portal {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

.portal.empty-state {
    align-items: center;
    justify-content: center;
    padding: 3rem 1.5rem;
    text-align: center;
    color: var(--text-dim);
}

.portal-header {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5rem 1rem;
    min-height: var(--header-h);
    border-bottom: 1px solid var(--border);
    background: var(--bg-panel);
}

.portal-header .logo-link {
    font-size: 1.1rem;
    margin-right: 0.25rem;
}

.selector-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
}

select,
textarea,
button {
    font-family: inherit;
}

select {
    background: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.3rem 0.5rem;
    font-size: 0.85rem;
    max-width: 12rem;
}

.header-status {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-left: auto;
    font-size: 0.8rem;
}

.epoch-badge {
    color: var(--text-faint);
    border: 1px dashed var(--border);
    border-radius: 999px;
    padding: 0.2rem 0.6rem;
    cursor: help;
}

.epoch-badge.cap-reached {
    color: #ffd9a0;
    border: 1px solid #8a5a1f;
    background: #3a2210;
    cursor: help;
}

.build-status {
    border-radius: 999px;
    padding: 0.2rem 0.6rem;
    color: var(--text-dim);
    background: var(--bg-raised);
}

.build-status.is-running {
    color: #0b0714;
    background: var(--up);
    font-weight: 600;
}

/* Task 21's public content policy link - the portal shell has no chrome of its own outside Home.razor
   (see PortalLayout.razor's own remarks), so this sits at the bottom of the page body rather than in a
   layout-level footer. */
.portal-footer {
    padding: 0.75rem 1rem 1.25rem;
    text-align: center;
    font-size: 0.75rem;
    color: var(--text-faint);
    border-top: 1px solid var(--border-soft);
}

.portal-footer a {
    color: inherit;
}

.privacy-note {
    margin: 0;
    padding: 0.3rem 1rem;
    font-size: 0.7rem;
    color: var(--text-faint);
    background: var(--bg-panel);
    border-bottom: 1px solid var(--border-soft);
}

.version-banner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1rem;
    background: #3a2210;
    border-bottom: 1px solid #8a5a1f;
    color: #ffd9a0;
    font-size: 0.85rem;
}

.btn-small {
    background: var(--accent-strong);
    color: #fff;
    border: none;
    border-radius: 6px;
    padding: 0.25rem 0.7rem;
    font-size: 0.8rem;
    cursor: pointer;
}

.btn-small:hover {
    filter: brightness(1.15);
}

/* ---- Desktop layout: game left ~65%, right rail ~35% ---- */

.app-shell {
    display: grid;
    grid-template-columns: 65% 35%;
    flex: 1;
    min-height: 0;
}

.game-column {
    position: relative;
    background: #000;
}

.game-frame-wrap {
    position: absolute;
    inset: 0;
}

.game-frame-wrap.empty {
    position: static;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 320px;
    color: var(--text-dim);
    padding: 2rem;
    text-align: center;
}

#game-frame {
    width: 100%;
    height: 100%;
    border: 0;
    display: block;
    background: #000;
    /* iOS Safari decides whether a touch drag over the <iframe> belongs to this (scrollable) portal
       page or to the framed document *before* the game inside ever sees it - a touch-action set inside
       the iframe's own content can't override that outer-page decision. Without this, a drag started on
       an in-game joystick/control can get hijacked as a page scroll instead of reaching the game, and a
       tap can be swallowed by the outer page's gesture recognizer. touch-action: none here hands every
       touch over that rectangle straight to the framed document. */
    touch-action: none;
    overscroll-behavior: contain;
}

.btn-open-window {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    z-index: 15;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 6px;
    padding: 0.35rem 0.7rem;
    font-size: 0.78rem;
    line-height: 1.2;
    cursor: pointer;
}

.btn-open-window:hover {
    background: rgba(0, 0, 0, 0.75);
    border-color: rgba(255, 255, 255, 0.6);
}

.rail {
    display: flex;
    flex-direction: column;
    min-height: 0;
    border-left: 1px solid var(--border);
    background: var(--bg-panel);
}

.tab-radio {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}

.tabbar {
    display: flex;
    order: 0;
    border-bottom: 1px solid var(--border);
    background: var(--bg-raised);
    height: var(--tabbar-h);
    flex: 0 0 auto;
}

.tab-label {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85rem;
    color: var(--text-dim);
    cursor: pointer;
    user-select: none;
    border-bottom: 2px solid transparent;
}

.tab-label:hover {
    color: var(--text);
}

#tab-requests:checked ~ .tabbar label[for="tab-requests"],
#tab-versions:checked ~ .tabbar label[for="tab-versions"],
#tab-chat:checked ~ .tabbar label[for="tab-chat"],
#tab-games:checked ~ .tabbar label[for="tab-games"] {
    color: var(--text);
    border-bottom-color: var(--accent);
    font-weight: 600;
}

.panels {
    flex: 1;
    order: 1;
    overflow-y: auto;
    padding: 0.75rem;
}

.panel {
    display: none;
}

#tab-requests:checked ~ .panels .panel-requests,
#tab-versions:checked ~ .panels .panel-versions,
#tab-games:checked ~ .panels .panel-games {
    display: block;
}

/* .panel-chat needs display:flex (task 16's own layout rule below), not block - split out of the
   shared selector above so its own display value doesn't get lost. Same specificity/source-order
   trap this comment exists to warn about: .panel-chat's later flex-layout rule must NEVER re-declare
   `display` unconditionally, or it silently wins the show/hide tie against `.panel { display: none }`
   above by source order and the chat panel renders always-visible regardless of which tab is
   selected - found exactly that way via live browser testing, not code review. */
#tab-chat:checked ~ .panels .panel-chat {
    display: flex;
}

.placeholder-note,
.empty-note {
    color: var(--text-dim);
    font-size: 0.9rem;
    padding: 1rem 0.25rem;
}

/* ---- Submit box (task 12) ---- */

.submit-box {
    margin-bottom: 0.75rem;
}

#request-form textarea,
#gameidea-form textarea {
    width: 100%;
    resize: vertical;
    background: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.5rem 0.6rem;
    font-size: 0.9rem;
}

.submit-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-top: 0.35rem;
}

.char-counter {
    font-size: 0.75rem;
    color: var(--text-faint);
}

.btn-primary {
    background: linear-gradient(180deg, var(--accent), var(--accent-strong));
    color: #fff;
    border: none;
    border-radius: 8px;
    padding: 0.45rem 1rem;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}

.btn-primary:hover {
    filter: brightness(1.15);
}

.btn-primary:disabled {
    opacity: 0.5;
    cursor: default;
}

.form-error {
    margin-top: 0.4rem;
    color: var(--down);
    font-size: 0.8rem;
}

/* ---- Vote widget (tasks 12/13) ---- */

.vote-widget {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.1rem;
    flex: 0 0 auto;
}

.vote-btn {
    background: none;
    border: 1px solid var(--border);
    border-radius: 4px;
    color: var(--text-dim);
    width: 1.6rem;
    height: 1.4rem;
    line-height: 1;
    font-size: 0.7rem;
    cursor: pointer;
}

.vote-btn:hover:not(:disabled) {
    border-color: var(--accent);
    color: var(--text);
}

.vote-btn:disabled {
    opacity: 0.4;
    cursor: default;
}

.vote-btn.up.active {
    background: var(--up);
    border-color: var(--up);
    color: #0b0714;
}

.vote-btn.down.active {
    background: var(--down);
    border-color: var(--down);
    color: #0b0714;
}

.vote-score {
    font-size: 0.85rem;
    font-weight: 700;
    min-width: 1.5rem;
    text-align: center;
}

/* ---- Request list (task 12) ---- */

.request-list,
.version-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.request-row {
    display: flex;
    gap: 0.6rem;
    padding: 0.5rem;
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    background: var(--bg-raised);
}

.request-row.is-leader {
    border-color: var(--accent);
    box-shadow: 0 0 0 1px var(--accent) inset;
}

.request-row.is-pending {
    opacity: 0.6;
}

.request-body {
    flex: 1;
    min-width: 0;
}

.leader-badge {
    display: inline-block;
    background: var(--accent);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    border-radius: 4px;
    padding: 0.1rem 0.4rem;
    margin-bottom: 0.25rem;
}

.request-text {
    margin: 0 0 0.35rem;
    font-size: 0.9rem;
    word-break: break-word;
}

.request-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: center;
    font-size: 0.75rem;
    color: var(--text-faint);
}

.status-chip {
    border-radius: 999px;
    padding: 0.05rem 0.5rem;
    font-weight: 600;
    text-transform: uppercase;
    font-size: 0.65rem;
    letter-spacing: 0.02em;
    border: 1px solid var(--border);
}

.status-open { color: var(--up); border-color: var(--up); }
.status-queued { color: #ffd9a0; border-color: #ffd9a0; }
.status-building { color: var(--accent); border-color: var(--accent); }
.status-built,
.status-promoted { color: #7ee08b; border-color: #7ee08b; }
.status-failed,
.status-rejected { color: var(--down); border-color: var(--down); }

.built-link {
    color: var(--up);
    text-decoration: none;
}

.built-link:hover {
    text-decoration: underline;
}

/* ---- Version list (task 13) ---- */

.version-row {
    padding: 0.6rem;
    border: 1px solid var(--border-soft);
    border-radius: 8px;
    background: var(--bg-raised);
}

.version-row.is-head {
    border-color: var(--up);
}

.version-row.is-playing {
    box-shadow: 0 0 0 1px var(--accent) inset;
}

.version-row.flash-highlight {
    animation: flash-highlight 1.5s ease-out;
}

@keyframes flash-highlight {
    0% { box-shadow: 0 0 0 2px var(--accent) inset; }
    100% { box-shadow: 0 0 0 0 transparent inset; }
}

.version-head {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-bottom: 0.25rem;
}

.version-semver {
    font-weight: 700;
}

.head-badge {
    background: var(--up);
    color: #0b0714;
    font-size: 0.65rem;
    font-weight: 700;
    border-radius: 4px;
    padding: 0.05rem 0.4rem;
}

.changelog {
    margin: 0 0 0.3rem;
    font-size: 0.85rem;
    color: var(--text);
}

.version-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.6rem;
    font-size: 0.75rem;
    color: var(--text-faint);
    margin-bottom: 0.4rem;
}

.version-actions {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.btn-play,
.btn-fork,
.btn-edit-request {
    background: var(--bg-panel);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.3rem 0.7rem;
    font-size: 0.8rem;
    cursor: pointer;
}

.btn-play:hover:not(:disabled) {
    border-color: var(--up);
}

.btn-play:disabled,
.btn-fork:disabled,
.btn-edit-request:disabled {
    opacity: 0.45;
    cursor: default;
}

/* CW-3: sits inline in .request-meta, so it needs to be small like the chips/links around it rather
   than the version panel's own larger action-row buttons. */
.btn-edit-request {
    padding: 0.05rem 0.5rem;
    font-size: 0.7rem;
}

/* ==================================================================================================
   Mobile (iPhone) layout - game on top at a 16:9 box, panel below, tabs pinned to the bottom.
   ================================================================================================== */

@media (max-width: 900px) {
    .app-shell {
        display: flex;
        flex-direction: column;
    }

    .game-column {
        width: 100%;
    }

    .game-frame-wrap {
        position: relative;
        inset: auto;
        width: 100%;
        aspect-ratio: 16 / 9;
    }

    .game-frame-wrap.empty {
        aspect-ratio: 16 / 9;
        min-height: 0;
    }

    .rail {
        border-left: none;
        border-top: 1px solid var(--border);
    }

    .tabbar {
        order: 2;
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 20;
        padding-bottom: env(safe-area-inset-bottom, 0);
        box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.4);
    }

    .panels {
        order: 1;
        padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0) + 0.75rem);
    }

    .portal-header {
        font-size: 0.85rem;
        padding: 0.4rem 0.6rem;
    }

    select {
        max-width: 8.5rem;
        font-size: 0.8rem;
    }

    /* Task 21's content-policy footer link sits after .app-shell, outside .panels, so it doesn't
       inherit .panels' own tabbar clearance above - without this, the fixed bottom tabbar (z-index 20)
       permanently covers the last ~tabbar-height of the viewport and the link becomes unclickable
       whenever the page is scrolled to the bottom. Same clearance formula .panels already uses. Found by
       an actual trial-click failing in Playwright, not by inspection - see AdminModerationTests.cs. */
    .portal-footer {
        padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0) + 0.75rem);
    }
}

/* ==================================================================================================
   Chat (task 16)
   ================================================================================================== */

.panel-chat {
    /* NOT `display: flex` here - see the #tab-chat:checked show/hide rule above for why setting
       `display` on the bare `.panel-chat` selector is exactly what breaks hide-by-default. */
    flex-direction: column;
    height: 100%;
    min-height: 20rem;
}

.chat-status {
    font-size: 0.75rem;
    color: var(--text-faint);
    padding: 0.1rem 0;
}

.chat-status.is-connected { color: var(--up); }
.chat-status.is-connecting { color: #ffd9a0; }
.chat-status.is-disconnected { color: var(--down); }

.chat-messages {
    list-style: none;
    margin: 0.35rem 0;
    padding: 0;
    flex: 1;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    min-height: 0;
}

.chat-message {
    font-size: 0.85rem;
    padding: 0.3rem 0.5rem;
    border-radius: 6px;
    background: var(--bg-raised);
    word-break: break-word;
}

.chat-message-author {
    font-weight: 700;
    color: var(--accent);
    margin-right: 0.4rem;
}

.chat-message-text {
    color: var(--text);
}

.chat-form {
    display: flex;
    gap: 0.4rem;
}

.chat-text {
    flex: 1;
    background: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 0.4rem 0.6rem;
    font-size: 0.9rem;
}

.chat-error {
    margin-top: 0.3rem;
}

/* ==================================================================================================
   Admin (task 6) - deliberately minimal, non-dark chrome distinct from the public portal, layered on
   top of MudBlazor's own styling (AdminLayout.razor). Every actual control here is a plain HTML
   element inside a native <form> (see that layout's comment on why: static SSR, no circuit).
   ================================================================================================== */

.admin-nav {
    display: flex;
    gap: 1rem;
}

.admin-nav a {
    color: inherit;
    text-decoration: none;
    font-size: 0.9rem;
    opacity: 0.85;
}

.admin-nav a:hover {
    opacity: 1;
    text-decoration: underline;
}

/* Task 21's global kill switch - "one click, prominent, and reversible", always visible in the admin
   app bar rather than buried in the full Settings form. */
.kill-switch-form {
    margin-right: 1rem;
}

.kill-switch-btn {
    border: 2px solid #fff;
    border-radius: 6px;
    padding: 0.35rem 0.8rem;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.02em;
    background: transparent;
    color: #fff;
    cursor: pointer;
    /* Task 25: a plain <button> wraps its label like any other inline text by default. Below ~480px
       nothing else in the app bar (title, .admin-nav, this button, the Sign-out MudButton) had
       `flex-shrink:0` or `white-space:nowrap`, so squeezing this row made "Kill switch: off" wrap onto
       a second line - growing this <button>'s (and its <form>'s) own box taller than the app bar's
       fixed height and poking out above/below it far enough to sit on top of the Sign-out button next
       to it (real occlusion, reproduced by AdminAuthTests.Login_..._LocksItBackDown's trial-click at the
       iPhone13 viewport, not flakiness). A wrapped, unpredictably-taller box is also just inherently
       fragile next to a sibling control - nowrap makes this button's box height deterministic (always
       one line), matching the same "force one line" fix mud-mobile.css already applies to the app bar's
       own title for the identical reason. The @media block below removes enough of this row's other
       width (nav link spacing/size, this button's own padding/border/font) that everything actually
       fits on that one line with real clearance, instead of the previous near-zero-margin fit that made
       any small rendering difference enough to tip into overlap. */
    white-space: nowrap;
}

.kill-switch-btn:hover {
    background: rgba(255, 255, 255, 0.15);
}

.kill-switch-btn.is-active {
    background: #c0392b;
    border-color: #c0392b;
    color: #fff;
    animation: kill-switch-pulse 2s ease-in-out infinite;
}

@keyframes kill-switch-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.7; }
}

/* Task 25: below ~480px, title + nav + kill switch + sign-out cannot all fit on one line with any real
   clearance between controls - shrinking padding/font alone still left the kill-switch form and the
   Sign-out button only ~16-24px apart, which measured (via getBoundingClientRect, matching Playwright's
   own BoundingBoxAsync exactly) as genuinely non-overlapping and still wasn't enough to keep Playwright's
   own actionability/interception check from reporting the kill-switch button as occluding Sign-out at
   the iPhone13 viewport - confirmed with a throwaway diagnostic test that a *raw* Mouse.Down/Up at the
   measured center coordinate reaches and activates Sign-out just fine even while the actionability
   pre-check keeps failing, and confirmed the same failure reproduces against bundled Chromium (not just
   the installed Chrome channel), so this isn't a CSS overlap bug so much as this row leaving no real
   margin between two independently-interactive controls. Rather than chase an ever-larger single-line
   gap, give the row a real second line: title+nav keep the first line, kill-switch and sign-out move to
   a second line of their own with generous width to spread across instead of being squeezed edge to
   edge. 480px (not this file's usual 900px portal breakpoint) because this row only gets tight once the
   public portal's tablet-width layout has already been left behind - MudBlazor's own AppBar/toolbar
   padding already steps down at a narrower point too (see the measured 56px vs 64px app-bar height). */
@media (max-width: 480px) {
    /* MudMainContent's top offset (and MudBlazor's own toolbar-height calc) are both driven off this
       custom property, so redefining it here is enough to reserve room for a three-line bar without
       hand-computing pixel offsets - see MudBlazor.min.css's `.mud-main-content{padding-top:calc(var(
       --mud-appbar-height) - ...)}` and `.mud-toolbar-appbar{height:calc(var(--mud-appbar-height) - ...)}`
       rules, both of which read this variable. `!important` is required: MudThemeProvider injects its own
       `:root{--mud-appbar-height:64px}` (unconditional, no media query) via a <style> tag it renders
       inside <body>, which sits LATER in the document than this stylesheet's <link> in <head> - on a
       specificity tie, later source order wins, so a plain (non-!important) override here was silently
       losing to MudThemeProvider's 64px every time (confirmed via getComputedStyle: the var read back as
       64px with this rule in place but no !important, and the appbar's own box stayed clipped to the old
       single-line height while its wrapped content rendered past it, uncovered by the purple background -
       found via a throwaway diagnostic screenshot, not by inspection). Safe to redefine broadly here:
       this app's only other consumer of MudBlazor's Fixed-AppBar/MudMainContent machinery is AdminLayout
       itself - the public portal (MainLayout.razor) doesn't use MudLayout at all. 156px = comfortable
       room for three ~40px rows plus the toolbar's own vertical padding, verified empirically the same
       way the two-line height above it was (real trial-clicks/measurements at this viewport, not a
       theoretical sum). */
    :root {
        --mud-appbar-height: 156px !important;
    }

    .mud-appbar .mud-toolbar {
        flex-wrap: wrap;
        row-gap: 0.3rem;
        align-content: center;
    }

    /* Title + nav no longer share line 1 below this breakpoint (see .admin-nav's own flex-basis:100%
       just below) - MudSpacer's job was only ever to push the kill-switch/sign-out wrapper to the far
       right of a SHARED line, which no longer happens once every row below the title has its own
       flex-basis:100% line to itself. Left in the DOM it still claims a line of its own (flex-wrap
       always breaks before a flex-basis:100% item, even an near-zero-width one), producing an empty
       gap row between nav and the actions row - hiding it removes that dead line outright. */
    .mud-appbar .mud-toolbar > .flex-grow-1 {
        display: none;
    }

    /* Task (Audit-log-clips-at-the-edge follow-up to task 25): title + all 4 nav links no longer fit on
       one line at a genuine ~390px viewport - measured live (a real Playwright trial-click plus
       getBoundingClientRect, the same method task 25 used) that the "Audit log" link's own box ended up
       partly past the viewport's right edge, and because MudAppBar is `position:fixed` with no scroll
       mechanism of its own, anything past that edge is genuinely unreachable, not just scrolled-away -
       matches the visually-clipped symptom exactly. Fix is the same pattern as .admin-appbar-actions
       just below: `flex-basis:100%` forces .admin-nav onto a fresh line of its own with the bar's full
       width to spread its 4 links across, rather than trying to squeeze title+nav any tighter onto one
       line (already tried in task 25's own sizing pass for the row below this one, and rejected there
       for the same reason: a razor-thin single-line fit is fragile to any future label/width change). */
    .admin-nav {
        flex-basis: 100%;
        gap: 0.5rem;
        margin-top: 0.2rem;
    }

    .admin-nav a {
        font-size: 0.72rem;
    }

    /* Forces a line break: this wrapper (kill-switch form + sign-out form together, see
       AdminLayout.razor) starts a fresh line of its own with the bar's full width to spread its two
       children across - real, generous clearance instead of a razor-thin single-line gap. Forcing the
       break on this WRAPPER rather than on .kill-switch-form directly is load-bearing: an earlier
       version put `flex-basis:100%` on .kill-switch-form alone, which did start it on a fresh line but,
       having claimed the full row width for itself, pushed the sign-out form onto a line of its own
       instead of sharing one with it (also caught by that same diagnostic screenshot). */
    .admin-appbar-actions {
        flex-basis: 100%;
        display: flex;
        align-items: center;
        justify-content: flex-start;
        margin-top: 0.2rem;
    }

    .kill-switch-form {
        margin-right: 3rem;
    }

    .kill-switch-btn {
        padding: 0.25rem 0.6rem;
        font-size: 0.72rem;
        border-width: 1px;
    }

    /* The Sign-out MudButton's default size still assumes desktop-width breathing room around its
       label; drop its own horizontal padding so it doesn't add width this row no longer has to spare. */
    .admin-appbar-actions form[action="auth/logout"] .mud-button-root {
        min-width: 0;
        padding-left: 0.6rem;
        padding-right: 0.6rem;
    }
}

.admin-field {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    margin-bottom: 0.9rem;
    max-width: 26rem;
}

.admin-field label {
    font-size: 0.85rem;
    font-weight: 600;
}

.admin-field input[type="number"],
.admin-field input[type="text"],
.admin-field input[type="password"] {
    border: 1px solid #999;
    border-radius: 6px;
    padding: 0.4rem 0.5rem;
    font-size: 0.9rem;
}

.admin-field-hint {
    font-size: 0.75rem;
    color: #666;
}

.admin-field-checkbox label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 400;
}

.admin-actions-row {
    margin-top: 1rem;
}

.admin-stub-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
}

/* Task 21 widened these tables (a Screen column, a per-row reason input) - wrap in a horizontally
   scrolling container rather than letting the table itself force the whole page wide at narrow
   (iPhone) viewports. */
.admin-table-wrap {
    overflow-x: auto;
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.85rem;
}

.admin-reason-input {
    width: 9rem;
    border: 1px solid #999;
    border-radius: 6px;
    padding: 0.25rem 0.4rem;
    font-size: 0.8rem;
}

/* Task 15: the "Promote to game" form needs slug/title/blurb inputs - same look as
   .admin-reason-input, just wider and stacked in their own row instead of sharing the hide/delete
   row's single reason input. */
.admin-promote-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-top: 0.35rem;
    align-items: center;
}

.admin-promote-input {
    border: 1px solid #999;
    border-radius: 6px;
    padding: 0.25rem 0.4rem;
    font-size: 0.8rem;
}

.admin-promote-input.slug { width: 8rem; }
.admin-promote-input.title { width: 12rem; }
.admin-promote-input.blurb { width: 16rem; }

/* Task 21's ScreenStatus column on the Requests table - Rejected/Flagged is exactly what the pre-screen
   is hiding from the public list, so it's worth a visual flag here too. */
.screen-status-rejected {
    color: #c0392b;
    font-weight: 600;
}

.screen-status-flagged {
    color: #b8860b;
    font-weight: 600;
}

.admin-table th,
.admin-table td {
    text-align: left;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid #ddd;
    vertical-align: top;
}

/* Task 19: per-gate pass/fail/skipped breakdown under the Build run monitor table's "Gates" column. */
.admin-gate-detail {
    font-size: 0.75rem;
    color: #666;
    margin-top: 0.15rem;
}

.admin-row-actions {
    display: flex;
    gap: 0.4rem;
    white-space: nowrap;
}

/* LineRegistryCheckService's repoint form on the admin Lines table. */
.admin-semver-select {
    margin-right: 0.4rem;
    padding: 0.15rem 0.3rem;
}

.admin-inline-form {
    display: inline;
}

.btn-danger {
    background: #c0392b !important;
}

.admin-login-page {
    align-items: center;
    justify-content: center;
    padding: 3rem 1.5rem;
}

.admin-login-card {
    max-width: 22rem;
    margin: 0 auto;
    background: var(--bg-panel);
    border: 1px solid var(--border);
    border-radius: 10px;
    padding: 1.5rem;
}

.admin-login-card h1 {
    font-size: 1.2rem;
    margin: 0 0 1rem;
}

.admin-login-card form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.admin-login-card .admin-field input {
    background: var(--bg-raised);
    color: var(--text);
    border-color: var(--border);
}

.admin-login-divider {
    text-align: center;
    color: var(--text-faint);
    font-size: 0.75rem;
    margin: 0.9rem 0 0.6rem;
}

.admin-google-login {
    display: block;
    text-align: center;
    background: var(--bg-raised);
    color: var(--text);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 0.45rem 1rem;
    font-size: 0.85rem;
    font-weight: 600;
    text-decoration: none;
}

.admin-google-login:hover {
    filter: brightness(1.1);
}
