/* ═══════════════════════════════════════════════════════════════════════════
   PoRedoImage — COMPONENTS
   Seed key 11888fdd. The world is a railway departure board: matte flap faces in
   ruled cells, brushed-steel frames, paint-white condensed caps, amber for work in
   progress, red for failure.

   Tokens, reset, grid and utilities live in css/foundation.css and load first.
   This sheet is the board's own furniture and nothing else.

   Rules this sheet holds to, so later edits do not drift out of the world:
     · Corners are square. --radius-* exist only because older markup names them.
     · No glass, no backdrop blur as decoration, no gradient text, no colored glow.
     · Steel and lamp are the only gradients, and both are material, not ornament.
     · Status is carried by four values only: STEEL=queued, AMBER=working,
       WHITE=done, RED=failed. Never invent a fifth.
     · One authored motion: the flap cascade. Everything else is a 120ms state change.
     · Every font-size is a --text-* step. Every breakpoint is one of the four.
     · No !important. The cascade layers below are what make that possible: a
       component rule beats a utility because of its LAYER, not its specificity.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer components {
/* ═══ Materials ══════════════════════════════════════════════════════════
   Three reusable surfaces. Everything else is composed from these.        */
/* A steel-framed board panel. */
.board,
.upload-panel,
.card,
.diag-stat,
.shimmer-wrapper,
.gallery-bar {
    position: relative;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    box-shadow: var(--shadow-md);
}

/* The steel top rail that names a board. */
.card-header {
    background: var(--steel-face);
    color: #14161a;
    font-family: var(--font-flap);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-size: var(--text-sm);
    padding: 0.55rem 0.9rem;
    border: none;
    border-bottom: 1px solid rgba(0,0,0,0.6);
    border-radius: 0;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
}

/* ═══ Flap character cells ═══════════════════════════════════════════════
   FlapText renders one .flap per character. The cascade below is this
   build's single authored motion moment: characters settle in sequence,
   from an already-visible default, with an exponential ease-out.          */
/* Cells wrap. They must: flap cells are fixed-width, so a long destination like
   "IMAGE REGENERATION" is ~330px of cells and ran off the right edge of a 390px
   phone viewport. A real board wraps or splits a long name too — what it never
   does is let the row scroll sideways. */
.flapstrip {
    display: inline-flex;
    flex-wrap: wrap;
    gap: 2px;
    row-gap: 3px;
    min-width: 0;
    font-family: var(--font-flap);
    font-weight: 700;
    text-transform: uppercase;
    line-height: 1;
    vertical-align: baseline;
}

.flap {
    --i: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 0.72em;
    padding: 0.16em 0.06em;
    background: var(--flap-shadow);
    color: var(--flap-white);
    box-shadow: var(--inset-cell);
    /* The split across the middle of every physical flap. */
    background-image: linear-gradient(to bottom,
        transparent calc(50% - 1px),
        var(--cell-split) calc(50% - 1px),
        var(--cell-split) calc(50% + 1px),
        transparent calc(50% + 1px));
    animation: flapSettle 320ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
    animation-delay: calc(var(--i) * 26ms);
    transform-origin: center top;
}

.flap--gap {
    background: none;
    box-shadow: none;
    background-image: none;
    min-width: 0.34em;
    animation: none;
}

@keyframes flapSettle {
    from { transform: rotateX(-72deg); filter: brightness(0.35); }
    to   { transform: rotateX(0deg);   filter: brightness(1); }
}

@media (prefers-reduced-motion: reduce) {
/* The cascade collapses to a single update, per the world's own rule. */
.flap { animation: none; }

}

/* Display-scale flaps, for the brand and page titles. */
.flapstrip--display .flap { font-size: clamp(1.1rem, 5.2vw, 2rem); min-width: 0.76em; }

.flapstrip--sm .flap { font-size: var(--text-xs); padding: 0.2em 0.14em; }

/* ═══ Status vocabulary ══════════════════════════════════════════════════
   Four values, one visual law. Status never relies on colour alone: each
   carries its own word, so the row reads correctly in monochrome too.     */
.status {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-family: var(--font-flap);
    font-weight: 700;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    padding: 0.2rem 0.5rem;
    border: 1px solid currentColor;
    white-space: nowrap;
}

.status--queued { color: var(--steel-dark); }

.status--working { color: var(--amber); }

.status--done { color: var(--flap-white); }

.status--failed { color: var(--cancel-red-tx); border-color: var(--cancel-red); }

/* The lamp beside a working row. Inline-block so it also works inside a
   paragraph, not only inside a flex row. */
.lamp {
    display: inline-block;
    vertical-align: middle;
    width: 10px;
    height: 10px;
    flex: 0 0 auto;
    background: var(--lamp-dark);
}

.lamp--lit {
    background: var(--lamp-lit);
    animation: lampPulse 1.4s steps(2, end) infinite;
}

@keyframes lampPulse { 0%, 60% { opacity: 1; } 61%, 100% { opacity: 0.42; } }

/* Column labels above a set of rows — the board's header strip. The template matches
   .studio-feature-card exactly (34px glyph bay, name, status) so SERVICE sits over the
   name and STATUS over the status, rather than approximately near them. */
.row-head {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr) auto;
    gap: 0.85rem;
    padding: 0.4rem 0.9rem;
    border-bottom: 1px solid var(--rule-strong);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--steel-dark);
}

/* ═══ Chrome: header, nav, footer ════════════════════════════════════════
   The mock-data banner must live in this global sheet: MockDataBanner is a child
   component, so MainLayout's scoped stylesheet cannot reach it and it would render
   as an unstyled white bar across the top of every page.

   It is amber because running on mocks is a provisional state — the same meaning
   amber carries everywhere else in this world. The detail list is clamped to one
   line so six mock service names cannot eat a third of a phone viewport. */
.mock-data-banner {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 0.4rem;
    background: var(--amber);
    color: var(--flap-black);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    padding: 0.3rem 0.9rem;
    text-align: center;
}

.mock-data-banner i { flex: 0 0 auto; }

.mock-data-banner__details {
    font-weight: 600;
    letter-spacing: 0.1em;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* On a phone the banner states the condition; the service list is detail the
   header has no room for. */
@media (max-width: 639.98px) {
.mock-data-banner__details { display: none; }

}

/* The rail is BRUSHED STEEL — a light surface. --steel (#B6BBC2) is a board grey meant
   for the dark flap faces; on the rail's own gradient it measures 1.3:1 at the light end
   and 2.4:1 at the middle, so "SIGN OUT" and the feature links were washed out at every
   width. The rail's ink is --steel-ink, which is what MainLayout.razor.css already used
   for the same elements — but that sheet is scoped to MainLayout and cannot reach anchors
   NavMenu and LoginDisplay render, so this global rule won and undid it. */
.app-nav__link {
    color: var(--steel-ink);
    text-decoration: none;
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    padding: 0.5rem 0.75rem;
    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.35rem;
    line-height: 1;
    white-space: nowrap;
    box-shadow: none;
}

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

/* Active gets the amber it always had, but on the dark cell rather than bare steel:
   amber on light steel is 1.9:1 and the current page is the one label that must be
   unmistakable. The cell is the board's own way of lighting a value. */
.app-nav__link.active {
    background: var(--flap-black);
    color: var(--amber);
    border-bottom-color: var(--amber);
    box-shadow: none;
}

/* Rail controls sit on a 54-60px bar, so they cannot be 44px tall without filling it.
   The tap target is extended past the painted box instead — the pointer/touch area is
   what the guideline is about, not the ink. */
.app-nav__link::after {
    content: "";
    position: absolute;
    inset: 50% 0 auto 0;
    height: 44px;
    transform: translateY(-50%);
}

.app-nav__link { position: relative; }

/* ── Skip link ───────────────────────────────────────────────────────────────
   Hidden until focused, then a real flap cell at the head of the rail. Every page
   put six or more rail stops in front of its own first control without it. */
.skip-link {
    position: absolute;
    left: 0.5rem;
    top: -100%;
    z-index: 2000;
    background: var(--flap-black);
    color: var(--amber);
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    padding: 0.75rem 1rem;
    border: 1px solid var(--amber);
    transition: top var(--motion-fast);
}

.skip-link:focus-visible {
    top: 0.5rem;
    outline: 2px solid var(--amber);
    outline-offset: 2px;
}

/* The skip target must not paint a focus ring when it receives focus programmatically —
   it is a landmark, not a control. */
#main-content:focus { outline: none; }

.app-nav__brand {
    color: var(--flap-white);
    text-decoration: none;
    background: transparent;
    box-shadow: none;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.app-nav__brand:hover { color: var(--flap-white); }

.app-nav__brand:hover .flap { color: var(--amber); }

.app-nav__user {
    /* Same steel-surface rule as .app-nav__link above: board greys are illegal on the rail. */
    color: var(--steel-ink);
    font-size: var(--text-xs);
    font-family: var(--font-flap);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    max-width: 200px;
}

.drop-zone__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    flex: 0 0 auto;
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--amber);
    font-size: var(--text-xl);
}

.feature-page-title,
.diag-title {
    min-width: 0;
    font-family: var(--font-flap);
    font-size: clamp(1.4rem, 5.5vw, 2.1rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    /* No gradient text — emphasis is weight and scale. */
    color: var(--flap-white);
    background: none;
    -webkit-text-fill-color: currentColor;
    margin: 0;
    line-height: 1.1;
}

/* ═══ Intake ═════════════════════════════════════════════════════════════
   The upload zone is the board's source row: a recessed flap bay waiting
   for a photo to be loaded into it.                                       */
.upload-panel { padding: 0; }

.drop-zone {
    position: relative;
    border: 1px dashed var(--steel-dark);
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    transition: border-color var(--motion-base), background var(--motion-base);
    cursor: pointer;
    overflow: hidden;
    min-height: 132px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.drop-zone--active {
    border-color: var(--amber);
    border-style: solid;
    background: rgba(255,180,0,0.06);
}

.drop-zone:hover { border-color: var(--steel); }

.drop-zone__input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
    z-index: 2;
}

.drop-zone__empty { text-align: center; padding: 1rem; pointer-events: none; }

.drop-zone__icon { margin-bottom: 0.5rem; }

.drop-zone__title {
    font-family: var(--font-flap);
    font-size: var(--text-lg);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--flap-white);
    margin: 0 0 0.35rem;
}

.drop-zone__sub { font-size: var(--text-md); color: var(--steel); margin: 0 0 0.4rem; }

.drop-zone__browse {
    color: var(--amber);
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* Sentences, so sentence case in the UI face. These were 11px tracked caps — the size the
   type scale reserves for labels — and wrapped to three lines in the Bulk rail. */
.drop-zone__formats,
.drop-zone__paste {
    font-size: var(--text-sm);
    color: var(--steel);
    margin: 0.2rem auto 0;
}

.drop-zone__paste kbd {
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    padding: 0.1rem 0.35rem;
    border-radius: 0;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    color: var(--flap-white);
}

.drop-zone--has-image {
    min-height: unset;
    border-style: solid;
    border-color: var(--rule-strong);
}

.drop-zone__preview { position: relative; width: 100%; }

.drop-zone__preview img.preview-image {
    display: block;
    max-width: 100%;
    max-height: 320px;
    width: auto;
    height: auto;
    margin: 0 auto;
    border-radius: 0;
}

.drop-zone__preview-overlay {
    position: absolute;
    inset: 0;
    background: rgba(7,7,8,0.72);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity var(--motion-fast);
}

.drop-zone:hover .drop-zone__preview-overlay,
.drop-zone:focus-within .drop-zone__preview-overlay { opacity: 1; }

.drop-zone__preview-hint {
    font-family: var(--font-flap);
    color: var(--flap-white);
    font-size: var(--text-xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    padding: 0.45rem 0.9rem;
    border-radius: 0;
    pointer-events: none;
}

/* Compact: a loaded photo collapses to a one-line chip. The file input still covers
   the whole chip, so click / drop / paste all replace it. */
.drop-zone--compact { flex: 1; min-width: 0; justify-content: flex-start; }

.drop-zone--compact .drop-zone__preview { display: flex; align-items: center; gap: var(--space-3); padding: 0.35rem; }

.drop-zone--compact .drop-zone__preview img.preview-image { max-height: 64px; max-width: 96px; margin: 0; }

.drop-zone--compact .drop-zone__preview-overlay { position: static; background: none; opacity: 1; justify-content: flex-start; }

.drop-zone--compact .drop-zone__preview-hint { background: none; box-shadow: none; padding: 0; color: var(--steel); }

.drop-zone--compact:hover .drop-zone__preview-hint { color: var(--amber); }

.drop-zone__title--desktop { display: block; }

.drop-zone__title--mobile { display: none; }

.drop-zone__sub--desktop { display: block; }

/* Touch, or a phone-width window: there is no drag source and usually no keyboard, so the
   drag-and-paste instructions are noise. */
@media (pointer: coarse), (max-width: 639.98px) {
.drop-zone__title--desktop { display: none; }

.drop-zone__title--mobile { display: block; }

.drop-zone__sub--desktop { display: none; }
}

/* Window-wide drop target. */
body.po-drop-active::after {
    content: "DROP IMAGE ANYWHERE";
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-flap);
    font-size: clamp(1rem, 3vw, 1.6rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.28em;
    color: var(--amber);
    background: rgba(7,7,8,0.82);
    border: 2px dashed var(--amber);
    pointer-events: none;
}

/* ═══ Choice cells ═══════════════════════════════════════════════════════
   ONE selectable cell. `.flap-choice` is a flap you pick; picking it turns it amber.

   A detail level, a beat style, a roast intensity, a meme template and a style
   direction are the same object wearing five names, and before this they were five
   independent rule sets that had already drifted: the AI-detail presets centred
   their label, the beat styles left-aligned theirs, and the intensity dial invented
   its own lamp column. They now differ only by layout modifier.

   Modifiers:
     --stack  label over meta, centred. Presets, beat styles.
     --lamp   status lamp in a left bay, label and meta stacked beside it.
     --tile   label over category, left-aligned. Template pickers.
     --active picked. Always paired with aria-checked / :checked on the real input.

   Parts: __name (the tracked cap), __meta (the sub-label), __badge (corner flag),
          __lamp (the dot).                                                        */

.flap-choice {
    position: relative;
    display: flex;
    gap: 0.7rem;
    padding: 0.7rem 0.85rem;
    border: 1px solid transparent;
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--steel);
    cursor: pointer;
    text-align: left;
    font-family: var(--font-ui);
    transition: color var(--motion-fast), border-color var(--motion-fast), background var(--motion-fast);
    user-select: none;
}

.flap-choice--stack { flex-direction: column; align-items: center; gap: 0.2rem; text-align: center; }
.flap-choice--tile  { flex-direction: column; gap: 0.15rem; }

.flap-choice--lamp {
    display: grid;
    grid-template-columns: auto 1fr;
    grid-template-areas: "lamp name" "lamp meta";
    align-items: center;
    column-gap: 0.55rem;
}

.flap-choice:hover {
    color: var(--flap-white);
    border-color: var(--steel-dark);
    background: #22232a;
}

/* The real radio/checkbox is visually replaced by the cell, so the cell has to
   carry the focus ring — remove this and the control becomes keyboard-invisible. */
.flap-choice:focus-within { outline: 2px solid var(--amber); outline-offset: 2px; }

/* Selected = the flap has turned. Amber border, amber label, lit ground. */
.flap-choice--active,
.studio-feature-card.flap-choice--active {
    border-color: var(--amber);
    background: rgba(255,180,0,0.08);
    color: var(--flap-white);
    box-shadow: var(--inset-cell);
}

.flap-choice__name {
    grid-area: name;
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--track-caps);
    color: var(--flap-white);
}

.flap-choice--active .flap-choice__name { color: var(--amber); }

.flap-choice__meta {
    grid-area: meta;
    font-family: var(--font-flap);
    font-size: var(--text-3xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--steel-dark);
}

/* The beat/intensity hints are sentences, not caps — they read as prose in the cell. */
.flap-choice--lamp .flap-choice__meta,
.flap-choice--stack .flap-choice__meta {
    font-family: var(--font-ui);
    font-size: var(--text-xs);
    text-transform: none;
    letter-spacing: 0;
    line-height: var(--lh-snug);
    color: var(--steel);
}

.flap-choice__lamp {
    grid-area: lamp;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--amber-dim);
}

.flap-choice--active .flap-choice__lamp { background: var(--amber); box-shadow: 0 0 8px rgba(255,180,0,0.55); }

.flap-choice__badge {
    position: absolute;
    top: -1px;
    right: -1px;
    background: var(--amber);
    color: var(--flap-black);
    font-family: var(--font-flap);
    font-size: var(--text-3xs);
    font-weight: 700;
    letter-spacing: var(--track-caps);
    padding: 1px 5px;
    border-radius: 0;
    white-space: nowrap;
    text-transform: uppercase;
}

/* The input is the control; the cell is its face. Hidden visually, never from AT. */
.flap-choice input[type="radio"],
.flap-choice input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 1px;
    height: 1px;
    margin: 0;
}

/* ── Fieldsets that hold a row of choices ── */
.flap-fieldset {
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    background: var(--flap-black);
    padding: 0.9rem 1rem 1rem;
    /* Inline only. `margin: 0` here is in the components layer, which outranks the
       utilities layer, so it silently cancelled the .mt-3 every caller puts on a fieldset —
       and the legend sat on the border of the control above it. */
    margin-inline: 0;
}

.flap-fieldset__legend {
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--track-wide);
    color: var(--steel-dark);
    padding: 0 0.35rem;
    width: auto;
    margin-bottom: 0.5rem;
}

/* One track for every set of choices. auto-fit means a two-stop dial and a
   five-stop one both fill the row without either being told a column count. */
.flap-choices {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 1px;
}

/* On a phone auto-fit gives 2 columns, which strands the last of three (2+1). Three stops
   share one row; four go 2x2. */
@media (max-width: 639.98px) {
    .flap-choices:has(> :nth-child(3):last-child) { grid-template-columns: repeat(3, minmax(0, 1fr)); }
    .flap-choices:has(> :nth-child(4):last-child) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .flap-choice--stack { padding-inline: 0.4rem; }
}


.prompt-history__label,
.gallery-bar__label,
.diag-stat__label,
.split-compare__label {
    display: block;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    color: var(--steel-dark);
    text-transform: uppercase;
    letter-spacing: var(--track-wide);
    margin-bottom: 0.5rem;
}

/* ═══ Buttons ════════════════════════════════════════════════════════════
   ONE button. `.flap-btn` is a lit flap you can press; the modifiers change how
   loudly it says so, never what it is.

   This replaces ~20 differently-named classes (btn-process, btn-generate-all,
   btn-result-primary, btn-result-secondary, btn-download-favs, btn-dl-modal,
   btn-heart-modal, studio-surprise, send-to__btn, compare-modes__btn,
   prompt-drawer__btn, gallery-undo__btn, …) that were held in sync only by two
   very long selector lists. A selector list is not consolidation: every new
   button still had to be added to it by hand in three places (base, :hover,
   :disabled), and any that was missed silently fell back to the browser default.

   Modifiers:
     --primary  amber fill; the one action a screen is for. At most one per view.
     (default)  outlined cell; everything else.
     --danger   destructive, red outline.
     --quiet    borderless; inline text actions inside a cell.
     --sm       compact — toolbars, result rows, chips.
     --icon     square, glyph only. Always needs an aria-label.
     --block    full width — the bottom of a form or a mobile rail.
     --active   pressed/selected state for toggle buttons; reads as --primary.  */

.flap-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    padding: 0.5rem 0.9rem;
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    background: transparent;
    color: var(--steel);
    font-family: var(--font-flap);
    font-size: var(--text-xs);
    font-weight: 600;
    line-height: var(--lh-tight);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    text-decoration: none;
    cursor: pointer;
    transition: border-color var(--motion-fast), color var(--motion-fast),
                background var(--motion-fast);
}

.flap-btn:hover:not(:disabled) {
    border-color: var(--amber);
    color: var(--amber);
    background: transparent;
}

.flap-btn:focus-visible { outline: 2px solid var(--amber); outline-offset: 2px; box-shadow: none; }

/* Disabled is a cancelled row: it keeps its cell and loses its lamp. Never a
   fade — opacity on an already-dim label drops it under the contrast floor. */
.flap-btn:disabled,
.flap-btn[aria-disabled="true"] {
    background: transparent;
    border-color: var(--rule-strong);
    color: var(--steel-dark);
    cursor: not-allowed;
    box-shadow: none;
    opacity: 1;
}

.flap-btn--primary {
    padding: 0.75rem 1.25rem;
    border-color: var(--amber);
    background: var(--amber);
    color: var(--flap-black);
    font-size: var(--text-md);
    font-weight: 700;
    box-shadow: var(--shadow-sm);
}

.flap-btn--primary:hover:not(:disabled) {
    background: var(--flap-white);
    border-color: var(--flap-white);
    color: var(--flap-black);
}

.flap-btn--danger { border-color: var(--cancel-red); color: var(--cancel-red-tx); }
.flap-btn--danger:hover:not(:disabled) { border-color: var(--cancel-red); background: var(--cancel-red); color: var(--flap-white); }

.flap-btn--quiet { border-color: transparent; padding-inline: 0.4rem; }
.flap-btn--quiet:hover:not(:disabled) { border-color: transparent; }

.flap-btn--sm { padding: 0.35rem 0.7rem; font-size: var(--text-3xs); letter-spacing: 0.12em; }
.flap-btn--primary.flap-btn--sm { padding: 0.45rem 0.85rem; font-size: var(--text-2xs); }

.flap-btn--icon { padding: 0.4rem; aspect-ratio: 1; gap: 0; }
.flap-btn--icon.flap-btn--sm { padding: 0.28rem; }

.flap-btn--block { width: 100%; }

/* A pressed toggle reads exactly like the primary action, because that is what it
   has become: the flap has turned and this is the state now in force. */
.flap-btn--active {
    border-color: var(--amber);
    background: var(--amber);
    color: var(--flap-black);
    font-weight: 700;
}

.flap-btn--active:hover:not(:disabled) { background: var(--flap-white); border-color: var(--flap-white); color: var(--flap-black); }

/* Touch targets. WCAG 2.5.8 asks for 24px; a coarse pointer wants 44. The board's
   compact buttons are 26–30px tall by design, so they grow only where the pointer
   is actually coarse rather than being padded out for everyone. */
@media (pointer: coarse) {
    .flap-btn { min-height: 44px; }
    .flap-btn--sm { min-height: 38px; }
    .flap-btn--icon { min-width: 44px; }
}

/* ═══ Progress: the row working its way across the board ═════════════════ */
.shimmer-wrapper { padding: 1.1rem; }

/* Status word beside the stage text, on one line at every width the header survives. */
.shimmer-head-line {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    flex-wrap: wrap;
}

.shimmer-header { display: flex; align-items: center; gap: 0.9rem; }

.shimmer-spinner { flex-shrink: 0; color: var(--amber); display: inline-flex; align-items: center; }

/* A run of amber lamps stepping left to right — the board saying "this one is live". */
.lamp-run {
    display: inline-block;
    width: 30px;
    height: 10px;
    background: var(--lamp-lit);
    animation: lampRunStrip 1.1s steps(6, end) infinite;
}

@keyframes lampRunStrip {
    0%   { background-position: 0 0; opacity: 1; }
    50%  { opacity: 0.45; }
    100% { background-position: 30px 0; opacity: 1; }
}

@keyframes spinRotate { to { transform: rotate(360deg); } }

.shimmer-status-label {
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--amber);
    min-height: 1.25rem;
}

/* The progress track is a strip of unlit flaps that light amber as they turn. */
.shimmer-progress {
    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);
}

.shimmer-progress__bar {
    height: 100%;
    width: 100%;
    background: var(--amber);
    border-radius: 0;
    transform: scaleX(var(--progress, 0));
    transform-origin: left center;
    /* transform, not width — a width transition relayouts every frame. */
    transition: transform var(--motion-slow) var(--motion-ease-in-out);
    will-change: transform;
}

/* Indeterminate: a lit run of flaps sweeping the track, because the length of the job
   is genuinely unknown. The alternative — parking a determinate bar at an invented 85%
   until the model answers — reads as a stall, which is precisely the bug this replaced. */
.shimmer-progress--indeterminate .shimmer-progress__bar {
    transform: none;
    width: 34%;
    animation: shimmer-sweep 1.4s var(--motion-ease-in-out) infinite;
    transition: none;
}

@keyframes shimmer-sweep {
    0%   { margin-left: -34%; }
    100% { margin-left: 100%; }
}

/* Honour reduced motion: hold a static lit segment rather than sweeping. */
@media (prefers-reduced-motion: reduce) {
    .shimmer-progress--indeterminate .shimmer-progress__bar {
        animation: none;
        width: 100%;
        opacity: 0.45;
    }
}

.shimmer-pct {
    font-family: var(--font-flap);
    font-size: var(--text-md);
    font-weight: 700;
    color: var(--amber);
    flex-shrink: 0;
    min-width: 42px;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* A row waiting to turn: the unlit flap sweep, not a soft-shadow shimmer. */
.shimmer-anim,
.gallery-thumb--skeleton {
    background-color: var(--flap-shadow);
    background-image: linear-gradient(90deg,
        transparent 0%, rgba(255,180,0,0.16) 45%, rgba(255,180,0,0.16) 55%, transparent 100%);
    background-size: 220% 100%;
    background-repeat: no-repeat;
    animation: flapSweep 1.5s linear infinite;
    box-shadow: var(--inset-cell);
    border-radius: 0;
}

@keyframes flapSweep {
    0%   { background-position: 220% 0; }
    100% { background-position: -120% 0; }
}

@keyframes riseIn { from { transform: translateY(10px); opacity: 0; } to { transform: none; opacity: 1; } }

.results-actions { display: flex; gap: 1px; flex-wrap: wrap; align-items: stretch; }

.results-actions__meta {
    display: inline-flex;
    align-items: center;
    margin-left: auto;
    padding-inline: var(--space-2);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    letter-spacing: 0.1em;
    color: var(--steel);
    font-variant-numeric: tabular-nums;
}

/* Dropdown menu on <details>. The summary is styled as a flap button; the list drops below it. */
.menu { position: relative; }
.menu > summary { list-style: none; cursor: pointer; height: 100%; }
.menu > summary::-webkit-details-marker { display: none; }
.menu[open] > summary { color: var(--amber); }
.menu > summary .bi-chevron-down { margin-left: 0.35rem; font-size: 0.8em; }

.menu__list {
    position: absolute;
    top: calc(100% + 2px);
    left: 0;
    z-index: 30;
    min-width: 14rem;
    display: flex;
    flex-direction: column;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    box-shadow: var(--shadow-md);
}

.menu__list--scroll { max-height: 18rem; overflow-y: auto; }

.menu__item {
    display: flex;
    align-items: center;
    text-align: left;
    padding: 0.55rem 0.8rem;
    background: none;
    border: 0;
    border-bottom: 1px solid var(--rule);
    color: var(--flap-white);
    font: inherit;
    font-size: var(--text-sm);
    cursor: pointer;
}

.menu__item:hover,
.menu__item:focus-visible { background: var(--flap-shadow); color: var(--amber); }

.menu__item i { color: var(--amber); }

/* .results-more__toggle is gone with the "More options" disclosure it opened; the panel's
   secondary actions are always visible now. */
.description-text {
    line-height: 1.65;
    font-size: var(--text-lg);
    text-align: left;
    max-width: 68ch;
    max-height: 300px;
    overflow-y: auto;
    padding-right: 6px;
    white-space: pre-line;
    color: var(--steel);
}

/* ── Compare: two boards side by side ─────────────────────────────────── */
.split-compare-wrapper { border: 1px solid var(--rule-strong); border-radius: 0; overflow: hidden; box-shadow: var(--shadow-md); }

.split-compare { display: grid; grid-template-columns: 1fr auto 1fr; background: var(--flap-black); }

.split-compare__side { position: relative; overflow: hidden; max-height: 420px; }

.split-compare__img {
    width: 100%; height: 100%; object-fit: cover;
    opacity: 0;
    transition: opacity var(--motion-slow) var(--motion-ease-in-out);
    display: block;
}

.split-compare__img.loaded { opacity: 1; }

/* fx.js overlays. The develop canvas sits exactly over the result and shares its crop, so the
   halftone resolves onto the same pixels the <img> shows underneath. */
.fx-develop {
    position: absolute; inset: 0;
    width: 100%; height: 100%; object-fit: cover;
    pointer-events: none;
}

.fx-burst { position: fixed; inset: 0; width: 100vw; height: 100vh; pointer-events: none; z-index: 2000; }

/* Gallery thumbnail → lightbox morph (poFx.heroFrom). */
::view-transition-group(po-hero) {
    animation-duration: 380ms;
    animation-timing-function: var(--motion-ease-in-out);
}

.split-compare__label {
    position: absolute;
    z-index: 1; /* above fx.js's develop canvas, which is appended after it */
    top: 8px;
    left: 8px;
    margin: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--flap-white);
    font-size: var(--text-3xs);
    padding: 0.2rem 0.5rem;
    border-radius: 0;
    pointer-events: none;
}

.split-compare__divider { display: flex; flex-direction: column; align-items: center; background: var(--steel-face); width: 18px; }

.split-compare__divider-line { flex: 1; width: 1px; background: rgba(0,0,0,0.5); }

.split-compare__divider-pill {
    background: var(--flap-black);
    color: var(--amber);
    border-radius: 0;
    width: 18px; height: 22px;
    display: flex; align-items: center; justify-content: center;
    font-size: var(--text-2xs);
    margin: 6px 0;
    flex-shrink: 0;
}

.split-compare__unavailable {
    width: 100%; min-height: 160px;
    display: flex; align-items: center; justify-content: center;
    font-family: var(--font-flap);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--steel-dark);
    font-size: var(--text-xs);
}

/* ── Send result onward: the connecting-services row ──────────────────── */
/* ═══ Accordions and disclosure ══════════════════════════════════════════ */
.description-accordion__toggle {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.6rem;
    cursor: pointer;
    text-align: left;
    transition: filter var(--motion-fast);
}

.description-accordion { border: 1px solid var(--rule-strong); border-radius: 0; overflow: hidden; }

.description-accordion__toggle {
    background: var(--steel-face);
    color: #14161a;
    font-family: var(--font-flap);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-size: var(--text-xs);
    border: none;
    padding: 0.55rem 0.9rem;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
}

.description-accordion__toggle:hover { filter: brightness(1.08); }

.description-accordion__meta { font-size: var(--text-2xs); color: #2b2f36; letter-spacing: 0.1em; }

.description-accordion__body { padding: 0.85rem 1rem; background: var(--flap-black); }

.gallery-bar__count,
.prompt-drawer__count,
.prompt-drawer__fab-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 700;
    letter-spacing: 0.08em;
    background: var(--amber);
    color: var(--flap-black);
    padding: 0.1rem 0.45rem;
    border-radius: 0;
    min-width: 1.35rem;
    font-variant-numeric: tabular-nums;
}

/* ═══ Diagnostics — the board read as a manifest ═════════════════════════ */
.diag-stat { padding: 0.8rem 1rem; }

.diag-stat__label { margin-bottom: 0.35rem; }

.diag-stat__value {
    font-family: var(--font-flap);
    font-size: var(--text-lg);
    font-weight: 700;
    letter-spacing: 0.06em;
    color: var(--flap-white);
    word-break: break-all;
}

.diag-stat__value--env { color: var(--amber); text-transform: uppercase; }

/* The layout shell no longer pads its content slot, so the two pages that are not
   built on FeatureShell state their own rhythm. Both are boards, not documents:
   they take the full rail width rather than a reading measure. */
.studio-page,
.diag-page {
    width: 100%;
    max-width: var(--rail-max);
    margin-inline: auto;
    padding: var(--space-4) var(--space-3) var(--space-5);
}

@media (max-width: 639.98px) {
    .studio-page,
    .diag-page { padding: var(--space-3) var(--space-2) var(--space-4); }
}

/* ═══ Studio — the concourse board ═══════════════════════════════════════
   The board head (.studio-hero*) and the saved-images strip (.studio-gallery-row)
   were removed from Studio.razor at the user's request, so their rules are deleted
   here too rather than left as dead selectors. The page now opens on the intake bay,
   which is why .studio-body carries the top spacing the hero used to provide. */
.studio-body {
    display: grid;
    grid-template-columns: minmax(0, 360px) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
    margin-top: 1.5rem;
}

.studio-upload-col,
.studio-features-col { min-width: 0; }

/* Intake sits at the head of the board at EVERY width, and the routing panel below it.
   The contract's FIRST VIEWPORT promises the intake bay first; with the AI picker on top
   the primary action sat under six selectors — below the fold on a phone, and second in
   the reading order on desktop. Provider choice has a working default and is a refinement;
   loading the photo is the job. This is an order rule, not a breakpoint rule, which is why
   it lives outside the media query. */
.studio-upload-col { display: flex; flex-direction: column; gap: 1rem; }

.studio-upload-col > .ai-picker { order: 2; }

@media (max-width: 1023.98px) {
.studio-body { grid-template-columns: 1fr; gap: 1.25rem; }

}

/* The five transformations are ruled board rows, not a grid of cards. */
.studio-feature-cards {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--rule-strong);
    background: var(--flap-black);
    box-shadow: var(--shadow-md);
}

.studio-feature-card {
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr) auto;
    align-items: center;
    gap: 0.85rem;
    padding: 0.8rem 0.9rem;
    border: none;
    border-bottom: 1px solid var(--rule);
    border-radius: 0;
    background: transparent;
    text-decoration: none;
    color: var(--flap-white);
    transition: background var(--motion-fast);
}

.studio-feature-card:last-child { border-bottom: none; }

.studio-feature-card:hover { background: rgba(255,255,255,0.04); color: var(--flap-white); transform: none; box-shadow: none; }

.studio-feature-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px; height: 34px;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--steel);
    font-size: var(--text-lg);
}

.studio-feature-card:hover .studio-feature-card__icon { color: var(--amber); }

.studio-feature-card__title {
    font-family: var(--font-flap);
    font-weight: 700;
    font-size: var(--text-lg);
    text-transform: uppercase;
    letter-spacing: 0.12em;
}

.studio-feature-card__desc {
    font-size: var(--text-sm);
    color: var(--steel);
    margin: 0.15rem 0 0;
    line-height: 1.45;
    max-width: 56ch;
}

.studio-feature-card__cta {
    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;
    align-self: center;
}

/* Image loaded: every row's departure is now callable. */
.studio-feature-card--ready .studio-feature-card__cta { color: var(--amber); }

.studio-feature-card--ready .studio-feature-card__icon { color: var(--amber); }

.feature-card {
    display: grid;
    grid-template-columns: 34px minmax(0,1fr) auto;
    align-items: center;
    gap: 0.85rem;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--rule);
    border-radius: 0;
    box-shadow: none;
    padding: 0.8rem 0.9rem;
    cursor: pointer;
    overflow: visible;
    transition: background var(--motion-fast);
}

.feature-card::before,
.feature-card::after { content: none; }

.feature-card:last-child { border-bottom: none; }

.feature-card:hover { background: rgba(255,255,255,0.04); transform: none; box-shadow: none; border-color: var(--rule); }

.feature-card__icon {
    display: inline-flex; align-items: center; justify-content: center;
    width: 34px; height: 34px;
    background: var(--flap-shadow); box-shadow: var(--inset-cell);
    color: var(--steel); font-size: var(--text-lg); margin: 0;
}

.feature-card__title {
    font-family: var(--font-flap);
    font-size: var(--text-lg); font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.12em;
    color: var(--flap-white);
}

.feature-card__desc { font-size: var(--text-sm); color: var(--steel); line-height: 1.45; margin: 0.15rem 0 0; max-width: 56ch; }

.feature-card__cta {
    margin: 0; padding: 0;
    font-family: var(--font-flap);
    font-size: var(--text-2xs); font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.16em;
    color: var(--steel-dark);
    display: flex; align-items: center; gap: 0.35rem;
    white-space: nowrap;
}

/* ═══ Galleries — contact strips of turned flaps ═════════════════════════ */
.gallery-bar {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.5rem 0.7rem;
    margin-bottom: 0;
    overflow: hidden;
}

.gallery-bar__label { margin-bottom: 0; flex: 0 0 auto; white-space: nowrap; display: inline-flex; align-items: center; gap: 0.35rem; }

.gallery-bar__scroll {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 1 1 auto;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 2px 0;
    scrollbar-width: thin;
}

.gallery-bar__empty-hint {
    flex: 1 1 auto;
    font-size: var(--text-sm);
    color: var(--steel);
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.gallery-bar__refresh {
    flex: 0 0 auto;
    background: transparent;
    border: 1px solid var(--rule-strong);
    color: var(--steel-dark);
    cursor: pointer;
    border-radius: 0;
    width: 30px; height: 30px;
    display: inline-flex; align-items: center; justify-content: center;
    transition: color var(--motion-fast), border-color var(--motion-fast);
}

.gallery-bar__refresh:hover { color: var(--amber); border-color: var(--amber); }

.gallery-thumb {
    position: relative;
    flex: 0 0 auto;
    width: 46px; height: 46px;
    padding: 0;
    border-radius: 0;
    overflow: hidden;
    border: 1px solid var(--rule-strong);
    background: var(--flap-shadow);
    cursor: pointer;
    transition: border-color var(--motion-fast);
    aspect-ratio: 1 / 1;
}

.gallery-thumb:hover { border-color: var(--amber); transform: none; }

.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 0; display: block; }

/* Kind is carried by the badge glyph in the corner, which is a drawn icon and reads
   without colour at all. The frame only echoes it as a 1px ring — a thicker coloured
   edge on one side would be the side-tab tell, and it would fight the photograph. */
.gallery-thumb.kind-original,
.gallery-thumb--original { border-color: var(--steel); }

.gallery-thumb.kind-regeneration,
.gallery-thumb--regen { border-color: var(--flap-white); }

.gallery-thumb.kind-meme,
.gallery-thumb--meme { border-color: var(--amber); }

.gallery-thumb.kind-bulkvariation,
.gallery-thumb--bulk { border-color: var(--steel-dark); }

.gallery-thumb__badge {
    position: absolute;
    bottom: 3px; right: 2px;
    font-size: var(--text-3xs);
    background: var(--flap-black);
    color: var(--flap-white);
    border-radius: 0;
    width: 16px; height: 16px;
    display: flex; align-items: center; justify-content: center;
    box-shadow: none;
}

.gallery-thumb--skeleton { border-color: transparent; cursor: default; }

/* Skip layout, paint and style for thumbs scrolled out of the strip. A gallery grows
   without bound — it is the one element on the board that does — and every saved image
   was previously a fully laid-out, fully decoded DOM node whether or not it was on
   screen. contain-intrinsic-size supplies the placeholder geometry so the scrollbar
   still measures the whole strip and does not jump as items come into view.

   NOT <Virtualize>: that component spaces items in the block axis, and this strip
   scrolls inline. It would render every item and add a component for the privilege. */
.gallery-thumb-wrap {
    content-visibility: auto;
    contain-intrinsic-size: auto 72px;
}

.gallery-thumb-wrap { position: relative; flex: 0 0 auto; }

.gallery-thumb__zoom {
    position: absolute;
    top: 2px; left: 2px;
    width: 18px; height: 18px;
    display: flex; align-items: center; justify-content: center;
    padding: 0;
    border: none;
    border-radius: 0;
    font-size: var(--text-3xs);
    color: var(--flap-white);
    background: rgba(7,7,8,0.85);
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--motion-fast), background var(--motion-fast);
}

.gallery-thumb__zoom:hover { background: var(--flap-black); }

.gallery-thumb-wrap:hover .gallery-thumb__zoom,
.gallery-thumb-wrap:focus-within .gallery-thumb__zoom { opacity: 1; }

@media (hover: none) {
    .gallery-thumb__zoom { opacity: 1; }
}

/* ═══ Overlays ═══════════════════════════════════════════════════════════ */
.gallery-lightbox {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    background: rgba(7,7,8,0.88);
    animation: fadeIn var(--motion-fast) ease;
}

.gallery-lightbox { z-index: 1600; padding: 1.5rem; }

@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

.bulk-modal-panel,
.gallery-lightbox__panel {
    position: relative;
    background: var(--flap-black);
    border: 1px solid var(--steel-dark);
    border-radius: 0;
    max-width: min(92vw, 640px);
    width: 100%;
    overflow: hidden;
    box-shadow: var(--shadow-xl);
    animation: riseIn var(--motion-base) var(--motion-ease-in-out);
}

.gallery-lightbox__panel {
    max-width: min(920px, 100%);
    max-height: 100%;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 1rem;
    overflow: auto;
}

.bulk-modal-img-wrap {
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    display: flex; align-items: center; justify-content: center;
    max-height: 60vh;
    overflow: hidden;
}

.bulk-modal-img-wrap img { max-width: 100%; max-height: 60vh; object-fit: contain; display: block; }

.bulk-modal-footer { padding: 0.85rem 0.9rem; border-top: 1px solid var(--rule); }

.bulk-modal-prompt {
    font-size: var(--text-sm);
    color: var(--steel);
    margin: 0 0 0.75rem;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.bulk-modal-actions { display: flex; gap: 1px; }

.gallery-lightbox__img { max-width: 100%; max-height: 65vh; object-fit: contain; border-radius: 0; background: var(--flap-shadow); }

.gallery-lightbox__meta { display: flex; flex-direction: column; gap: 0.15rem; }

.gallery-lightbox__name {
    font-family: var(--font-flap);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

.gallery-lightbox__sub {
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--steel-dark);
}

.gallery-lightbox__actions { display: flex; gap: 1px; flex-wrap: wrap; }

/* ═══ Meme, caption battle, style director ═══════════════════════════════ */
.meme-caption-reveal { text-align: center; padding: 0.5rem 1rem; }

.meme-caption-line {
    font-family: var(--font-flap);
    font-size: clamp(1.1rem, 4.5vw, 1.6rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    /* Real depth, offset and blurred — not a chromatic halo. */
    text-shadow: 0 2px 4px rgba(0,0,0,0.8);
}

.meme-template-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 1px;
    max-height: 240px;
    overflow-y: auto;
    padding: 1px;
}

.meme-template-zones { background: var(--flap-shadow); box-shadow: var(--inset-cell); border-radius: 0; padding: 0.85rem; }


/* ═══ Bulk generate ══════════════════════════════════════════════════════ */
.bulk-upload-preview {
    border-radius: 0;
    overflow: hidden;
    aspect-ratio: 16/9;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    display: flex;
    align-items: center;
    justify-content: center;
}

.bulk-upload-preview img { width: 100%; height: 100%; object-fit: cover; }

/* ═══ Prompt drawer ══════════════════════════════════════════════════════ */
/* In flow in the Bulk rail, under Generate. It was position:fixed bottom-left, where on a
   phone it sat on top of the board's empty-state text and on a desktop it was a control
   half a screen away from the button it configures. */
.prompt-drawer__fab {
    display: flex;
    width: 100%;
    justify-content: center;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.4rem;
    padding: 0.6rem 1rem;
    border: 1px solid var(--steel-dark);
    border-radius: 0;
    background: var(--flap-shadow);
    color: var(--flap-white);
    font-family: var(--font-flap);
    font-weight: 700;
    font-size: var(--text-xs);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    cursor: pointer;
    transition: border-color var(--motion-fast), color var(--motion-fast);
}

.prompt-drawer__fab:hover { border-color: var(--amber); color: var(--amber); }

.prompt-drawer__fab i { font-size: var(--text-lg); }

.prompt-drawer__fab--active { border-color: var(--amber); color: var(--amber); }

.prompt-drawer__backdrop {
    position: fixed;
    top: 61px; left: 0; right: 0; bottom: 0;
    z-index: 1050;
    background: rgba(7,7,8,0.7);
}

.prompt-drawer {
    position: fixed;
    top: 61px; left: 0; bottom: 0;
    width: min(400px, 90vw);
    z-index: 1055;
    display: flex;
    flex-direction: column;
    background: var(--flap-black);
    border-right: 1px solid var(--steel-dark);
    box-shadow: var(--shadow-xl);
    transform: translateX(-100%);
    transition: transform var(--motion-slow) var(--motion-ease-in-out);
    overflow: hidden;
}

.prompt-drawer--open { transform: translateX(0); }

.prompt-drawer__header {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.55rem 0.9rem;
    background: var(--steel-face);
    border-bottom: 1px solid rgba(0,0,0,0.6);
}

.prompt-drawer__title {
    flex: 1;
    display: flex;
    align-items: center;
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #14161a;
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
    margin: 0;
}

.prompt-drawer__count { background: var(--flap-black); color: var(--amber); }

.prompt-drawer__close {
    width: 1.9rem; height: 1.9rem;
    border: 1px solid rgba(0,0,0,0.5);
    border-radius: 0;
    background: var(--flap-black);
    color: var(--steel);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: background var(--motion-fast), color var(--motion-fast);
}

.prompt-drawer__close:hover { background: var(--amber); color: var(--flap-black); }

.prompt-drawer__actions { display: flex; gap: 1px; padding: 0.7rem 0.9rem; border-bottom: 1px solid var(--rule); }



.prompt-drawer__hint {
    padding: 0.5rem 0.9rem;
    font-size: var(--text-xs);
    color: var(--steel);
    background: var(--flap-shadow);
    border-bottom: 1px solid var(--rule);
}

.prompt-drawer__hint code {
    background: var(--flap-black);
    color: var(--amber);
    padding: 0.05rem 0.3rem;
    border-radius: 0;
    font-size: var(--text-xs);
}

.prompt-drawer__list { flex: 1; padding: 0.9rem; display: grid; gap: 0.6rem; overflow-y: auto; }

.prompt-drawer__item { display: flex; align-items: flex-start; gap: 0.5rem; flex-wrap: wrap; }

.prompt-drawer__row { display: flex; align-items: flex-start; gap: 0.5rem; width: 100%; }

/* Row numbers are the board's position column, so they stay tabular. */
.prompt-drawer__num {
    flex-shrink: 0;
    width: 1.6rem; height: 1.6rem;
    margin-top: 0.3rem;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 0;
    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;
}

.prompt-drawer__textarea,
textarea.form-control,
input.form-control {
    flex: 1;
    background: var(--flap-shadow);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    color: var(--flap-white);
    font-family: var(--font-ui);
    font-size: var(--text-md);
    padding: 0.5rem 0.7rem;
    box-shadow: var(--inset-cell);
    transition: border-color var(--motion-fast);
    line-height: 1.5;
}

/* Autosized (ux.js, plus field-sizing natively where supported), so the box always fits its
   text and never scrolls inside the already-scrolling drawer list. resize is off because a
   hand-dragged height is overwritten on the next keystroke. */
.prompt-drawer__textarea {
    resize: none;
    min-height: 48px;
    overflow-y: hidden;
    field-sizing: content;
}

.prompt-drawer__textarea:focus,
textarea.form-control:focus,
input.form-control:focus {
    outline: none;
    border-color: var(--amber);
    box-shadow: var(--inset-cell), 0 0 0 1px var(--amber);
    background: var(--flap-shadow);
    color: var(--flap-white);
}

::placeholder {
    color: var(--steel-dark);
    font-family: var(--font-flap);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: 0.8em;
    opacity: 1;
}

.prompt-drawer__reset {
    flex-shrink: 0;
    margin-top: 0.3rem;
    width: 1.9rem; height: 1.9rem;
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    background: var(--flap-shadow);
    color: var(--steel-dark);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: color var(--motion-fast), border-color var(--motion-fast);
}

.prompt-drawer__reset:hover { color: var(--amber); border-color: var(--amber); transform: none; }


/* ═══ Prompt history ═════════════════════════════════════════════════════ */
.prompt-history { padding: 0.5rem 0.75rem; border-top: 1px solid var(--rule); border-bottom: 1px solid var(--rule); }

.prompt-history__label { display: flex; align-items: center; margin-bottom: 0.4rem; }

.prompt-history__target { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 500; color: var(--steel); }

.prompt-history__list { display: flex; flex-direction: column; gap: 1px; max-height: 190px; overflow-y: auto; }

.prompt-history__chip {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    border: 1px solid transparent;
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
}

.prompt-history__chip--pinned { border-color: var(--amber); background: rgba(255,180,0,0.08); }

.prompt-history__use {
    flex: 1; min-width: 0;
    text-align: left;
    background: transparent; border: none;
    color: var(--steel);
    font-size: var(--text-xs);
    padding: 0.4rem 0.5rem;
    cursor: pointer;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.prompt-history__use:hover { color: var(--amber); }

.prompt-history__pin,
.prompt-history__drop {
    background: transparent; border: none;
    color: var(--steel-dark);
    padding: 0.25rem 0.35rem;
    font-size: var(--text-xs);
    cursor: pointer;
    transition: color var(--motion-fast);
}

.prompt-history__pin:hover,
.prompt-history__drop:hover { color: var(--flap-white); }

.prompt-history__chip--pinned .prompt-history__pin { color: var(--amber); }

/* ═══ Session cost — the fare counter ════════════════════════════════════ */
.session-cost-wrapper {
    position: relative;
    display: inline-block;
}

.session-cost {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    padding: 0.15rem 0.5rem;
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--steel);
    white-space: nowrap;
    cursor: pointer;
    text-decoration: none;
}

.session-cost:hover {
    color: var(--flap-white);
    background: var(--flap-black);
}

.session-cost__value { font-weight: 700; color: var(--amber); font-variant-numeric: tabular-nums; }

.session-cost__meta { color: var(--steel-dark); }

.session-cost-popover {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 1050;
    min-width: 290px;
    background: var(--flap-black);
    border: 1px solid var(--rule-strong);
    box-shadow: 0 4px 20px rgba(0,0,0,0.85);
    padding: 0.75rem;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--flap-white);
}

.session-cost-popover__header {
    padding-bottom: 0.4rem;
    border-bottom: 1px solid var(--rule);
    margin-bottom: 0.4rem;
    font-size: var(--text-2xs);
}

@media (max-width: 639.98px) {
.session-cost__meta { display: none; }
.session-cost-popover { max-width: calc(100vw - 2rem); }
}

/* Small caption and chip text used inside the cost popover. Lives in the global
   sheet because SessionCostChip does not own a scoped .razor.css. */
.session-cost-caption { font-size: 0.65rem; }
.session-cost-chip-text { font-size: 0.7rem; }

/* ═══ Page options ══════════════════════════════════════════════════════
   The settings a feature reads, laid out between the photo and the action. */
.options-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
    gap: var(--space-3);
    margin-top: var(--space-3);
}

.options-hint {
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: var(--steel);
}

.options-hint i { color: var(--amber); }

/* ═══ Primary action bar ════════════════════════════════════════════════
   The page's one primary button. Below md it pins to the bottom of the viewport
   while its panel is on screen — sticky, not fixed, so it never covers a result
   and no page has to reserve space for it. */
.cta-bar { margin-top: var(--space-3); display: flex; flex-direction: column; gap: 0.4rem; }

/* A disabled button that does not say why is a dead end. */
.cta-bar__hint {
    margin: 0;
    max-width: none;
    font-size: var(--text-sm);
    color: var(--steel);
    text-align: center;
}

@media (max-width: 767.98px) {
    /* The phone tab bar (NavMenu.razor.css) is fixed to the bottom edge; everything that
       pins to the bottom clears it, and the page gets room to scroll its last row out. */
    :root { --tabbar-h: 56px; }
    body { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px)); }

    .cta-bar {
        position: sticky;
        bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom, 0px));
        z-index: 20;
        padding: 0.6rem var(--space-2);
        background: var(--flap-shadow);
        border-top: 1px solid var(--steel-dark);
        box-shadow: 0 -4px 18px rgba(0,0,0,0.6);
    }
}

}

@layer overrides {
/* ═══ Small components that used to come from Bootstrap ══════════════════
   These are no longer overrides of a framework's opinion — they ARE the badge,
   the alert, the spinner and the form control. The CDN build that previously
   supplied them shipped ~230KB to provide these nine rules plus a grid, and the
   sheet then spent 44 more rules and 46 !important declarations disagreeing with
   the rest of it. Owning them outright costs less than arguing with them.       */

.badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.2rem 0.5rem;
    border-radius: 0;
    background: var(--flap-shadow);
    color: var(--steel);
    font-family: var(--font-flap);
    font-size: var(--text-3xs);
    font-weight: 700;
    letter-spacing: var(--track-caps);
    text-transform: uppercase;
    line-height: var(--lh-tight);
    white-space: nowrap;
}

/* Status colours on a badge come from the four-value vocabulary, nowhere else. */
.badge.bg-success { background: var(--flap-white); color: var(--flap-black); }
.badge.bg-warning { background: var(--amber); color: var(--flap-black); }
.badge.bg-danger  { background: var(--cancel-red); color: var(--flap-white); }
.badge.bg-info,
.badge.bg-primary,
.badge.bg-secondary { background: var(--flap-shadow); color: var(--steel); }

.alert {
    border-radius: 0;
    border: 1px solid var(--rule-strong);
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--steel);
    font-size: var(--text-md);
    padding: 0.85rem 1rem;
    margin-bottom: var(--space-3);
}

.alert-danger { border-color: var(--cancel-red); color: var(--cancel-red-tx); }
.alert-info   { border-color: var(--rule-strong); color: var(--steel); }

/* The working lamp as a spinner. One authored rotation, paused under
   prefers-reduced-motion by the global rule in foundation.css. */
.spinner-border {
    display: inline-block;
    width: 1.1rem;
    height: 1.1rem;
    vertical-align: -0.15em;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    color: var(--amber);
    animation: flap-spin 0.75s linear infinite;
}

.spinner-border-sm { width: 0.8rem; height: 0.8rem; border-width: 1.5px; }

@keyframes flap-spin { to { transform: rotate(360deg); } }

/* Cards are the board panel (see Materials); these are the parts inside it. */
.card-header {
    padding: 0.7rem 1rem;
    border-bottom: 1px solid var(--rule);
    background: var(--flap-shadow);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
    color: var(--steel-dark);
}

.card-body { padding: 1rem; }

.form-label {
    display: block;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
    color: var(--steel-dark);
    margin-bottom: 0.35rem;
}

.form-control {
    display: block;
    width: 100%;
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    background: var(--flap-shadow);
    box-shadow: var(--inset-cell);
    color: var(--flap-white);
    font-family: var(--font-ui);
    font-size: var(--text-lg);
    line-height: var(--lh-snug);
}

.form-control:focus { border-color: var(--amber); outline: none; box-shadow: inset 0 0 0 1px var(--amber); }

.form-control-sm { padding: 0.32rem 0.5rem; font-size: var(--text-md); }

/* ═══ Radzen, retinted ═══════════════════════════════════════════════════
   Radzen is themed through its OWN custom properties rather than by out-specifying
   its selectors. That matters here: the previous approach listed six component
   classes (.rz-card, .rz-panel, .rz-dropdown, .rz-textbox, .rz-notification,
   .rz-dialog) and forced each with !important — which is why the one Radzen
   component the app actually leans on, the /diag DataGrid, was never covered and sat
   on the page as a white Material table inside a matte-black departure board.

   Re-pointing the variables reaches every component at once, including ones this app
   has not adopted yet, and it survives a Radzen upgrade that renames a class. */
:root {
    --rz-body-background-color: var(--board-ground);
    --rz-base-background-color: var(--flap-black);
    --rz-text-color: var(--flap-white);
    --rz-text-secondary-color: var(--steel);
    --rz-text-disabled-color: var(--steel-dark);
    --rz-link-color: var(--amber);
    --rz-border-radius: 0;
    --rz-primary: var(--amber);
    --rz-primary-light: var(--amber);
    --rz-primary-dark: var(--amber-dim);
    --rz-danger: var(--cancel-red);
    --rz-success: var(--flap-white);
    --rz-warning: var(--amber);
    --rz-info: var(--steel);

    /* DataGrid: a manifest printed on the board, not a spreadsheet. */
    --rz-grid-background-color: var(--flap-black);
    --rz-grid-border: 1px solid var(--rule-strong);
    --rz-grid-border-radius: 0;
    --rz-grid-cell-color: var(--flap-white);
    --rz-grid-cell-border: 1px solid var(--rule);
    --rz-grid-bottom-cell-border: 1px solid var(--rule);
    --rz-grid-cell-font-size: var(--text-md);
    --rz-grid-header-background-color: var(--flap-shadow);
    --rz-grid-header-color: var(--steel-dark);
    --rz-grid-header-font-size: var(--text-2xs);
    --rz-grid-header-font-weight: 700;
    --rz-grid-header-cell-border: 1px solid var(--rule);
    --rz-grid-header-cell-border-bottom: 1px solid var(--rule-strong);
    --rz-grid-header-sorted-background-color: var(--flap-shadow);
    --rz-grid-filter-background-color: var(--flap-shadow);
    --rz-grid-filter-color: var(--flap-white);
    --rz-grid-filter-border: 1px solid var(--rule-strong);
    --rz-grid-header-filter-icon-active-color: var(--amber);
    --rz-grid-header-filter-icon-hover-color: var(--amber);
    --rz-grid-row-hover-background-color: rgba(255,180,0,0.06);
    --rz-grid-stripe-background-color: var(--flap-shadow);
}

/* Column heads are the board's own tracked caps, which no variable can express. */
.rz-data-grid .rz-column-title,
.rz-datatable .rz-column-title {
    font-family: var(--font-flap);
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
}

/* Radzen paints its own surfaces with hard-coded whites in a few places the theme
   variables do not reach. These are the remaining few, named explicitly. */
.rz-data-grid,
.rz-datatable,
.rz-grid-table,
.rz-grid-table tbody tr,
.rz-datatable-data tr { background-color: var(--flap-black); color: var(--flap-white); }

/* ── Select bar (Gallery kind filter) ─────────────────────────────────────
   Radzen's default paints the selected segment in its own blue and the rest in
   white-on-white, which is the one thing this board cannot absorb. Retinted to the
   four-value vocabulary: steel faces, amber for the active segment, square corners,
   tracked flap caps. Every size class is covered because Size is a component
   parameter — changing it in markup must not silently fall back to the blue. */
.rz-selectbutton {
    /* inline-flex, so the bar is exactly as wide as its segments — as a block it stretched
       and the container's rule colour showed through as a dead grey tail. */
    display: inline-flex;
    flex-wrap: wrap;
    gap: 1px;
    border: 1px solid var(--rule-strong);
    background: var(--rule-strong);
    border-radius: 0;
    overflow: hidden;
}

.rz-selectbutton .rz-button {
    border-radius: 0 !important;
    border: none;
    background: var(--flap-black);
    color: var(--steel);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
    box-shadow: none;
    transition: color var(--motion-fast), background var(--motion-fast);
}

.rz-selectbutton .rz-button:hover { background: var(--flap-shadow); color: var(--flap-white); }

.rz-selectbutton .rz-button.rz-state-active,
.rz-selectbutton .rz-button.rz-button-xs.rz-state-active,
.rz-selectbutton .rz-button.rz-button-sm.rz-state-active,
.rz-selectbutton .rz-button.rz-button-md.rz-state-active,
.rz-selectbutton .rz-button.rz-button-lg.rz-state-active {
    background: var(--amber);
    color: var(--flap-black);
    font-weight: 700;
}

.rz-selectbutton .rz-button:focus-visible {
    outline: 2px solid var(--amber);
    outline-offset: -2px;
}

/* A select bar inside the page options fills its column, one equal segment per choice. */
.options-row .rz-selectbutton { display: flex; width: 100%; }
.options-row .rz-selectbutton .rz-button { flex: 1 1 0; }

/* ── 2x2 comparison matrix (Radzen dialog content) ────────────────────────
   Replaces a Bootstrap modal whose row/col/ratio/border/rounded/bg-black classes all
   stopped existing with the bundle. A real 2-up grid, hairline rules, square corners. */
.compare-matrix { display: flex; flex-direction: column; gap: var(--space-3); }

.compare-matrix__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
}

/* One column below the fold-out width: two 1:1 tiles side by side on a phone leaves each
   too small to actually compare. */
@media (max-width: 600px) {
    .compare-matrix__grid { grid-template-columns: minmax(0, 1fr); }
}

.compare-matrix__cell {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin: 0;
    padding: var(--space-2);
    background: var(--flap-shadow);
    border: 1px solid var(--rule);
}

.compare-matrix__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2);
}

.compare-matrix__img-wrap {
    aspect-ratio: 1 / 1;
    background: var(--flap-black);
    box-shadow: var(--inset-cell);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}

/* contain, not cover: this panel exists to compare variations, and a crop would hide the
   part of the frame the user is comparing. */
.compare-matrix__img-wrap img { width: 100%; height: 100%; object-fit: contain; display: block; }

.compare-matrix__prompt {
    margin: 0;
    color: var(--steel);
    font-size: var(--text-2xs);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.compare-matrix__actions { display: flex; gap: var(--space-2); align-items: center; margin-top: auto; }

.compare-matrix__actions .rz-button { border-radius: 0; }

.compare-matrix__empty { color: var(--steel); margin: 0; padding: var(--space-4) 0; text-align: center; }

.compare-matrix__footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px solid var(--rule);
}

.compare-matrix__footer .rz-button { border-radius: 0; }

/* ── Gallery card ─────────────────────────────────────────────────────────
   The card, its thumbnail frame and the two overlays. All of this used to lean on
   Bootstrap utilities (card, position-relative/absolute, top-0, start-0, end-0,
   card-img-top, ratio) that stopped existing with the bundle, which is why the kind
   chip and the select box collided on top of each other. */
.gallery-card {
    display: flex;
    flex-direction: column;
    height: 100%;
    background: var(--flap-black);
    border: 1px solid var(--rule);
    border-radius: 0;
}

.gallery-card.border-warning { border-color: var(--amber); }

.gallery-card__thumb-wrapper {
    position: relative;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background: var(--flap-shadow);
}

.gallery-card__img { width: 100%; height: 100%; object-fit: cover; display: block; }

.gallery-card__kind { position: absolute; top: 0.5rem; left: 0.5rem; z-index: 1; }

.gallery-card__select { position: absolute; top: 0.5rem; right: 0.5rem; z-index: 1; }

/* Radzen's checkbox on a photo: square, and dark enough to read against a light image. */
.gallery-card__select .rz-chkbox-box,
.gallery-selectall .rz-chkbox-box {
    border-radius: 0;
    background: var(--flap-black);
    border: 1px solid var(--steel);
}

.gallery-card__select .rz-chkbox-box.rz-state-active,
.gallery-selectall .rz-chkbox-box.rz-state-active {
    background: var(--amber);
    border-color: var(--amber);
    color: var(--flap-black);
}

.gallery-selectall {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    cursor: pointer;
    color: var(--steel);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
}

.gallery-selectall:hover { color: var(--flap-white); }

/* ── Gallery card action row ──────────────────────────────────────────────
   Was `btn-group` + `btn-outline-*`, all Bootstrap classes that no longer exist.
   RadzenButton supplies the control; these rules supply the board's hairline rule
   and square corners. */
.gallery-card { transition: border-color var(--motion-fast); }

.gallery-card__tags { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.35rem; max-height: 2.6rem; overflow: hidden; }

.gallery-card__tag { font-size: 0.72rem; border: 0; cursor: pointer; }

/* Actions overlay the bottom of the thumbnail. Hidden until hover or keyboard focus on a
   pointer that can hover; always shown on touch, where there is no hover to reveal them. */
.gallery-card__actions {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    z-index: 1;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.15rem 0.25rem;
    background: rgba(7,7,8,0.82);
}

@media (hover: hover) {
    .gallery-card__actions { opacity: 0; transition: opacity var(--motion-fast); }
    .gallery-card:hover .gallery-card__actions,
    .gallery-card:focus-within .gallery-card__actions { opacity: 1; }
}

.gallery-card__actions-group { display: flex; gap: 1px; }

.gallery-card__actions .rz-button {
    border-radius: 0;
    min-width: 0;
    padding: 0.3rem 0.5rem;
}

.gallery-card__actions .rz-button:hover { background: var(--flap-shadow); }

/* Tooltip, in the board's voice rather than Radzen's rounded grey bubble. */
.flap-tooltip .rz-tooltip-content,
.rz-tooltip.flap-tooltip .rz-tooltip-content {
    background: var(--steel-face);
    color: #14161a;
    border-radius: 0;
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
    box-shadow: var(--shadow-md);
}

/* Confirm dialog: square, flap-black, so a destructive question does not arrive
   looking like a different product. */
.flap-confirm .rz-dialog-content { background: var(--flap-black); color: var(--flap-white); }

.flap-confirm .rz-dialog-confirm-buttons .rz-button { border-radius: 0; }

/* ── Text input (Gallery search) ──────────────────────────────────────────── */
.rz-textbox {
    background: var(--flap-shadow);
    border: 1px solid var(--rule-strong);
    border-radius: 0;
    color: var(--flap-white);
    font-family: var(--font-ui);
    box-shadow: var(--inset-cell);
}

.rz-textbox:focus,
.rz-textbox:focus-visible {
    border-color: var(--amber);
    outline: none;
}

.rz-textbox::placeholder {
    color: var(--steel-dark);
    font-family: var(--font-flap);
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
}

/* Search field: the magnifier sits inside the box, so the input is padded past it
   rather than the icon being a separate input-group segment (the Bootstrap
   input-group classes this used to rely on no longer exist). */
/* Gallery toolbar: title, search, filter, actions on one row; wraps on narrow screens. */
.gallery-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    margin-bottom: var(--space-3);
}

.gallery-toolbar__title {
    margin: 0;
    font-family: var(--font-flap);
    font-size: var(--text-lg);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--flap-white);
    white-space: nowrap;
}

.gallery-toolbar__title i { color: var(--amber); }

.gallery-toolbar .gallery-search { flex: 1 1 14rem; min-width: 0; }

.gallery-toolbar__actions { display: flex; gap: 1px; margin-left: auto; }

.gallery-batch {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-bottom: var(--space-3);
    padding: 0.4rem 0.6rem;
    border: 1px solid var(--amber);
    background: var(--flap-shadow);
}

.spin { animation: flap-spin 1s linear infinite; }

.gallery-search { position: relative; display: flex; align-items: center; }

.gallery-search__icon {
    position: absolute;
    left: 0.6rem;
    color: var(--steel-dark);
    font-size: var(--text-sm);
    pointer-events: none;
    z-index: 1;
}

.gallery-search__input { width: 100%; padding-left: 2rem !important; padding-right: 2rem !important; }

.gallery-search__clear { position: absolute; right: 0.15rem; color: var(--steel); }

.gallery-kinds { display: flex; min-width: 0; overflow-x: auto; }

.rz-notification-item,
.rz-dialog,
.rz-card,
.rz-panel {
    border-radius: 0;
    box-shadow: var(--shadow-md);
    background-color: var(--flap-black);
    color: var(--flap-white);
    border: 1px solid var(--rule-strong);
}

/* The gallery lightbox is a Radzen dialog now; it keeps its own frameless treatment. */
.rz-dialog.gallery-lightbox .rz-dialog-content { padding: 0; }

/* Dialog chrome. The .rz-dialog rule above paints the panel, but Radzen draws the TITLE BAR
   from its own light theme variables, so every dialog in the app — the variation modal, the
   gallery lightbox, the confirm, the comparison matrix — opened with a white header strip
   above a flap-black body. Styled once here rather than per dialog. */
.rz-dialog-titlebar {
    background: var(--steel-face);
    border-bottom: 1px solid var(--rule-strong);
    border-radius: 0;
    padding: 0.5rem 0.9rem;
}

.rz-dialog-title {
    color: #14161a;
    font-family: var(--font-flap);
    font-size: var(--text-sm);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--track-flap);
    text-shadow: 0 1px 0 rgba(255,255,255,0.35);
}

/* Radzen's close glyph inherits the light theme's ink, which vanishes on the steel face. */
.rz-dialog-titlebar .rz-dialog-titlebar-close { color: #14161a; border-radius: 0; }

.rz-dialog-titlebar .rz-dialog-titlebar-close:hover { background: rgba(0,0,0,0.15); }

.rz-dialog-content { background: var(--flap-black); color: var(--flap-white); }

/* Outlined buttons inside a dialog: Radzen's default is a white pill. Scoped to
   .rz-variant-outlined ONLY — adding .rz-shade-default here also caught the filled
   amber buttons and flattened them to transparent, which cost the dialog its primary
   action. Filled variants keep whatever ButtonStyle the markup asked for. */
.rz-dialog .rz-button.rz-variant-outlined {
    border-radius: 0;
    border-color: var(--rule-strong);
    color: var(--flap-white);
    background: transparent;
}

/* Square corners for every dialog button, whatever its variant. */
.rz-dialog .rz-button { border-radius: 0; }

/* Filled amber is the board's "go" face and it carries BLACK ink, not white. Radzen keeps
   its own light-theme foreground on filled buttons, which put paint-white on #FFB400 — a
   contrast ratio around 1.9:1, well under AA. Same for the danger fill. */
.rz-dialog .rz-button.rz-variant-filled.rz-warning,
.rz-button.rz-variant-filled.rz-warning { color: var(--flap-black); font-weight: 700; }

.rz-dialog .rz-button.rz-variant-filled.rz-danger,
.rz-button.rz-variant-filled.rz-danger { color: var(--flap-white); }

.rz-dialog .rz-button.rz-variant-outlined:hover { background: var(--flap-shadow); }


/* ═══ Chrome the framework still emits ═══════════════════════════════════ */

.blazor-error-boundary {
    background: var(--cancel-red);
    padding: 1rem 1rem 1rem 3.7rem;
    color: var(--flap-white);
    font-family: var(--font-flap);
    text-transform: uppercase;
    letter-spacing: var(--track-caps);
}

.blazor-error-boundary::after { content: "AN ERROR HAS OCCURRED."; }

#blazor-error-ui {
    background: var(--cancel-red);
    color: var(--flap-white);
    font-family: var(--font-flap);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-size: var(--text-sm);
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0,0,0,0.4);
    display: none;
    left: 0;
    padding: 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.6rem; }
#blazor-error-ui .reload { color: var(--flap-black); background: var(--flap-white); padding: 0 0.4rem; text-decoration: none; }

code { color: var(--amber); }

/* ═══ Scrollbars — the board's own gutters ═══════════════════════════════ */

* { scrollbar-color: var(--steel-dark) var(--flap-black); scrollbar-width: thin; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--flap-black); }
::-webkit-scrollbar-thumb { background: var(--steel-dark); border: 2px solid var(--flap-black); }
::-webkit-scrollbar-thumb:hover { background: var(--steel); }

/* ═══ Responsive ═════════════════════════════════════════════════════════
   Portrait is the primary composition. Column priority narrows; the board never
   scrolls sideways. Four breakpoints only — see the table in foundation.css.   */

@media (max-width: 639.98px) {
    body { background-image: none; }
    .upload-panel { padding: 0; }

    /* The status column drops below the name rather than squeezing it. Once it does,
       the SERVICE / STATUS header no longer sits over the columns it names, so it is
       withdrawn rather than left pointing at the wrong place. */
    .row-head { display: none; }
    .studio-feature-card { grid-template-columns: 30px minmax(0,1fr); row-gap: 0.35rem; }
    .studio-feature-card__cta { grid-column: 2; }
    .feature-card { grid-template-columns: 30px minmax(0,1fr); row-gap: 0.35rem; }
    .feature-card__cta { grid-column: 2; }

    .bulk-modal-panel { max-height: 92vh; overflow-y: auto; }
    .bulk-modal-img-wrap { max-height: 45vh; }
    .results-actions { display: grid; grid-template-columns: 1fr 1fr; }
    .results-actions > *, .results-actions .menu > summary { width: 100%; justify-content: center; }
    .results-actions__meta { grid-column: 1 / -1; margin-left: 0; justify-content: center; padding-block: 0.25rem; }
    .split-compare { grid-template-columns: 1fr; }
    .split-compare__divider { width: auto; height: 14px; flex-direction: row; }
    .split-compare__divider-line { width: auto; height: 1px; flex: 1; }
}

}

/* ══════════════════════════════════════════════════════════════════════════════
   TOUCH TARGETS
   Measured on a 390×844 viewport, every one of these came in under the 44px
   minimum: the gallery refresh and prompt-drawer close buttons at 30×30, every
   AI picker select at 31px tall, and the small flap buttons at 38px. They are
   fine with a mouse and awkward with a thumb, which is the width where this app
   is most likely to be used on a photo you just took.

   The rule raises the CONTROL where the layout can absorb it and extends the
   hit area past the ink where it cannot — the guideline is about what the
   finger can land on, not how big the paint is.
   ══════════════════════════════════════════════════════════════════════════ */
@media (pointer: coarse), (max-width: 767.98px) {

    .gallery-bar__refresh,
    .prompt-drawer__close {
        min-width: 44px;
        min-height: 44px;
    }

    .ai-picker__select {
        min-height: 44px;
        font-size: var(--text-md);
    }

    .flap-btn--sm { min-height: 44px; }

    /* Hit area extended without moving the painted box, for controls whose size is
       load-bearing on the rail or inside a dense row. */
    .flap-choice,
    .gallery-bar__refresh,
    .prompt-drawer__close { position: relative; }

    .flap-choice::after {
        content: "";
        position: absolute;
        inset: 50% 0 auto 0;
        min-height: 44px;
        height: 100%;
        transform: translateY(-50%);
    }
}

/* The 10px step is an UPPERCASE-ONLY size by the type scale's own rule, and badges
   using it sat at the edge of legibility on a phone. Bump the smallest two steps on
   coarse pointers; the tracking keeps them reading as flap caps either way. */
@media (pointer: coarse), (max-width: 767.98px) {
    .badge,
    .ai-picker__fixed-summary { font-size: var(--text-2xs); }
}

/* Named reason shown when a description was synthesised from tags rather than produced by a
   model that looked at the photo. Amber, not red: the result is usable, just degraded. */
.description-fallback {
    color: var(--flap-amber, #e0a63a);
    border-left: 3px solid var(--flap-amber, #e0a63a);
    background: rgba(224, 166, 58, 0.08);
    padding: 0.5rem 0.75rem;
    border-radius: 0 4px 4px 0;
}

/* ═══ Camera capture ═══════════════════════════════════════════════════
   The live-preview alternative to the drop zone. Same steel frame and square
   corners as the board's other bays, so opening the camera reads as the intake
   bay changing mode rather than a modal arriving from somewhere else. */
/* A column beside the drop zone, not a full-width row under it: the two are alternative
   answers to one question, and the row cost ~50px above every page's options. */
.upload-panel__intake { display: flex; gap: var(--space-2); align-items: stretch; }
.upload-panel__intake > .drop-zone { flex: 1; min-width: 0; }

.upload-panel__camera-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    width: 5.5rem;
    flex: 0 0 auto;
    padding: var(--space-2);
    background: transparent;
    border: 1px dashed var(--steel);
    border-radius: 0;
    color: var(--flap-white);
    font-family: inherit;
    font-size: var(--text-sm);
    letter-spacing: .04em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color .12s linear, color .12s linear;
}

.upload-panel__camera-btn:hover:not(:disabled),
.upload-panel__camera-btn:focus-visible {
    border-color: var(--amber);
    color: var(--amber);
}

.upload-panel__camera-btn:disabled { opacity: .5; cursor: default; }

.upload-panel__camera-btn i { font-size: 1.25rem; }

.upload-panel__camera-text { font-size: var(--text-2xs); letter-spacing: 0.1em; }

/* Beside the compact photo chip the camera is an icon-only square. */
.upload-panel__intake--compact .upload-panel__camera-btn { width: auto; padding: 0 var(--space-3); }

.upload-panel__intake--compact .upload-panel__camera-text { display: none; }

.camera {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.camera__stage {
    position: relative;
    /* Matches the drop zone's footprint so switching modes does not jump the page. */
    aspect-ratio: 4 / 3;
    background: var(--flap-black);
    border: 1px solid var(--steel);
    overflow: hidden;
}

.camera__video {
    width: 100%;
    height: 100%;
    /* cover, not contain: letterboxing a live preview inside an already-framed bay reads as
       a broken image rather than a deliberate crop. */
    object-fit: cover;
    display: block;
}

/* Front lens only. capturePhoto() mirrors the captured frame to match, so what is saved is
   what was on screen. */
.camera__video--mirrored { transform: scaleX(-1); }

.camera__controls {
    display: flex;
    gap: var(--space-2);
    justify-content: flex-end;
    flex-wrap: wrap;
}

@media (prefers-reduced-motion: reduce) {
    .upload-panel__camera-btn { transition: none; }
}

/* ═══ Classes the markup asked for and never got ═══════════════════════════
   Each of these was referenced from a class attribute with no rule anywhere in the
   app. Some were leaning on Bootstrap utilities that stopped existing with the bundle
   (.gallery-page's padding, the cost table's table-dark/table-sm), the rest were simply
   never written. Marker-only modifiers are NOT listed here: .shimmer-progress--determinate,
   .gallery-bar--loading/--empty and friends sit on bases that already carry their styling,
   so they render correctly with no rule of their own. */

/* Gallery page gutter. Was `p-3 p-md-4`; p-md-4 never existed, so the wider desktop
   gutter silently never applied. */
.gallery-page { padding: var(--space-3); }

@media (min-width: 768px) {
    .gallery-page { padding: var(--space-4); }
}

/* Dashed empty-state frame — the one place the board uses a dashed rule, to say
   "nothing here yet" rather than "a thing with an edge". */
.border-dashed { border-style: dashed !important; }

/* Utilities the markup used that the Bootstrap bundle used to supply. */
.fst-italic { font-style: italic; }
.text-amber { color: var(--amber); }

/* ── Video feature ──────────────────────────────────────────────────────── */
.video-prompt-help {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    margin-top: var(--space-2);
    color: var(--steel);
    font-size: var(--text-2xs);
    line-height: var(--lh-tight);
}

.video-prompt-help i { color: var(--amber); flex-shrink: 0; }

/* Without a width cap a 720p clip pushes the results column wider than the page. */
.video-result__player {
    width: 100%;
    max-width: 100%;
    aspect-ratio: 16 / 9;
    display: block;
    background: var(--flap-black);
    border: 1px solid var(--rule);
    box-shadow: var(--inset-cell);
}

/* ── Session cost breakdown ─────────────────────────────────────────────────
   The popover's figures were in a `table table-sm table-dark table-borderless`, none of
   which exists any more — so the breakdown rendered as a bare browser table on a dark
   panel: serif-ish default type, default cell padding, no alignment. */
.session-cost-popover__body { padding: var(--space-2) var(--space-3) var(--space-3); }

.session-cost-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-2xs);
    margin-bottom: var(--space-2);
}

.session-cost-table td {
    padding: 0.25rem 0.35rem;
    border: none;
    vertical-align: middle;
}

/* Figures right-aligned and tabular, so the column of prices reads as a column. */
.session-cost-table td:not(:first-child) {
    text-align: right;
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.session-cost-table tfoot td {
    border-top: 1px solid var(--rule-strong);
    padding-top: 0.4rem;
    font-weight: 700;
}


/* ═══ Split upload panel (lg+) ══════════════════════════════════════════
   A feature page's panel lays the photo beside the options and the action, so the whole
   setup fits a laptop screen instead of scrolling past a full-width drop zone. Below lg the
   two columns stack exactly as before. Unlayered, like the camera block above, so it wins
   over the compact-chip rules in the components layer. */
@media (min-width: 1024px) {
    .upload-panel--split {
        display: grid;
        grid-template-columns: minmax(280px, 24rem) minmax(0, 1fr);
        column-gap: var(--space-4);
    }

    /* The drop zone fills its column's height, level with the options beside it, rather than
       stopping at 132px above an empty column. A bigger target, and no dead space. */
    .upload-panel--split .upload-panel__source { display: flex; flex-direction: column; }
    .upload-panel--split .upload-panel__intake { flex: 1; }
    .upload-panel--split .upload-panel__settings { align-self: start; }

    .upload-panel--split .upload-panel__settings > :first-child { margin-top: 0; }

    /* The source column has the height to show the photo, so a loaded photo keeps its
       preview here rather than collapsing to the 64px chip a stacked layout needs. */
    .upload-panel--split .drop-zone--compact { justify-content: center; }
    .upload-panel--split .drop-zone--compact .drop-zone__preview { display: block; padding: 0; }
    .upload-panel--split .drop-zone--compact .drop-zone__preview img.preview-image { max-height: 320px; max-width: 100%; margin: 0 auto; }
    .upload-panel--split .drop-zone--compact .drop-zone__preview-overlay {
        position: absolute;
        background: rgba(7,7,8,0.72);
        opacity: 0;
        justify-content: center;
    }
    .upload-panel--split .drop-zone--compact:hover .drop-zone__preview-overlay,
    .upload-panel--split .drop-zone--compact:focus-within .drop-zone__preview-overlay { opacity: 1; }
    .upload-panel--split .drop-zone--compact .drop-zone__preview-hint {
        background: var(--flap-shadow);
        box-shadow: var(--inset-cell);
        padding: 0.45rem 0.9rem;
        color: var(--flap-white);
    }
}
