/* ─────────────────────────────────────────────────────────────────────────
   winner-spotlight.css — THE WINNER REVEAL, SITE-WIDE.

   Owner ruling 2026-08-30: *"I like this so much that I want this to be the
   default winner instead of the winner card everywhere in desktop/responsive
   mode as well… so replace this with Winner Card everywhere."*

   ⛔ EVERY RULE IS SCOPED TO `html.wsp-spotlight`, WHICH ONLY app-winner.js SETS.
   That is deliberate and it is the graceful-degradation story: if the JavaScript
   never runs, the class is never added, the winner CARD is never hidden, and the
   page behaves exactly as it did before this file existed. A CSS-only hide would
   have taken the card away from a visitor whose reveal could then never appear.

   ⛔ WHY ITS OWN FILE. The Spotlight is not app-only any more, so it could not
   stay in app-mode.css, whose whole contract is that every rule inside is scoped
   to `html.wsp-app`. The alternative — splitting it between picker-ui.css and
   classic-wheel-styles.css — is the documented pair that has now drifted apart
   three separate times (SKILLS 38: .wsp-colour-ctl, .wsp-fair-handle, and the
   seven tab classes). One file, one copy, no pair to keep in sync.

   ⚠️ THE MODAL POPUP IS A DIFFERENT FEATURE and is not touched here. The owner
   was explicit: the winner CARD (below the stage) is what the Spotlight
   replaces; the opt-in winner MODAL stays as it is.
   ───────────────────────────────────────────────────────────────────────── */

/* ── THE WINNER CARD STANDS DOWN ─────────────────────────────────────────
   `display: none`, never the `hidden` attribute: winner-result.js's `visible()`
   reads that attribute and engines branch on it, so setting it would silently
   tell every engine the winner is not showing. The bar also has to stay
   MEASURABLE for measureActions(), and it remains the source of truth for which
   buttons exist — app-winner.js mirrors it. */
html.wsp-spotlight .wsp-result {
    display: none !important;
}

/* ⛔⛔ AND THE HIDDEN BAR TOO — MEASURED AT A 36px STAGE SHIFT WITHOUT THIS.
   picker-ui.css:620 and classic-wheel-styles.css:1055 both carry

       body.zen-mode .main-layout-left .wsp-result[hidden] { display: block !important }

   which exists so the RESERVED slot is laid out exactly like the bar it
   reserves for. Against two important declarations the more specific selector
   wins, and theirs is (0,3,2) to the rule above's (0,2,0) — so in the IDLE state
   the bar kept rendering as a 72px block, and at reveal time `[hidden]` stopped
   matching and it collapsed to 0.

   `fs-reveal-frames.js --size=1440x900` reported it precisely:
       classic-wheel  SHIFT 36px   slot idle 72 -> full 0   grew -72px
       dice-roll      SHIFT 36px   ·  coin-toss 37px  ·  lucky-box 37px
   Half of 72, because the fullscreen column is vertically centred (SKILLS 75).
   That is CARRYOVER §A2 — the defect the owner has reported more than any
   other — reintroduced by a specificity loss, and it is exactly what he
   predicted when he said the reservation would need handling again.

   This selector is (0,4,2): one class more specific than theirs, so it wins
   without touching a rule 128 pages depend on. */
html.wsp-spotlight body.zen-mode .main-layout-left .wsp-result[hidden] {
    display: none !important;
}

/* ── AND THE SPACE IT USED TO RESERVE GOES WITH IT ───────────────────────
   Owner, 2026-08-30: *"in full screen the stage/shifting etc. needs a proper
   handling again of course… because winner card was always below the stage,
   shifting everything even further below… now winner card will be gone, so it
   will be now kind of normal again."* Exactly so.

   `--wsp-zen-bar-h` (and its phone-width sibling) existed for ONE reason: the
   fullscreen column is vertically centred, so a card appearing below the stage
   grew the column and pushed the stage up. That was §A2, reported repeatedly,
   and fixed by reserving the card's height up front. With no card there is
   nothing to reserve, and reserving anyway would leave a block of dead space
   under the stage — the same defect, mirrored.

   ⚠️ THE 38 PER-PAGE VALUES ARE NOT DELETED. They live in 13 stylesheets and
   are now inert *while this class is set*, which keeps the revert a one-line
   change. Deleting them would make going back a 13-file job, and this project
   has already been bitten once by treating "inert" as "safe to remove" from the
   wrong direction (CARRYOVER §B7 round 2). */
html.wsp-spotlight body.zen-mode .main-layout-left .wsp-result-slot {
    min-height: 0 !important;
}

/* ── THE SPOTLIGHT REVEAL ────────────────────────────────────────────────
   Owner ruling 2026-08-30, direction A of four: the stage DIMS, the wheel stays
   where it is, and the winner's name blooms over it. It replaced a bottom sheet
   that carried Save card / Share link — two controls he judged pointless on a
   phone, and which also covered the wheel, the one thing a player wants to look
   at after a spin.

   ⛔ WHITE INK IS SAFE HERE BY CONSTRUCTION, AND THAT IS WHY THE SCRIM IS OPAQUE
   ENOUGH TO MATTER. Measured across the wheel engine's own eight-colour palette,
   NOT ONE segment colour carries white text at AA — they run 1.94 to 4.55, and
   the one that scrapes past does so by 0.05. So the winning colour is never a
   text ground here: it is a soft radial BLOOM behind the name, sitting on a
   near-black scrim, and the name reads against the scrim. That also makes the
   reveal theme-independent — the scrim is dark in light, dark and high-contrast
   alike, so one ink is correct in all three (SKILLS 86: one ink cannot serve two
   grounds, so do not give it two).

   ⚠️ NO CONFETTI IS ADDED. The engines already fire their own through
   confetti-engine.js; a second burst here would double it on every picker. */
.wsp-aw {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Tap anywhere to dismiss — the scrim is the whole surface. */
    cursor: pointer;
}

.wsp-aw[hidden] { display: none; }

.wsp-aw-scrim {
    position: absolute;
    inset: 0;
    background: rgba(6, 8, 13, .82);
    opacity: 0;
    transition: opacity .22s ease;
}

.wsp-aw.is-open .wsp-aw-scrim { opacity: 1; }

/* The winning colour, and the only place it appears. --wsp-result-accent is the
   picker's OWN accent, already declared on .wsp-result by 95 of the page
   stylesheets; app-winner.js reads it off the bar and sets it here. Pages that
   never declared one fall through to the theme accent, which is always defined. */
.wsp-aw-bloom {
    position: absolute;
    inset: 0;
    background: radial-gradient(42% 30% at 50% 46%,
                color-mix(in srgb, var(--wsp-aw-accent, var(--theme-accent, #6366f1)) 55%, transparent),
                transparent 70%);
    opacity: 0;
    transition: opacity .42s ease;
}
.wsp-aw.is-open .wsp-aw-bloom { opacity: 1; }

/* @supports guard: color-mix is widely supported but not universally, and a
   dropped background here would lose the bloom silently. */
@supports not (background: color-mix(in srgb, red 50%, transparent)) {
    .wsp-aw-bloom {
        background: radial-gradient(42% 30% at 50% 46%,
                    var(--wsp-aw-accent, var(--theme-accent, #6366f1)), transparent 70%);
        opacity: 0;
    }
    .wsp-aw.is-open .wsp-aw-bloom { opacity: .5; }
}

.wsp-aw-body {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .35rem;
    padding: 0 1.5rem;
    text-align: center;
    max-width: 30rem;
}

.wsp-aw-art {
    width: 64px;
    height: 64px;
    margin-bottom: .35rem;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transform: scale(.86);
    transition: opacity .3s ease .04s, transform .3s ease .04s;
}
.wsp-aw.is-open .wsp-aw-art { opacity: 1; transform: none; }
.wsp-aw-art[hidden] { display: none; }
.wsp-aw-art img { width: 100%; height: 100%; object-fit: cover; border-radius: 12px; }
.wsp-aw-art svg  { max-width: 100%; max-height: 100%; }

.wsp-aw-kicker {
    margin: 0;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .22em;
    text-transform: uppercase;
    /* Light enough to clear AA on the scrim without competing with the name. */
    color: #cfd4e4;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .3s ease .1s, transform .3s ease .1s;
}
.wsp-aw.is-open .wsp-aw-kicker { opacity: 1; transform: none; }
.wsp-aw-kicker[hidden] { display: none; }

.wsp-aw-name {
    margin: 0;
    font-size: clamp(2.1rem, 11vw, 3.2rem);
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -.02em;
    color: #ffffff;
    text-shadow: 0 4px 30px rgba(0, 0, 0, .55);
    /* Long entries must wrap rather than overflow the viewport. */
    overflow-wrap: anywhere;
    opacity: 0;
    transform: scale(.82) translateY(10px);
    transition: opacity .42s cubic-bezier(.2, .9, .3, 1.25) .06s,
                transform .42s cubic-bezier(.2, .9, .3, 1.25) .06s;
}
.wsp-aw.is-open .wsp-aw-name { opacity: 1; transform: none; }

.wsp-aw-verdict {
    margin: .25rem 0 0;
    font-size: 1rem;
    color: #d7dbe8;
    opacity: 0;
    transition: opacity .3s ease .2s;
}
.wsp-aw.is-open .wsp-aw-verdict { opacity: 1; }
.wsp-aw-verdict[hidden] { display: none; }

.wsp-aw-actions {
    margin-top: 1.35rem;
    display: flex;
    gap: .6rem;
    opacity: 0;
    transition: opacity .3s ease .3s;
}
.wsp-aw.is-open .wsp-aw-actions { opacity: 1; }
.wsp-aw-actions:empty { display: none; }

/* One ghost pill. There is no filled/primary variant any more: the only action
   the sheet still offers is Again, and a solid button competing with the name
   was half of what made the old sheet read as a form. */
.wsp-aw-btn {
    -webkit-appearance: none;
    appearance: none;
    min-height: 44px;
    padding: .6rem 1.5rem;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, .38);
    background: rgba(255, 255, 255, .08);
    color: #ffffff;
    font-size: .95rem;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    transition: background .16s ease, border-color .16s ease, transform .1s ease;
}
.wsp-aw-btn:hover { background: rgba(255, 255, 255, .16); border-color: rgba(255, 255, 255, .6); }
.wsp-aw-btn:focus-visible { outline: 2px solid #ffffff; outline-offset: 3px; }
.wsp-aw-btn:active { transform: translateY(1px); }

/* The dismiss affordance when there is no button at all — which is the common
   case, because A13 hides Again whenever the page's own start control is on
   screen. Without this the reveal is dismissible but does not say so. */
.wsp-aw-hint {
    margin-top: 1.5rem;
    font-size: .78rem;
    letter-spacing: .04em;
    color: #a9b0c4;
    opacity: 0;
    transition: opacity .3s ease .34s;
}
.wsp-aw.is-open .wsp-aw-hint { opacity: 1; }
.wsp-aw-hint[hidden] { display: none; }

@media (prefers-reduced-motion: reduce) {
    .wsp-aw-scrim, .wsp-aw-bloom, .wsp-aw-art,
    .wsp-aw-kicker, .wsp-aw-name, .wsp-aw-verdict,
    .wsp-aw-actions, .wsp-aw-hint { transition: none; }
    /* Anything whose only entrance is a transform must still end up placed. */
    .wsp-aw-name { transform: none; }
    .wsp-aw-kicker { transform: none; }
    .wsp-aw-art { transform: none; }
}
