/* /Layout/MainLayout.razor.rz.scp.css */
/* ── App shell: the concourse gantry ──────────────────────────────────────
   The header and footer are the brushed-steel frame the boards hang in. They
   are the only steel on the page besides board rails, which is what makes the
   frame read as structure rather than decoration.

   Contrast note: steel is a light surface, so all text on it is near-black
   (#14161a on the mid-steel #6E747D ≈ 5.6:1). Do not put --steel-dark or any
   of the board's dim greys on the rails — they were computed for flap-black. */

/* The gantry is bolted, like the reference board's frame. The bolt heads are a background
   layer at the rail's two ends rather than pseudo-elements, because ::before/::after on a
   sticky header collide with the reveal shadow below. */
.app-header[b-duu2a000yx] {
    position: sticky;
    top: 0;
    z-index: 100;
    background-image:
        radial-gradient(circle at 11px 50%, rgba(0,0,0,0.55) 0 2.5px, rgba(255,255,255,0.28) 2.6px 3.4px, transparent 3.5px),
        radial-gradient(circle at calc(100% - 11px) 50%, rgba(0,0,0,0.55) 0 2.5px, rgba(255,255,255,0.28) 2.6px 3.4px, transparent 3.5px),
        var(--steel-face);
    border-bottom: 1px solid rgba(0,0,0,0.65);
    box-shadow: 0 2px 10px rgba(0,0,0,0.6);
}

/* The board hangs off the gantry: a dark reveal under the steel. */
.app-header[b-duu2a000yx]::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: -3px;
    height: 3px;
    background: linear-gradient(180deg, rgba(0,0,0,0.55), transparent);
    pointer-events: none;
}

/* .mock-data-banner is NOT styled here. MockDataBanner is a child component, so Blazor's
   scoped-CSS attribute never lands on its markup and every rule in this file misses it —
   the same trap already documented for .app-nav__user. Its rules live in app.css. */

/* Radzen used to paint anchors out from under this rule. It now loads into the
   `vendor` cascade layer (see css/foundation.css), so these no longer need
   !important — they win on ordinary source order. Keep them: the rail is steel and
   must never inherit the board's amber link colour. */
.app-header a[b-duu2a000yx],
.app-header a:link[b-duu2a000yx],
.app-header a:visited[b-duu2a000yx],
.app-header a:hover[b-duu2a000yx] {
    background: transparent;
    box-shadow: none;
    text-decoration: none;
}

.app-nav[b-duu2a000yx] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 1.25rem;
    height: 60px;
    max-width: var(--rail-max);
    margin: 0 auto;
    width: 100%;
    gap: 0.75rem;
}

.app-nav__brand[b-duu2a000yx] {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-shrink: 0;
    text-decoration: none;
    background: transparent;
}

/* The wordmark sits in real flap cells, so the brand is the board naming itself. */
.app-nav__brand .flapstrip[b-duu2a000yx] { gap: 1px; }
.app-nav__brand .flap[b-duu2a000yx] { font-size: var(--text-sm); min-width: 0.7em; padding: 0.24em 0.1em; }

.app-nav__brand-icon[b-duu2a000yx] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    background: var(--flap-black);
    color: var(--amber);
    flex-shrink: 0;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.6);
}

.app-nav__links[b-duu2a000yx] { display: flex; align-items: center; gap: 0.125rem; }

/* Nav links sit on steel, so they invert: dark ink, amber underline when active. */
.app-nav__link[b-duu2a000yx] {
    color: var(--steel-ink);
    text-decoration: none;
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    padding: 0.5rem 0.7rem;
    border-radius: 0;
    border-bottom: 2px solid transparent;
    transition: color var(--motion-fast), border-color var(--motion-fast);
    background: transparent;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    line-height: 1;
    white-space: nowrap;
}

.app-nav__link:hover[b-duu2a000yx] {
    background: transparent;
    color: var(--flap-black);
    border-bottom-color: rgba(0,0,0,0.45);
}

.app-nav__link.active[b-duu2a000yx] {
    background: var(--flap-black);
    color: var(--amber);
    border-bottom-color: var(--amber);
    box-shadow: none;
}

.app-nav__user[b-duu2a000yx] {
    color: var(--steel-ink);
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    max-width: 200px;
}

.app-main[b-duu2a000yx] {
    min-height: calc(100vh - 60px);
    background: var(--board-ground);
}

/* ── Footer: the platform rail ───────────────────────────────────────────── */
.app-footer[b-duu2a000yx] {
    background-image:
        radial-gradient(circle at 11px 50%, rgba(0,0,0,0.55) 0 2.5px, rgba(255,255,255,0.28) 2.6px 3.4px, transparent 3.5px),
        radial-gradient(circle at calc(100% - 11px) 50%, rgba(0,0,0,0.55) 0 2.5px, rgba(255,255,255,0.28) 2.6px 3.4px, transparent 3.5px),
        var(--steel-face);
    border-top: 1px solid rgba(0,0,0,0.65);
    padding: 0.5rem 0;
}

.app-footer__inner[b-duu2a000yx] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    max-width: var(--rail-max);
    margin: 0 auto;
    padding: 0 1.25rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--steel-ink);
}

.app-footer__center[b-duu2a000yx] { color: var(--steel-ink); font-weight: 600; }

.app-footer__hint kbd[b-duu2a000yx] {
    background: var(--flap-black);
    color: var(--flap-white);
    border: 1px solid rgba(0,0,0,0.5);
    border-radius: 0;
    padding: 0.1rem 0.4rem;
    font-size: var(--text-2xs);
}

.app-footer__env-badge[b-duu2a000yx] {
    background: var(--flap-black);
    color: var(--amber);
    border: 1px solid rgba(0,0,0,0.5);
    border-radius: 0;
    padding: 0.15rem 0.5rem;
    font-size: var(--text-2xs);
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}

/* The footer's cost counter sits on steel too, so it needs the dark cell. */
.app-footer__inner[b-duu2a000yx]  .session-cost {
    background: var(--flap-black);
    color: var(--flap-white);
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.5);
}

/* ── Narrow widths: column priority narrows, the rail never wraps ────────── */
@media (max-width: 639.98px) {
    .app-nav[b-duu2a000yx] { padding: 0 0.6rem; height: 54px; gap: 0.4rem; }
    .app-nav__link[b-duu2a000yx] { padding: 0.4rem 0.45rem; font-size: var(--text-2xs); letter-spacing: 0.1em; }
    .app-nav__user[b-duu2a000yx] { display: none; }
    .app-main[b-duu2a000yx] { min-height: calc(100vh - 54px); }

    .d-flex.align-items-center.gap-2.ms-3[b-duu2a000yx],
    .app-nav__link.ms-3[b-duu2a000yx] { margin-left: 0; }
}

/* Below 480px the wordmark's cells cost more than they earn; the mark holds. */
@media (max-width: 479.98px) {
    .app-nav__brand-text[b-duu2a000yx] { display: none; }
    .app-nav__brand .flapstrip[b-duu2a000yx] { display: none; }
}

/* ── ErrorBoundary fallback (shared Po convention) ─────────────────────────── */
.po-error-panel[b-duu2a000yx] {
    margin: 1rem auto;
    max-width: 40rem;
    padding: 1rem 1.25rem;
    border: 1px solid var(--po-error-border, #b3261e);
    border-radius: 0.5rem;
    background: var(--po-error-bg, rgba(179, 38, 30, 0.08));
    color: inherit;
}

.po-error-panel h2[b-duu2a000yx] {
    margin: 0 0 0.5rem;
    font-size: 1.1rem;
}

.po-error-panel p[b-duu2a000yx] {
    margin: 0;
}
/* /Layout/NavMenu.razor.rz.scp.css */
/* Individual link styling is global (app.css .app-nav__link) because these anchors are
   rendered by NavLink inside this component and MainLayout's scoped sheet cannot reach
   them. Only the rail's own layout belongs here. */

.app-nav__links[b-jaxy1nnjrx] {
    display: flex;
    align-items: center;
    gap: 0.125rem;
    flex: 1 1 auto;
    min-width: 0;
    justify-content: center;
}

/* Below md the five labels cannot share the rail with the wordmark. They wrap to a
   second row when the wordmark and account controls leave room, and only scroll inline
   when even two rows cannot fit — a hidden nav is the bug this component was written
   to fix. The horizontal scrollbar is left visible (thin) so the overflow is
   discoverable on touch, with an edge fade so it does not look broken. */
@media (max-width: 991.98px) {
    .app-nav__links[b-jaxy1nnjrx] {
        justify-content: flex-end;
        flex-wrap: wrap;
        overflow-x: auto;
        scrollbar-width: thin;
        -webkit-overflow-scrolling: touch;
        mask-image: linear-gradient(to right, transparent 0, black 12px, black calc(100% - 12px), transparent 100%);
    }

    /* On really narrow viewports, fall back to a single scrollable row to keep the
       rail height constant and the wordmark from reflowing into two lines. */
    @media (max-width: 479.98px) {
        .app-nav__links[b-jaxy1nnjrx] {
            flex-wrap: nowrap;
            justify-content: flex-start;
        }
    }
}
/* /Pages/BulkGenerate/BulkGallery.razor.rz.scp.css */
/* Scoped CSS for BulkGallery.razor.

   This is the surface the chosen world was picked for: ten slots that each move
   independently from QUEUED through WORKING to DONE or FAILED. So it is built as
   the departures board proper — a fixed grid of numbered bays, each one a flap
   cell that turns in place. Columns never move; only the cells' contents change.

   The old sheet leaned on light-mode fallbacks (#f8fafc, #f1f5f9, #334155) that
   are invisible on flap-black, and on a blue→violet gradient fill. Both are gone.

   Status colour law, shared with the rest of the app and never extended:
     steel-dark = QUEUED · amber = WORKING · flap-white = DONE · red = FAILED
   Every state also carries its own word, so the board reads in monochrome. */

/* ── Empty state: an unlit board waiting for a service ─────────────────────
   Solid rule, not dashed. In this world a dashed edge means "you can drop a file
   here", and it is used by the intake zone directly above this panel. This bay is
   an output area — giving it the same dashed edge invited a drop that does nothing. */
.bulk-empty-state[b-6z247pq4c3] {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: var(--spacing-xl) var(--spacing-lg);
    text-align: center;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    min-height: 260px;
}

.bulk-empty-state__icon[b-6z247pq4c3] { font-size: var(--text-3xl); color: var(--steel-dark); margin-bottom: var(--spacing-md); }

.bulk-empty-state__title[b-6z247pq4c3] {
    font-family: var(--font-flap);
    font-size: var(--text-md);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--flap-white);
    margin-bottom: 0.5rem;
}

.bulk-empty-state__hint[b-6z247pq4c3] { font-size: var(--text-md); color: var(--steel); max-width: 42ch; margin: 0 auto; line-height: 1.55; }

/* ── Board progress header ───────────────────────────────────────────────── */
.bulk-progress-header[b-6z247pq4c3] {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
    margin-bottom: 0.4rem;
    font-variant-numeric: tabular-nums;
}

/* A strip of unlit flaps that light amber as slots complete. */
.bulk-progress-bar[b-6z247pq4c3] {
    height: 10px;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    border-radius: 0;
    overflow: hidden;
    background-image: repeating-linear-gradient(90deg, transparent 0 7px, var(--cell-split) 7px 8px);
}

.bulk-progress-bar__fill[b-6z247pq4c3] {
    height: 100%;
    width: 100%;
    background: var(--amber);
    border-radius: 0;
    transform: scaleX(var(--progress, 0));
    transform-origin: left center;
    transition: transform var(--motion-slow) var(--motion-ease-in-out);
    will-change: transform;
}

/* ── The board grid (virtualized — see <Virtualize> in the component) ────── */
.bulk-gallery[b-6z247pq4c3] {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 1px;
    background: var(--rule);
    margin-top: 0;
    scrollbar-gutter: stable;
}

/* Portrait: two bays across keeps the images large enough to judge. A single
   column would push slot 10 far below the fold on a phone. */
@media (max-width: 639.98px) {
    .bulk-gallery[b-6z247pq4c3] { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 479.98px) {
    .bulk-gallery[b-6z247pq4c3] { grid-template-columns: 1fr; }
}

/* ── A bay ───────────────────────────────────────────────────────────────── */
.bulk-slot[b-6z247pq4c3] {
    border-radius: 0;
    overflow: hidden;
    background: var(--flap-black);
    border: 1px solid transparent;
    transition: border-color var(--motion-fast);
}

.bulk-slot:focus-within[b-6z247pq4c3] { outline: 2px solid var(--amber); outline-offset: -2px; }

.bulk-slot--pending[b-6z247pq4c3]  { }
.bulk-slot--processing[b-6z247pq4c3] { border-color: var(--amber); }
.bulk-slot--complete[b-6z247pq4c3] { }
.bulk-slot--failed[b-6z247pq4c3]   { border-color: var(--cancel-red); }

/* Bay header: the position number in its own cell, status on the right. */
.bulk-slot__header[b-6z247pq4c3] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    padding: 0.35rem 0.5rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    border-bottom: 1px solid var(--rule);
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
}

.bulk-slot__num[b-6z247pq4c3] {
    font-weight: 700;
    color: var(--flap-white);
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.08em;
}

.bulk-slot__status--queued[b-6z247pq4c3] { font-size: var(--text-2xs); }

/* ── Image bay (1:1, so the grid's rows stay aligned across the board) ───── */
.bulk-slot__img-area[b-6z247pq4c3] {
    aspect-ratio: 1 / 1;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    overflow: hidden;
}

.bulk-slot__img-area img[b-6z247pq4c3] {
    width: 100%;
    height: 100%;
    object-fit: cover;
    cursor: zoom-in;
    /* The finished image is revealed, not scaled on hover: a photo that jumps
       under the cursor makes a ten-up comparison harder to read. */
    opacity: 0;
    animation: slotReveal-b-6z247pq4c3 var(--motion-slow) var(--motion-ease-in-out) forwards;
}

@keyframes slotReveal-b-6z247pq4c3 { to { opacity: 1; } }

.bulk-slot__img-area img:focus-visible[b-6z247pq4c3] { outline: 2px solid var(--amber); outline-offset: -2px; }

.bulk-slot__img-area--pending[b-6z247pq4c3] {
    color: var(--steel-dark);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    letter-spacing: 0.16em;
}

.bulk-slot__img-area--processing[b-6z247pq4c3] {
    flex-direction: column;
    gap: 0.5rem;
    color: var(--amber);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.16em;
}

.bulk-slot__img-area--failed[b-6z247pq4c3] {
    color: var(--cancel-red-tx);
    font-size: var(--text-xs);
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.75rem;
    text-align: center;
    line-height: 1.4;
}

/* The working indicator is the board's row lamp, not a spinning ring. */
.bulk-slot__spinner[b-6z247pq4c3] {
    width: 26px;
    height: 10px;
    border: none;
    border-radius: 0;
    background: radial-gradient(circle at 50% 50%, var(--amber) 0 22%, transparent 24%) 0 0 / 5px 5px;
    animation: lampRun-b-6z247pq4c3 1.1s steps(5, end) infinite;
}

@keyframes lampRun-b-6z247pq4c3 {
    0%   { background-position: 0 0; opacity: 1; }
    50%  { opacity: 0.45; }
    100% { background-position: 25px 0; opacity: 1; }
}

/* A queued bay: the unlit sweep crossing dark flaps. */
.bulk-slot__shimmer[b-6z247pq4c3] {
    width: 100%;
    height: 100%;
    background-color: var(--flap-shadow);
    background-image: linear-gradient(90deg,
        transparent 0%, rgba(255,180,0,0.14) 45%, rgba(255,180,0,0.14) 55%, transparent 100%);
    background-size: 220% 100%;
    background-repeat: no-repeat;
    animation: bulk-shimmer-b-6z247pq4c3 1.5s linear infinite;
}

@keyframes bulk-shimmer-b-6z247pq4c3 {
    0%   { background-position: 220% 0; }
    100% { background-position: -120% 0; }
}

/* ── Bay footer ──────────────────────────────────────────────────────────── */
.bulk-slot__footer[b-6z247pq4c3] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.4rem;
    padding: 0.35rem 0.5rem;
    min-height: 2.2rem;
    border-top: 1px solid var(--rule);
}

.bulk-slot__prompt-preview[b-6z247pq4c3] {
    font-size: var(--text-xs);
    color: var(--steel-dark);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 60%;
}

.bulk-slot__actions[b-6z247pq4c3] { display: flex; gap: 1px; flex-shrink: 0; }

/* ── Inline bay actions: three outlined cells, one grammar ───────────────── */
.btn-dl-slot[b-6z247pq4c3],
.btn-heart-slot[b-6z247pq4c3],
.btn-reroll-slot[b-6z247pq4c3] {
    background: transparent;
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    color: var(--steel-dark);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    padding: 0.22rem 0.45rem;
    cursor: pointer;
    transition: border-color var(--motion-fast), color var(--motion-fast);
    flex-shrink: 0;
}

.btn-dl-slot:hover[b-6z247pq4c3],
.btn-heart-slot:hover[b-6z247pq4c3],
.btn-reroll-slot:hover:not(:disabled)[b-6z247pq4c3] { border-color: var(--amber); color: var(--amber); background: transparent; }

.btn-dl-slot:focus-visible[b-6z247pq4c3],
.btn-heart-slot:focus-visible[b-6z247pq4c3],
.btn-reroll-slot:focus-visible[b-6z247pq4c3] { outline: 2px solid var(--amber); outline-offset: 2px; }

/* A kept variation is a confirmed departure: the flap turns amber and stays. */
.btn-heart-slot--active[b-6z247pq4c3] {
    color: var(--flap-black);
    border-color: var(--amber);
    background: var(--amber);
}

.btn-heart-slot--active:hover[b-6z247pq4c3] { color: var(--flap-black); background: var(--flap-white); border-color: var(--flap-white); }

.btn-reroll-slot:disabled[b-6z247pq4c3] { opacity: 0.5; cursor: not-allowed; }

/* ── Re-roll panel: a supplementary service, shown under the main board ──── */
.bulk-reroll-panel[b-6z247pq4c3] {
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: var(--spacing-md);
    box-shadow: var(--shadow-md);
}

.bulk-reroll-grid[b-6z247pq4c3] { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 1px; }

.bulk-reroll-cell[b-6z247pq4c3] {
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    border: 1px solid transparent;
    border-radius: 0;
    overflow: hidden;
    transition: border-color var(--motion-fast);
}

.bulk-reroll-cell:hover[b-6z247pq4c3] { border-color: var(--steel-dark); }
.bulk-reroll-cell img[b-6z247pq4c3] { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block; }
/* /Pages/BulkGenerate/BulkPromptEditor.razor.rz.scp.css */
/* Scoped CSS for BulkPromptEditor.razor.

   Ten prompts, ten numbered rows — the same manifest shape as the board they feed.
   `--color-text` was never defined anywhere in this project, so every rule below
   that used it was rendering with the inherited colour by accident; they now name
   real tokens. The light-mode code chip (#eff6ff on #bfdbfe) is replaced by a dark
   flap cell, which is the only place a token placeholder appears in this world. */

.prompt-editor[b-vuenerq3up] {
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    overflow: hidden;
}

/* The editor's steel rail, matching every other titled panel. */
.prompt-editor__header[b-vuenerq3up] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.35rem;
    padding: 0.5rem 0.85rem;
    border-bottom: 1px solid rgba(0,0,0,0.6);
    background: var(--steel-face);
}

.prompt-editor__title[b-vuenerq3up] {
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: #14161a;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
    margin: 0;
    white-space: nowrap;
}

.prompt-editor__actions[b-vuenerq3up] { display: flex; gap: 1px; flex-shrink: 0; }
.prompt-editor__actions .btn .d-action-label[b-vuenerq3up] { display: inline; }

.prompt-editor__token-hint[b-vuenerq3up] {
    width: 100%;
    font-size: var(--text-xs);
    color: #2b2f36;
    padding: 0.25rem 0;
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

.prompt-editor__token-hint code[b-vuenerq3up] {
    background: var(--flap-black);
    color: var(--amber);
    border: 1px solid rgba(0,0,0,0.5);
    border-radius: 0;
    padding: 0.1rem 0.4rem;
    font-size: var(--text-xs);
    font-weight: 600;
}

.prompt-editor__list[b-vuenerq3up] {
    padding: 0.75rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
    gap: 0.4rem;
}

/* Single column below the two-up threshold — a 320px minimum would otherwise
   force a horizontal scroll on the narrowest phones. */
@media (max-width: 639.98px) {
    .prompt-editor__list[b-vuenerq3up] { grid-template-columns: 1fr; }
}

.prompt-editor__item[b-vuenerq3up] { display: flex; align-items: flex-start; gap: 0.5rem; }

/* Row numbers are the board's position column: tabular, in their own cell. */
.prompt-editor__num[b-vuenerq3up] {
    width: 1.6rem;
    height: 1.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    margin-top: 0.4rem;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--steel-dark);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    text-align: center;
    padding: 0;
}

.prompt-editor__textarea[b-vuenerq3up] {
    flex: 1;
    background: var(--flap-shadow);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    box-shadow: var(--inset-cell);
    color: var(--flap-white);
    font-family: var(--font-ui);
    font-size: var(--text-md);
    padding: 0.5rem 0.7rem;
    resize: vertical;
    min-height: 56px;
    transition: border-color var(--motion-fast);
    line-height: 1.5;
}

.prompt-editor__textarea:focus[b-vuenerq3up] {
    outline: none;
    border-color: var(--amber);
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell), 0 0 0 1px var(--amber);
}

.btn-reset-row[b-vuenerq3up] {
    background: transparent;
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    color: var(--steel-dark);
    font-size: var(--text-xs);
    width: 26px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex-shrink: 0;
    margin-top: 0.45rem;
    transition: color var(--motion-fast), border-color var(--motion-fast);
}

.btn-reset-row:hover[b-vuenerq3up] { background: transparent; border-color: var(--amber); color: var(--amber); }
.btn-reset-row:focus-visible[b-vuenerq3up] { outline: 2px solid var(--amber); outline-offset: 2px; }
/* /Pages/Login.razor.rz.scp.css */
/* Scoped styles for Login.razor — the ticket window before the concourse.

   The card is a steel-framed board with a single dark bay in it. The old 3px
   gradient stripe across the top is gone: a coloured hairline above a panel is
   the AI side-tab tell, and this world already marks a panel by framing it. */

.login-page[b-ah696wlyti] {
    min-height: 80vh;
    display: flex;
    align-items: center;
    justify-content: center;
}

.login-card[b-ah696wlyti] {
    background: var(--flap-black);
    border: 1px solid var(--steel-dark);
    border-radius: 0;
    padding: 0;
    width: 100%;
    max-width: 400px;
    box-shadow: var(--shadow-xl);
    position: relative;
    overflow: hidden;
}

/* The card's own steel rail carries the title, matching every other board. */
.login-card__rail[b-ah696wlyti] {
    background: var(--steel-face);
    border-bottom: 1px solid rgba(0,0,0,0.6);
    padding: 0.7rem 1.1rem;
}

.login-card__body[b-ah696wlyti] { padding: 1.5rem 1.5rem 1.75rem; }

.login-title[b-ah696wlyti] {
    font-family: var(--font-flap);
    font-size: var(--text-md);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    margin: 0;
    color: #14161a;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
}

.login-subtitle[b-ah696wlyti] {
    font-size: var(--text-md);
    color: var(--steel);
    margin: 0 0 1.25rem;
    line-height: 1.55;
}

/* Microsoft sign-in keeps its own brand plate — a white flap, deliberately the
   one light cell on the page, because it represents someone else's identity. */
.btn-ms-signin[b-ah696wlyti] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    background: var(--flap-white);
    color: #14161a;
    border: 1px solid var(--flap-white);
    border-radius: 0;
    padding: 0.7rem 1rem;
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    text-decoration: none;
    transition: background var(--motion-fast), border-color var(--motion-fast);
}

.btn-ms-signin:hover[b-ah696wlyti] {
    background: var(--amber);
    border-color: var(--amber);
    color: var(--flap-black);
}

.login-divider[b-ah696wlyti] {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    color: var(--steel-dark);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.2em;
    margin: 1.25rem 0;
}

.login-divider[b-ah696wlyti]::before,
.login-divider[b-ah696wlyti]::after {
    content: '';
    flex: 1;
    height: 1px;
    background: var(--rule-strong);
}

.btn-anon-signin[b-ah696wlyti] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    background: transparent;
    color: var(--steel);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: 0.7rem 1rem;
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    text-decoration: none;
    transition: border-color var(--motion-fast), color var(--motion-fast);
}

.btn-anon-signin:hover[b-ah696wlyti] {
    background: transparent;
    border-color: var(--amber);
    color: var(--amber);
}

/* A guest pass is provisional work, so it takes amber — the working colour. */
.anon-badge[b-ah696wlyti] {
    font-family: var(--font-flap);
    font-size: var(--text-3xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--flap-black);
    background: var(--amber);
    border: none;
    border-radius: 0;
    padding: 0.1rem 0.35rem;
    margin-left: 0.4rem;
}
/* /Pages/NotFound.razor.rz.scp.css */
/* Scoped styles for NotFound.razor. Inline styles are forbidden (NET_RULES §4), so the
   404's composition lives here.

   The oversized de-emphasised glyph is gone: this world announces a dead route the same
   way the board announces a dropped service — with the word CANCELLED in red, then the
   destination that isn't running. That is more legible than a faded 4rem icon and it
   reuses a status vocabulary the user has already seen on every other page. */

.not-found[b-v2en5wj55g] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.85rem;
    max-width: 46ch;
    margin: 0 auto;
    padding: 1.5rem;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    box-shadow: var(--shadow-md);
}

.not-found__title[b-v2en5wj55g] { margin: 0; }

.not-found__hint[b-v2en5wj55g] {
    margin: 0;
    color: var(--steel);
    font-size: var(--text-lg);
    line-height: 1.6;
}

.not-found__back[b-v2en5wj55g] { align-self: flex-start; text-decoration: none; }
/* /Pages/RapRoast.razor.rz.scp.css */
/* Scoped styles for RapRoast.razor.

   Every colour resolves from the board tokens. The previous sheet hard-coded slate
   greys (#475569, #334155) that were computed for a light page; on flap-black they
   fall to ~2:1. Contrast floors used here, all against --flap-shadow #1B1B1E:
     --flap-white 17:1 · --steel 9.4:1 · --steel-dark 4.8:1 · --amber 10:1

   The `border-left: 4px` accent on .roast-refused is gone. A thick coloured edge on
   a callout is the AI side-tab tell; the refusal now reads as a full amber-framed
   cell, which is also how every other state in this world announces itself. */

.roast-disclaimer[b-er5f24d7zj] { margin-top: 0.75rem; font-size: var(--text-xs); color: var(--steel-dark); }

/* Explicit toggle. A parental-advisory sticker rather than a switch, because that is the
   universally read symbol for exactly this setting — and it earns the row without adding
   another lamp that would read as a fourth intensity stop. */
.roast-explicit[b-er5f24d7zj] {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-top: 0.75rem;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--rule-strong);
    background: var(--flap-black);
    cursor: pointer;
}

.roast-explicit input[b-er5f24d7zj] { accent-color: var(--amber); width: 1rem; height: 1rem; flex: none; }

.roast-explicit__badge[b-er5f24d7zj] {
    flex: none;
    display: grid;
    place-items: center;
    width: 1.35rem;
    height: 1.35rem;
    border: 1px solid var(--steel-dark);
    color: var(--steel-dark);
    font-size: var(--text-xs);
    font-weight: 700;
    line-height: 1;
}

.roast-explicit__text[b-er5f24d7zj] { display: flex; flex-direction: column; line-height: 1.25; }
.roast-explicit__name[b-er5f24d7zj] { color: var(--steel); font-size: var(--text-sm); }
.roast-explicit__meta[b-er5f24d7zj] { color: var(--steel-dark); font-size: var(--text-xs); }

.roast-explicit--on[b-er5f24d7zj] { border-color: var(--amber); }
.roast-explicit--on .roast-explicit__badge[b-er5f24d7zj] { border-color: var(--amber); color: var(--amber); }
.roast-explicit--on .roast-explicit__name[b-er5f24d7zj] { color: var(--flap-white); }

/* Filter report. Deliberately unalarming when clean — a green-on-black "0%" is a receipt,
   not a warning, and only the amber frame appears once a gate has actually bitten. */
.roast-filters[b-er5f24d7zj] {
    margin-top: 0.75rem;
    padding: 0.65rem 0.75rem;
    border: 1px solid var(--rule-strong);
    background: var(--flap-black);
}

.roast-filters--hit[b-er5f24d7zj] { border-color: var(--amber); }

.roast-filters__headline[b-er5f24d7zj] {
    margin: 0 0 0.5rem;
    font-size: var(--text-xs);
    color: var(--steel);
}

.roast-filters__pct[b-er5f24d7zj] {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--steel);
    margin-right: 0.3rem;
}

.roast-filters--hit .roast-filters__pct[b-er5f24d7zj] { color: var(--amber); }

.roast-filters__list[b-er5f24d7zj] { list-style: none; margin: 0; padding: 0; }

.roast-filters__row[b-er5f24d7zj] {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    align-items: baseline;
    padding: 0.2rem 0;
    font-size: var(--text-xs);
    color: var(--steel-dark);
    border-top: 1px solid var(--rule);
}

.roast-filters__gate[b-er5f24d7zj] { color: var(--steel); min-width: 6.5rem; }
.roast-filters__verdict[b-er5f24d7zj] { margin-left: auto; text-transform: uppercase; letter-spacing: 0.05em; }
.roast-filters__row.is-rejected .roast-filters__verdict[b-er5f24d7zj] { color: var(--amber); }
.roast-filters__row.is-passed .roast-filters__verdict[b-er5f24d7zj] { color: var(--steel-dark); }

.roast-filters__session[b-er5f24d7zj] {
    margin: 0.5rem 0 0;
    font-size: var(--text-xs);
    color: var(--steel-dark);
}

.roast-player[b-er5f24d7zj] {
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: 1rem;
}

.roast-player__audio[b-er5f24d7zj] { width: 100%; }

/* A refusal is a cancelled row, so it takes the cancelled colour — no side tab. */
.roast-refused[b-er5f24d7zj] {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    border: 1px solid var(--cancel-red);
    border-radius: 0;
    padding: 0.85rem 1rem;
    color: var(--cancel-red-tx);
}

.roast-softened[b-er5f24d7zj] { margin-top: 0.65rem; color: var(--steel); }

.roast-lyrics[b-er5f24d7zj] {
    margin-top: 1.25rem;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: 1rem 1.25rem;
}

.roast-lyrics__heading[b-er5f24d7zj] {
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
    margin-bottom: 0.6rem;
}

/* Lyrics are pre-formatted: section tags and line breaks carry meaning, so whitespace
   is preserved, but long bars still wrap rather than scrolling the page sideways. */
.roast-lyrics__text[b-er5f24d7zj] {
    white-space: pre-wrap;
    word-break: break-word;
    font-family: var(--font-ui);
    font-size: var(--text-lg);
    line-height: 1.7;
    color: var(--flap-white);
    max-width: 60ch;
    margin: 0;
}

.roast-seen[b-er5f24d7zj] { margin-top: 1rem; font-size: var(--text-md); }

.roast-seen summary[b-er5f24d7zj] {
    cursor: pointer;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
}

.roast-seen summary:hover[b-er5f24d7zj] { color: var(--amber); }

.roast-subject[b-er5f24d7zj] {
    margin: 0 0 1rem;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: 0.75rem;
    text-align: center;
}

/* Capped by viewport height as well as width: a tall portrait photo would otherwise
   push the audio player and the bars entirely below the fold. */
.roast-subject__img[b-er5f24d7zj] {
    max-width: 100%;
    max-height: 340px;
    width: auto;
    border-radius: 0;
    display: block;
    margin: 0 auto;
    background: var(--flap-shadow);
}

.roast-subject__caption[b-er5f24d7zj] {
    margin-top: 0.5rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
}

.roast-seen__thin[b-er5f24d7zj] { margin-top: 0.5rem; font-size: var(--text-xs); color: var(--steel); }

/* Structured scene slots. A definition list is the correct semantic — each label
   genuinely describes its value — and it gives screen readers the pairing for free.
   Visually it is the board's two-column manifest. */
.scene-slots[b-er5f24d7zj] {
    display: grid;
    grid-template-columns: minmax(90px, auto) 1fr;
    gap: 0.4rem 0.85rem;
    margin: 0.5rem 0 0;
    font-size: var(--text-sm);
}

.scene-slots dt[b-er5f24d7zj] {
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
}

.scene-slots dd[b-er5f24d7zj] { margin: 0; color: var(--steel); }

/* OCR lines are quoted verbatim off the image, so they sit in monospaced cells —
   measurement, not costume: this is literal text read out of the photograph. */
.scene-slots__chip[b-er5f24d7zj] {
    display: inline-block;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    border: none;
    border-radius: 0;
    padding: 0.1rem 0.4rem;
    margin: 0 0.25rem 0.25rem 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: var(--text-xs);
    color: var(--flap-white);
}

@media (max-width: 479.98px) {
    .scene-slots[b-er5f24d7zj] { grid-template-columns: 1fr; gap: 0.1rem 0; }
    .scene-slots dd[b-er5f24d7zj] { margin-bottom: 0.5rem; }
}

/* ── Sync nudge ──────────────────────────────────────────────────────────────
   Sits with the player, not the lyrics, because it corrects the track's alignment
   rather than the words. Only rendered once the karaoke driver reports it is live. */

.roast-sync[b-er5f24d7zj] {
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 0.5rem 0.85rem;
    margin-top: 0.85rem;
    padding-top: 0.85rem;
    border-top: 1px solid var(--rule);
}

.roast-sync__label[b-er5f24d7zj] {
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
    white-space: nowrap;
}

.roast-sync__value[b-er5f24d7zj] { display: block; color: var(--amber); letter-spacing: 0.08em; }
.roast-sync__range[b-er5f24d7zj] { width: 100%; accent-color: var(--amber); }
.roast-sync__range:focus-visible[b-er5f24d7zj] { outline: 2px solid var(--amber); outline-offset: 3px; }

.roast-sync__hint[b-er5f24d7zj] {
    grid-column: 1 / -1;
    font-size: var(--text-xs);
    line-height: 1.5;
    color: var(--steel-dark);
}

/* ── The script ──────────────────────────────────────────────────────────────
   One bar per row, each a button that seeks the track. The lit row is amber; rows
   already performed drop to steel-dark (4.8:1 on the cell — still AA for body text)
   so the eye lands on what is being said now without the past going unreadable. */

.roast-script[b-er5f24d7zj] {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0 0;
    max-height: 22rem;
    overflow-y: auto;
    scrollbar-width: thin;
}

.roast-script__row[b-er5f24d7zj] { margin: 0; }

.roast-script__line[b-er5f24d7zj] {
    display: block;
    width: 100%;
    text-align: left;
    background: none;
    border: none;
    border-left: 2px solid transparent;
    padding: 0.22rem 0.6rem;
    font-family: var(--font-ui);
    font-size: var(--text-lg);
    line-height: 1.65;
    color: var(--steel);
    cursor: pointer;
    transition: color var(--motion-fast), border-color var(--motion-fast);
}

.roast-script__line:disabled[b-er5f24d7zj] { cursor: default; color: var(--flap-white); }
.roast-script__line:not(:disabled):hover[b-er5f24d7zj] { color: var(--flap-white); border-left-color: var(--steel-dark); }
.roast-script__line:focus-visible[b-er5f24d7zj] { outline: 2px solid var(--amber); outline-offset: -2px; }
.roast-script__line.is-past[b-er5f24d7zj] { color: var(--steel-dark); }

.roast-script__line.is-active[b-er5f24d7zj] {
    color: var(--amber);
    border-left-color: var(--amber);
    font-weight: 600;
}

.roast-script__section[b-er5f24d7zj] {
    margin: 0.9rem 0 0.25rem;
    padding: 0 0.6rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.18em;
    color: var(--steel-dark);
}

.roast-script__section:first-child[b-er5f24d7zj] { margin-top: 0; }

/* ── Export ──────────────────────────────────────────────────────────────────
   The recording is a real-time capture, so the progress bar is measuring playback,
   not encoding. The copy says so — a bar that looks like a normal export would read
   as hung when it is simply listening to a 30-second track. */

.roast-export[b-er5f24d7zj] {
    margin-top: 1rem;
    padding: 0.85rem 1rem;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
}

.roast-export__label[b-er5f24d7zj] {
    display: block;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
    margin-bottom: 0.6rem;
}

.roast-export__actions[b-er5f24d7zj] { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.roast-export__progress[b-er5f24d7zj] { margin-top: 0.75rem; }

.roast-export__bar[b-er5f24d7zj] {
    height: 4px;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
}

.roast-export__bar[b-er5f24d7zj]::before {
    content: "";
    display: block;
    height: 100%;
    width: var(--export, 0%);
    background: var(--amber);
    transition: width var(--motion-base) linear;
}

.roast-export__pct[b-er5f24d7zj] {
    display: block;
    margin-top: 0.4rem;
    font-size: var(--text-xs);
    color: var(--steel);
}

@media (prefers-reduced-motion: reduce) {
    .roast-export__bar[b-er5f24d7zj]::before { transition: none; }
}
/* /Shared/AiServicePicker.razor.rz.scp.css */
/* Scoped CSS for AiServicePicker.razor — the routing panel.

   This is where the product tells you which machine handles each step, so it is
   built as the board's patch list: one ruled row per capability, label left,
   selected service right. PRODUCT.md's third principle (cost and execution
   location stay visible) is why this panel is legible rather than tucked away. */

.ai-picker[b-dx24rw2mip] {
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0;
    background: var(--flap-black);
    box-shadow: var(--shadow-md);
}

.ai-picker__legend[b-dx24rw2mip] {
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: #14161a;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
    background: var(--steel-face);
    border-bottom: 1px solid rgba(0,0,0,0.6);
    padding: 0.5rem 0.85rem;
    width: 100%;
    float: none;
}

/* Label above selector, always. These option strings are long ("Azure Computer Vision —
   Fastest, uses your API key"), and a side-by-side grid truncated every one of them to
   "Azure Computer Visi⌄" inside the Studio's 360px column. A stacked row costs one line
   and shows the whole value, which is the point of disclosing the provider at all. */
.ai-picker__row[b-dx24rw2mip] {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0.2rem;
    padding: 0.45rem 0.85rem;
    border-bottom: 1px solid var(--rule);
}

.ai-picker__row:last-of-type[b-dx24rw2mip] { border-bottom: none; }

.ai-picker__label[b-dx24rw2mip] {
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
}

.ai-picker__select[b-dx24rw2mip] {
    width: 100%;
    min-width: 0;
    text-overflow: ellipsis;
    padding: 0.4rem 0.6rem;
    font-family: var(--font-ui);
    font-size: var(--text-sm);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--flap-white);
    transition: border-color var(--motion-fast);
}

.ai-picker__select:hover[b-dx24rw2mip] { border-color: var(--steel-dark); }

.ai-picker__select:focus-visible[b-dx24rw2mip] {
    outline: 2px solid var(--amber);
    outline-offset: 1px;
    border-color: var(--amber);
}

.ai-picker__select:disabled[b-dx24rw2mip] {
    background: var(--flap-black);
    color: var(--steel-dark);
    cursor: not-allowed;
}

/* Where the model actually runs. Amber, because execution location is the
   thing this panel exists to disclose. */
.ai-picker__device[b-dx24rw2mip] {
    margin: 0;
    padding: 0.35rem 0.85rem 0.6rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--amber);
}

/* ── Single-provider capabilities ─────────────────────────────────────────────
   Stated on one collapsed line instead of four disabled selects. See the component
   for why: a control that cannot be operated is a fact, and facts do not need a
   full-width form control each. */
.ai-picker__fixed[b-dx24rw2mip] {
    border-top: 1px solid var(--color-border, rgba(255,255,255,0.08));
    margin-top: 0.35rem;
    padding-top: 0.4rem;
}

.ai-picker__fixed-summary[b-dx24rw2mip] {
    cursor: pointer;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--steel);
    padding: 0.5rem 0.25rem;
    list-style: none;
}

.ai-picker__fixed-summary[b-dx24rw2mip]::-webkit-details-marker { display: none; }

.ai-picker__fixed-summary[b-dx24rw2mip]::before {
    content: "▸ ";
    color: var(--amber);
}

.ai-picker__fixed[open] .ai-picker__fixed-summary[b-dx24rw2mip]::before { content: "▾ "; }

.ai-picker__fixed-summary:hover[b-dx24rw2mip] { color: var(--flap-white); }

.ai-picker__fixed-summary:focus-visible[b-dx24rw2mip] {
    outline: 2px solid var(--amber);
    outline-offset: 2px;
}

.ai-picker__fixed-list[b-dx24rw2mip] {
    list-style: none;
    margin: 0;
    padding: 0.15rem 0.25rem 0.35rem;
}

.ai-picker__fixed-item[b-dx24rw2mip] {
    display: flex;
    justify-content: space-between;
    gap: 0.75rem;
    padding: 0.3rem 0;
    font-size: var(--text-xs);
    line-height: 1.3;
}

.ai-picker__fixed-label[b-dx24rw2mip] {
    color: var(--steel);
    text-transform: uppercase;
    font-family: var(--font-flap);
    letter-spacing: 0.08em;
}

.ai-picker__fixed-value[b-dx24rw2mip] { color: var(--flap-white); text-align: right; }
/* /Shared/FeatureShell.razor.rz.scp.css */
/* The three measures, as CSS Grid rather than a 12-column flex grid.

   Grid is the right tool here and the old markup was using the wrong one: a
   `row`/`col-lg-7 col-md-9` pair expresses "seven twelfths at large, nine twelfths
   at medium", which is a proportion of a container nobody chose. What the page
   actually wants is "as wide as reads well, centred" — a measure, which `max-width`
   states directly and a column count only approximates. */

.feature-shell[b-cq3qcttah0] {
    /* One padding decision for every feature page. MainLayout no longer adds its own,
       so this is the only vertical rhythm at the top of a feature. */
    width: 100%;
    max-width: var(--rail-max);
    margin-inline: auto;
    padding-inline: var(--space-3);
}

/* ── Focused ──────────────────────────────────────────────────────────────
   Setting the column from the text it holds — instead of from a twelfth of a
   viewport — is why this stays comfortable from a 1280px laptop to a 4K monitor,
   where `col-lg-7` would have grown to a 900px line nobody can track.

   The measure is on the SHELL, not on the body: constraining only the body left the
   page title ranged full-width above a centred column, an L-shape with the heading
   detached from the thing it titles. A measure is a column of content, and the
   heading is the top of that column. */
.feature-shell--focused[b-cq3qcttah0] {
    max-width: 46rem;
}

/* ── Split ────────────────────────────────────────────────────────────────
   The rail is sized to its contents (an upload panel and its controls), not to a
   fraction; the workspace takes the rest. minmax(0, 1fr) rather than 1fr because a
   grid item's automatic minimum size is its content, and a wide result image inside
   the workspace would otherwise push the whole layout past the viewport. */
.feature-shell__split[b-cq3qcttah0] {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 1024px) {
    .feature-shell__split[b-cq3qcttah0] { grid-template-columns: minmax(280px, 22rem) minmax(0, 1fr); }
}

.feature-shell__rail[b-cq3qcttah0] { min-width: 0; }
.feature-shell__work[b-cq3qcttah0] { min-width: 0; }

/* ── Board ────────────────────────────────────────────────────────────────
   Full width. Nothing to constrain: the content is a grid of its own. */
.feature-shell--board .feature-shell__body[b-cq3qcttah0] { max-width: none; }

/* ── Vertical rhythm ─────────────────────────────────────────────────────── */
.feature-shell[b-cq3qcttah0] { padding-block: var(--space-4) var(--space-5); }

@media (max-width: 639.98px) {
    .feature-shell[b-cq3qcttah0] { padding-inline: var(--space-2); padding-block: var(--space-3) var(--space-4); }
}

/* Room for the fixed action rail, so the last control on the page is never parked
   underneath it. Applied unconditionally on small screens rather than via a marker
   class the way it used to be — a page that forgets the class gets an unreachable
   control, and that is not a failure worth leaving available. */
@media (max-width: 767.98px) {
    .feature-shell:has(~ .mobile-sticky-cta)[b-cq3qcttah0] { padding-bottom: 5rem; }
}
/* /Shared/MyImagesGallery.razor.rz.scp.css */

/* Gallery unreachable — the amber warning state. Distinct from the empty state next to it,
   because "no images yet" and "cannot reach your images" must not look the same. */
.gallery-bar--error[b-roaeppb5wj] { border-color: var(--amber-dim, rgba(255,180,0,0.4)); }

.gallery-bar__empty-hint--error[b-roaeppb5wj] { color: var(--amber); }
/* /Shared/StyleRecipeDrawer.razor.rz.scp.css */
/* Scoped CSS for StyleRecipeDrawer.razor.

   The drawer is a single card that flips between collapsed and expanded. Recipe cards
   inside the grid are <button> elements (not <div @onclick>) so they get keyboard
   activation, focus ring and disabled styling for free — see the fix that converted
   them from clickable divs. */

.style-recipe-drawer[b-qt1vx4dmui] { transition: border-color 0.2s; }

.recipe-card[b-qt1vx4dmui] {
    cursor: pointer;
    transition: transform 0.15s ease, border-color 0.15s ease;
    text-align: left;
    width: 100%;
    /* Override Bootstrap's <button> defaults that would otherwise reset typography and
       introduce a blue focus ring. The flap card surface is the right focus target. */
    color: inherit;
    font: inherit;
}

.recipe-card:hover[b-qt1vx4dmui] { transform: translateY(-2px); border-color: var(--bs-warning) !important; }
.recipe-card:focus-visible[b-qt1vx4dmui] { outline: 2px solid var(--amber); outline-offset: 2px; }

/* The recipe grid scrolls inline when the catalog grows. max-height keeps it inside the
   drawer so the category pills stay anchored at the top. */
.recipe-grid-scroll[b-qt1vx4dmui] { max-height: 380px; overflow-y: auto; }

.recipe-desc[b-qt1vx4dmui] {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.25;
}

.recipe-icon[b-qt1vx4dmui] {
    width: 36px;
    height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.cursor-pointer[b-qt1vx4dmui] { cursor: pointer; }
.extra-small[b-qt1vx4dmui] { font-size: 0.72rem; }
