/* blazewheel.css — 🎡 Spin the Wheel.
 *
 * Modeled on blazeplinko.css: same dark-panel, gold-glow, Comic-Sans identity as
 * the rest of the site, and self-sufficient on purpose — nav.css is the only
 * other stylesheet this page links, so the .viewer-info-btn / .viewer-modal-*
 * "how it works" block and the footer/trust-strip rules are copied here rather
 * than shared.
 *
 * nav.css CONSUMES --navbar-bg, --glow, --c-gold and --c-orange from the page's
 * own stylesheet. A page that forgets them renders the navbar transparent with
 * no shadow, and an unresolved var() fails silently. They are declared first,
 * below, for exactly that reason.
 *
 * THE WHEEL'S MOTION CONTRACT, in one place. blazewheel.js writes exactly two
 * custom properties onto the SVG rotor group (.bw-rotor):
 *   --bw-rot      the target rotation in degrees (a plain number + "deg" string)
 *   --bw-spin-ms  how long the deceleration takes (0 for instant placement)
 * The transition below turns those into the spin-down. The easing is a strong
 * ease-out: several fast revolutions bleeding off into a crawl — a real wheel
 * braking, not a linear rotate. JS owns WHERE it stops (the emitted wedge);
 * CSS owns HOW it feels. Idle drift is a separate keyframe on the wheelbox's
 * [data-state="idle"] so it can never fight the rotor's transition.
 */

*, *::before, *::after { box-sizing: border-box; }

:root {
    --nav-h: 80px;
    --touch: 44px;

    /* The four tokens nav.css needs. Do not remove. */
    --navbar-bg: rgba(0, 0, 0, 0.95);
    --glow: 0 0 10px rgba(255, 69, 0, 0.7);
    --c-gold: #ffcf48;
    --c-orange: orange;

    --c-red: #ff4500;
    --c-green: #2ecc71;

    /* Wheel geometry. One number: everything on the hero derives from it. */
    /* Owner directive, 2026-08-08: "maybe make the wheel a bit bigger and
       number a bit smaller but they should fit." The wedge labels are sized
       in SVG user units against the chord their own wedge offers (see
       buildWheel), so a bigger box makes every label bigger on screen at the
       SAME user-unit size — the two changes compound rather than cancel. */
    --bw-size: 440px;

    /* TEXT CONTRAST. Two solid warm neutrals, both well clear of 7:1 on the
       dark panel (same pair as blazeplinko.css — translucent text over a
       translucent panel has no fixed contrast ratio at all). */
    --c-soft: #f2ece0;
    --c-dim: #ded6c6;
}

body {
    margin: 0;
    padding: 0;
    font-family: var(--font-body, "Comic Sans MS", system-ui, sans-serif);
    background: url('images/BlazeitBG.png') no-repeat center center;
    background-size: 100% auto;
    background-repeat: no-repeat;
    background-position: center center;
    background-attachment: fixed;
    background-color: #b0e8f7;
    color: white;
    display: flex;
    flex-direction: column;
    align-items: center;
    min-height: 100vh;
    width: 100%;
    overflow-x: hidden;
    padding-top: calc(var(--nav-h) + 24px);
}

@media screen and (max-width: 768px) {
    body { padding-top: calc(var(--nav-h) + 16px); }
}

/* Sticky footer: body is the flex column, main takes the slack, footer
   stretches across the bottom. THREE COLUMNS, same arrangement as
   blazeplinko.html; minmax(0, …) on every track is load-bearing (a long
   unbroken string would otherwise blow a track wide). */
.container {
    flex: 1 1 auto;
    width: 100%;
    max-width: 1500px;
    padding: 2rem;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.75fr) minmax(0, 1fr);
    align-items: start;
    gap: 20px;
}

.info-panel,
.game-panel {
    min-width: 0;
    background: rgba(0, 0, 0, 0.75);
    padding: 25px;
    border-radius: 20px;
    box-shadow: 0 0 10px rgba(255, 69, 0, 0.6);
}

.game-panel { text-align: center; }
.info-panel { text-align: left; }
.info-panel h3, .info-panel h2 { text-align: center; }

.title {
    font-size: 2rem;
    margin-bottom: 5px;
    color: var(--c-gold, #ffcf48);
    text-shadow: 0 0 6px #ff8c00, 0 0 10px #ff4500;
    font-weight: bold;
}

.tagline {
    margin: 0 0 14px;
    color: var(--c-soft);
    font-size: 0.95rem;
}

.wallet-btn {
    padding: 10px 20px;
    border-radius: 10px;
    background-color: orange;
    color: black;
    font-weight: bold;
    border: none;
    cursor: pointer;
    min-height: var(--touch);
    transition: background-color 0.2s, opacity 0.2s;
    font-family: inherit;
    font-size: 1rem;
}
.wallet-btn:hover:not(:disabled) { background-color: darkorange; }
.wallet-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.wallet-btn:focus-visible { outline: 3px solid var(--c-gold); outline-offset: 2px; }

.stats {
    background-color: rgba(0, 0, 0, 0.6);
    border-radius: 15px;
    padding: 20px;
    margin-bottom: 20px;
    box-shadow: 0 0 10px rgba(255, 69, 0, 0.5);
    text-align: left;
    overflow-wrap: break-word;
    word-break: break-word;
    max-width: 100%;
}
.stats h3, .stats h2 { margin: 0 0 10px; color: var(--c-gold, #ffcf48); }
.stats p { margin: 0 0 6px; font-size: 0.95rem; }
.stats p:last-child { margin-bottom: 0; }

/* ---- The hero ---- */
.bw-hero {
    position: relative;   /* anchors .bw-mute-btn to this block's own corner */
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 0 0 6px;
}
.bw-hero .bw-result,
.bw-hero .bw-quote {
    width: 100%;
    max-width: 640px;
}

/* ---- Banners ---- */
.bw-banner {
    border-radius: 12px;
    padding: 12px 16px;
    margin: 0 0 14px;
    font-size: 0.95rem;
    text-align: center;
}
.bw-banner[hidden] { display: none; }
.bw-banner-warn {
    background: rgba(255, 69, 0, 0.18);
    border: 1px solid rgba(255, 122, 24, 0.7);
    color: #ffd9a8;
}

/* ---- The wheel ----
   The box is the pointer's containing block; the SVG fills it. max-width keeps
   it inside the spine at every breakpoint; width:100% + aspect-ratio keeps it
   square without JS measuring anything. */
.bw-wheelbox {
    position: relative;
    width: min(var(--bw-size), 92%);
    aspect-ratio: 1 / 1;
    margin: 6px auto 10px;
}
.bw-wheelbox svg {
    display: block;
    width: 100%;
    height: 100%;
    filter: drop-shadow(0 0 14px rgba(255, 69, 0, 0.5));
}

/* The rotor — the <g> blazewheel.js rotates. transform-origin must be the SVG
   centre (the viewBox is -100..100 both axes, so 0 0 IS the centre; specifying
   it explicitly guards against browsers defaulting to the element box).
   The transition IS the deceleration: --bw-spin-ms is the whole reveal window
   (1.5s-5s, drawn per spin) and the easing is how it bleeds off.

   THE EASING IS EXACT, NOT EYEBALLED (owner directive, 2026-08-08: "wheel
   spin animation should last 1,5 to 5 sec and according to that time it has
   to start fast and go slow till it stops at the correct outcome").
   cubic-bezier(0.33, 0.67, 0.67, 1) is the one cubic Bézier whose x(s) = s
   and y(s) = 2s - s² — i.e. EXACTLY the position curve of a wheel under
   constant friction coming to rest: it launches at twice the average speed
   and decelerates linearly to zero at the moment it lands. Nothing else in
   the range behaves like a real wheel; the old curve (0.12, 0.72, 0.15, 1)
   dumped almost all the travel into the first fifth of the window, which
   over a 5-second spin would leave four seconds of a wheel that looks
   frozen. blazewheel.js scales the number of revolutions with the drawn
   duration so the LAUNCH speed stays constant across the whole range — see
   SPIN_LAUNCH_DPS there. */
.bw-rotor {
    transform: rotate(var(--bw-rot, 0deg));
    transform-origin: 0 0;
    transition: transform var(--bw-spin-ms, 0ms) cubic-bezier(0.33, 0.67, 0.67, 1);
    will-change: transform;
}

/* Idle drift: the WHOLE wheelbox slowly turns, inviting the spin — a separate
   property owner from the rotor's transition, so the two can never fight.
   Stands down the moment the box leaves the idle/armed states.

   ARMED drifts too, slightly quicker (owner request, 2026-08-13: while the
   oracle decides, "make the wheel slightly spin instead of being static").
   This is honest motion: a constant drift lands nowhere and predicts
   nothing — the wedge still does not exist until the chain answers. When
   settleWheel() flips the box to "spinning", this animation stands down and
   the rotor's own multi-revolution launch takes over; the outer svg's snap
   back to 0 vanishes inside the launch, exactly as it always has from the
   idle state. */
@keyframes bwIdleDrift {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}
.bw-wheelbox[data-state="idle"] svg {
    animation: bwIdleDrift 90s linear infinite;
}
.bw-wheelbox[data-state="armed"] svg {
    animation: bwIdleDrift 48s linear infinite;
}

/* The fixed pointer at the top. Belongs to the page, never rotates. */
.bw-pointer {
    position: absolute;
    top: -6px;
    left: 50%;
    transform: translateX(-50%);
    width: 0;
    height: 0;
    border-left: 13px solid transparent;
    border-right: 13px solid transparent;
    border-top: 26px solid var(--c-gold);
    filter: drop-shadow(0 0 6px rgba(255, 140, 0, 0.9));
    z-index: 2;
    transition: filter 0.2s;
}
/* Armed: the bet is escrowed, the wedge does not exist yet, and the pointer
   says so — a hot glow, no wheel movement. */
.bw-wheelbox[data-state="armed"] .bw-pointer {
    border-top-color: var(--c-orange);
    filter: drop-shadow(0 0 10px rgba(255, 69, 0, 1)) drop-shadow(0 0 18px rgba(255, 207, 72, 0.8));
    animation: bwPointerArmed 1.1s ease-in-out infinite;
}
@keyframes bwPointerArmed {
    0%, 100% { transform: translateX(-50%) scale(1); }
    50%      { transform: translateX(-50%) scale(1.18); }
}

/* Wedge tiers — cold for busts, warming with the multiplier, fire for the
   jackpot sliver. Applied by blazewheel.js from the LIVE table. These are SVG
   fills, so the custom properties are consumed via fill on the path. */
.bw-wedge { stroke: rgba(0, 0, 0, 0.55); stroke-width: 1; }
.bw-wedge-bust { fill: #35506b; }
.bw-wedge-low  { fill: #6fb7d6; }   /* 1.5x */
.bw-wedge-mid  { fill: var(--c-gold); }   /* 2x-3x */
.bw-wedge-hot  { fill: #ff9d3c; }   /* 5x */
/* The jackpot sliver — a permanent soft pulse (owner visuals directive,
   2026-08-07: "jackpot = glow not image") so the rarest wedge on the wheel is
   discoverable at a glance even before any spin, on top of its own
   fire-red fill. .bw-wedge-hit.bw-wedge-jackpot below overrides this with
   a stronger pulse once it actually lands — more specific selector, wins
   normally. */
.bw-wedge-jackpot {
    fill: var(--c-red);
    animation: bwJackpotIdleGlow 2.2s ease-in-out infinite;
}
@keyframes bwJackpotIdleGlow {
    0%, 100% { filter: drop-shadow(0 0 2px rgba(255, 69, 0, 0.7)); }
    50%      { filter: drop-shadow(0 0 7px rgba(255, 69, 0, 1)); }
}
.bw-wedge-label {
    fill: #14202e;
    font-family: inherit;
    font-weight: bold;
    text-anchor: middle;
    user-select: none;
}
.bw-wedge-bust + .bw-wedge-label, .bw-wedge-label.on-dark { fill: #dfe9f4; }

/* The landed wedge: lit and pulsing until the next spin clears it. SVG
   animates fine with CSS filter/opacity — still compositor-friendly. */
.bw-wedge-hit {
    filter: brightness(1.6) drop-shadow(0 0 8px rgba(255, 207, 72, 0.95));
}
.bw-wedge-hit.bw-wedge-jackpot {
    animation: bwJackpotWedge 1s ease-in-out infinite;
}
@keyframes bwJackpotWedge {
    0%, 100% { filter: brightness(1.6) drop-shadow(0 0 8px rgba(255, 207, 72, 0.95)); }
    50%      { filter: brightness(2.2) drop-shadow(0 0 20px rgba(255, 69, 0, 1)); }
}

/* Hub cap over the rotor centre, so the wedge tips don't read as a pie chart.
   The SUN mascot (an SVG <image>, blazewheel.js) sits inside this circle,
   clipped to it — .bw-hub is now just the coin backdrop + gold ring frame. */
.bw-hub { fill: #1a0d00; stroke: var(--c-gold); stroke-width: 2; }
.bw-hub-label { fill: var(--c-gold); font-weight: bold; text-anchor: middle; user-select: none; }

/* ---- Corner mascots (owner visuals directive, 2026-08-07) ----
   CAT + RABBIT flank the wheel, entirely OUTSIDE the SVG and the rotor:
   plain <img> siblings of the SVG inside #bwWheelBox (position:relative),
   positioned to bleed just past the box's empty corner triangles (the
   circle is inscribed in the square box, so each corner already has real
   empty space before any wedge begins) — they never reach the circle
   itself. Purely decorative: no pointer events, aria-hidden, and dropped
   below 768px alongside the smaller wheel size, where the panel needs the
   width back. Clarity outranks decoration. */
.bw-mascot {
    position: absolute;
    width: 72px;
    height: auto;
    pointer-events: none;
    user-select: none;
    z-index: 1;
    filter: drop-shadow(0 0 8px rgba(0, 0, 0, 0.55));
    animation-duration: 4.6s;
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
}
.bw-mascot-cat {
    top: -6%;
    left: -12%;
    transform: rotate(-9deg);
    animation-name: bwMascotBobCat;
}
.bw-mascot-rabbit {
    bottom: -5%;
    right: -12%;
    transform: rotate(8deg);
    animation-name: bwMascotBobRabbit;
    animation-delay: 2.3s;
}
@keyframes bwMascotBobCat {
    0%, 100% { transform: rotate(-9deg) translateY(0); }
    50%      { transform: rotate(-9deg) translateY(-6px); }
}
@keyframes bwMascotBobRabbit {
    0%, 100% { transform: rotate(8deg) translateY(0); }
    50%      { transform: rotate(8deg) translateY(-6px); }
}

/* Legend line under the wheel: the thin wedges (5x, the 42x sliver) are hard
   to label in-place, so the readout names every tier with its odds. Built by
   JS from the live table, textContent only. */
.bw-wheel-readout {
    margin: 8px 0 12px;
    font-size: 1.05rem;
    color: var(--c-gold);
    font-weight: bold;
}

/* ---- Result + quote ---- */
.bw-result {
    border-radius: 14px;
    padding: 14px 16px;
    margin: 0 auto 10px;
    font-size: 1.05rem;
    font-weight: bold;
    text-align: center;
}
.bw-result[hidden] { display: none; }
.bw-result.win {
    background: rgba(46, 204, 113, 0.16);
    border: 1px solid rgba(46, 204, 113, 0.8);
    color: #b8ffcf;
}
.bw-result.loss {
    background: rgba(255, 69, 0, 0.16);
    border: 1px solid rgba(255, 122, 24, 0.8);
    color: #ffd9a8;
}
.bw-result.neutral {
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.35);
    color: #fff;
}
.bw-quote {
    margin: 0 auto 16px;
    min-height: 1.2em;
    font-size: 0.95rem;
    font-style: italic;
    color: var(--c-soft);
    text-align: center;
}

/* ---- The console ---- */
.bw-console {
    text-align: center;
    border: 1px solid rgba(255, 122, 24, 0.55);
    box-shadow: 0 0 14px rgba(255, 69, 0, 0.55);
}
.bw-budget-line {
    margin: 0 0 8px;
    font-size: 0.95rem;
    color: var(--c-gold);
    font-weight: bold;
    min-height: 1.2em;
}
.bw-bet-summary { margin: 0 0 6px; font-size: 1rem; color: var(--c-soft); }
#bwBetText { color: var(--c-gold); font-weight: bold; }
.bw-payout-preview { margin: 0 0 8px; font-size: 0.95rem; color: var(--c-gold); min-height: 1.2em; }

/* Native-S oracle fee, VRF mode only. Cooler than the gold money copy on
   purpose — this is a cost in a different currency, not part of the payout. */
.bw-vrf-fee {
    margin: 0 0 10px;
    padding: 8px 10px;
    border-radius: 8px;
    background: rgba(120, 180, 255, 0.1);
    border: 1px solid rgba(120, 180, 255, 0.35);
    font-size: 0.85rem;
    color: #bcd9ff;
}
.bw-vrf-fee[hidden] { display: none; }

/* ---- Amount + budget ---- */
.bw-amount, .bw-budget { text-align: left; }
.bw-group-label { margin: 0 0 8px; font-size: 0.9rem; color: var(--c-soft); }
#bwAmountInput {
    width: 100%;
    padding: 12px 14px;
    min-height: var(--touch);
    border-radius: 10px;
    border: 1px solid rgba(255, 207, 72, 0.45);
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-family: inherit;
    font-size: 1.1rem;
}
.bw-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.bw-chip, .bw-budget-chip {
    flex: 1 1 60px;
    min-height: var(--touch);
    padding: 8px 10px;
    border-radius: 999px;
    border: 1px solid rgba(255, 207, 72, 0.45);
    background: rgba(0, 0, 0, 0.5);
    color: var(--c-gold);
    font-family: inherit;
    font-size: 0.95rem;
    cursor: pointer;
    transition: background-color 0.15s, border-color 0.15s;
}
.bw-chip:hover:not(:disabled), .bw-budget-chip:hover { background: rgba(255, 122, 24, 0.2); border-color: rgba(255, 207, 72, 0.95); }

.bw-chip[aria-pressed="true"],
.bw-budget-chip[aria-pressed="true"] {
    background: orange;
    color: #000;
    border-color: #fff;
    font-weight: bold;
}
.bw-budget .bw-chips { margin-top: 0; }
.bw-budget-note { margin: 10px 0 0; font-size: 0.9rem; color: var(--c-dim); min-height: 1.2em; }

.bw-limits { margin: 10px 0 0; font-size: 0.9rem; color: var(--c-dim); }
.bw-amount .bw-limits:last-child { margin-bottom: 0; }
.bw-amount-error { margin: 8px 0 0; font-size: 0.9rem; color: #ff8f6b; }
.bw-amount-error[hidden] { display: none; }

/* ---- Actions ---- */
.bw-actions { margin-top: 0; }
.bw-action-btn { display: block; width: 100%; font-size: 1.15rem; padding: 14px 20px; }
.bw-reclaim-btn { display: block; width: 100%; margin-top: 10px; background-color: var(--c-gold); }
.bw-reclaim-btn[hidden] { display: none; }
.bw-step { margin: 10px 0 0; font-size: 0.9rem; color: var(--c-dim); text-align: center; }
.bw-step[hidden] { display: none; }
.bw-hint { margin: 6px 0 0; font-size: 0.9rem; line-height: 1.5; color: var(--c-dim); text-align: center; }

.bw-tx-status { margin: 12px 0 0; font-size: 0.95rem; color: var(--c-gold, #ffcf48); min-height: 1.2em; }
.bw-tx-link { margin: 6px 0 0; font-size: 0.9rem; }
.bw-tx-link a { color: var(--c-gold); }
/* The Receipt deep link sits on its own line under the SonicScan link. */
.bw-tx-link a.receipt-link { display: block; margin-top: 4px; }

/* ---- History ---- */
.bw-history { list-style: none; margin: 0; padding: 0; }
.bw-history li {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    justify-content: space-between;
    padding: 8px 0;
    border-bottom: 1px dashed rgba(255, 255, 255, 0.15);
    font-size: 0.9rem;
}
.bw-history li:last-child { border-bottom: none; }
.bw-history .bw-h-call { color: var(--c-gold); }
.bw-history .bw-h-result.win { color: #b8ffcf; }
.bw-history .bw-h-result.loss { color: #ffb3a0; }
.bw-history .bw-h-result.pending { color: #ffd9a8; }
.bw-history-status { margin: 8px 0 0; font-size: 0.9rem; color: var(--c-dim); }

/* ---- Jackpot Winners ---- */
/* One element carries both the winner line and the "nobody yet" line, so it
   gets one neutral colour rather than a win tint a JS path would have to
   remember to remove. "Nobody has hit it yet" is information, not a failure,
   and it should not look like an error. */
.bw-jp-line {
    margin: 6px 0 0;
    font-size: 0.9rem;
    line-height: 1.5;
    color: var(--c-soft);
    /* A wallet + "42×" must never widen the panel on a phone. */
    overflow-wrap: anywhere;
}
.bw-jp-stamp { margin: 6px 0 0; font-size: 0.78rem; color: var(--c-dim); }

/* ---- At-a-glance panels ---- */
.bw-howto p { margin: 0 0 8px; font-size: 0.9rem; line-height: 1.55; color: var(--c-soft); }
.bw-howto p:last-child { margin-bottom: 0; }
.bw-howto strong { color: var(--c-gold); }
/* The 3-steps-blockhash / 2-steps-VRF note (owner request, 2026-08-13). Reads
   as a callout under the ritual, not as another step. */
.bw-mode-note {
    margin-top: 10px !important;
    padding: 8px 10px;
    border-left: 3px solid var(--c-orange);
    border-radius: 6px;
    background: rgba(255, 140, 0, 0.09);
}

.bw-gameinfo p { line-height: 1.5; color: var(--c-soft); }
/* Every one of these is a LIVE value read off getGameSettings — gold, like
   every other number on this page that came from the chain. */
.bw-gameinfo p > span,
.bw-gameinfo .bw-info-list li { color: var(--c-gold); font-weight: bold; }

.bw-info-label { margin: 8px 0 4px; }
.bw-gameinfo .bw-info-label:first-of-type { margin-top: 0; }
.bw-info-list {
    list-style: none;
    margin: 0 0 6px;
    padding: 0 0 0 6px;
    font-size: 0.95rem;
    line-height: 1.6;
}
.bw-info-list li { overflow-wrap: anywhere; }

.bw-verify-head {
    margin: 14px 0 6px;
    padding-top: 12px;
    border-top: 1px dashed rgba(255, 255, 255, 0.2);
    color: var(--c-gold);
    font-weight: bold;
}
.bw-verify p { font-size: 0.9rem; }
.bw-verify-note { color: var(--c-dim); font-size: 0.9rem; }
.bw-verify-note strong { color: var(--c-gold); }
.contract-link { color: var(--c-gold, #ffcf48); font-weight: bold; text-decoration: none; overflow-wrap: anywhere; }
.contract-link:hover { color: orange; text-decoration: underline; }

/* ---- a11y helpers ---- */
.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}
a:focus-visible, button:focus-visible, input:focus-visible {
    outline: 3px solid var(--c-gold);
    outline-offset: 2px;
}
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    background: #000;
    color: var(--c-gold);
    padding: 10px 16px;
    z-index: 2000;
    border-radius: 0 0 8px 0;
}
.skip-link:focus { left: 0; }

/* ---- Footer — same mechanism as every other page ---- */
.footer {
    width: 100%;
    text-align: center;
    padding: 10px 0;
    background: rgba(0, 0, 0, 0.8);
    color: white;
    position: relative;
    font-size: 14px;
    margin-top: 20px;
    align-self: stretch;
}
.trust-strip {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    align-items: baseline;
    gap: 4px 12px;
    margin-top: 6px;
    padding: 0 12px;
    font-size: 12px;
    color: #ded6c6; /* opaque: translucent text over a translucent panel has no contrast ratio (READABILITY rule) */
}
.trust-strip a { color: var(--c-gold); text-decoration: none; }
.trust-strip a:hover, .trust-strip a:focus-visible { color: var(--c-orange); text-decoration: underline; }
.trust-sep { opacity: 0.5; }

/* ---- "how it works" info button + modal — copied from blazeplinko.css ---- */
.viewer-info-btn {
    display: inline-block;
    font-family: inherit;
    font-size: 14px;
    margin: 0 auto 16px;
    padding: 6px 14px;
    min-height: var(--touch);
    border-radius: 999px;
    border: 1px solid rgba(255, 207, 72, .6);
    background: rgba(0, 0, 0, .45);
    color: var(--c-gold);
    cursor: pointer;
    transition: background-color .2s ease, border-color .2s ease;
}

.viewer-info-btn:focus-visible { outline: 3px solid var(--c-gold); outline-offset: 2px; }

.viewer-modal {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
}


.viewer-modal-card {
    position: relative;
    width: 100%;
    max-width: 520px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
    padding: 26px 24px 24px;
    border-radius: 16px;
    border: 2px solid #ff7a18;
    background: rgba(10, 6, 2, .97);
    color: #fff;
    box-shadow: 0 20px 60px -12px rgba(0, 0, 0, .8), var(--glow);
    animation: bwModalIn .22s ease;
    text-align: center;
}
@keyframes bwModalIn {
    from { opacity: 0; transform: translateY(10px) scale(.98); }
    to   { opacity: 1; transform: none; }
}


.viewer-modal-close:focus-visible { outline: 3px solid var(--c-gold); outline-offset: 2px; }

#bwInfoTitle {
    margin: 0 0 4px;
    font-size: 20px;
    color: var(--c-gold);
    text-shadow: 0 0 6px #ff8c00, 0 0 10px #ff4500;
    text-wrap: balance;
}


.viewer-rap-mode { margin: 0 0 6px; font-weight: bold; color: var(--c-gold); font-size: 15px; }
.viewer-rap { margin: 0 0 8px; font-size: 14px; line-height: 1.7; color: #fff; }
.viewer-rap:last-child { margin-bottom: 0; }
.viewer-rap code { color: var(--c-gold); }


/* ---- The RESULT modal ----
   Built by blazewheel.js through the shared BlazeModal shell (viewer-modal.js),
   which reuses the .viewer-modal-* classes above. The heading BlazeModal
   creates has no id of ours to hang styling on, so it is targeted structurally. */
.viewer-modal-card > h2 {
    margin: 0 0 10px;
    font-size: 22px;
    color: var(--c-gold);
    text-shadow: 0 0 6px #ff8c00, 0 0 10px #ff4500;
    text-wrap: balance;
}
.viewer-modal-body { text-align: center; }

.bw-modal-wedge {
    display: inline-block;
    margin: 0 auto 10px;
    padding: 8px 18px;
    border-radius: 12px;
    font-size: 1.6rem;
    font-weight: bold;
    color: #1a0d00;
    background: var(--c-gold);
    box-shadow: 0 0 20px rgba(255, 122, 24, 0.6);
}
.bw-modal-wedge.loss { background: #6fb7d6; color: #06222e; }

.bw-modal-headline { margin: 0 0 10px; font-size: 1.15rem; font-weight: bold; }
.bw-modal-headline.win { color: #b8ffcf; }
.bw-modal-headline.loss { color: #ffd9a8; }

/* The jackpot headline — the rarest sliver on the wheel, hit. Same pulse the
   landed jackpot wedge carries, so modal and wheel read as one moment. */
.bw-modal-headline.jackpot {
    color: var(--c-gold);
    text-shadow: 0 0 10px #ff8c00, 0 0 18px #ff4500, 0 0 30px rgba(255, 207, 72, 0.9);
    animation: bwJackpotPulse 1s ease-in-out infinite;
}
@keyframes bwJackpotPulse {
    0%, 100% { text-shadow: 0 0 10px #ff8c00, 0 0 18px #ff4500; }
    50%      { text-shadow: 0 0 16px #ff8c00, 0 0 30px #ff4500, 0 0 44px rgba(255, 207, 72, 1); }
}

.bw-modal-quote { margin: 0 0 16px; font-size: 0.95rem; font-style: italic; color: var(--c-soft); }

/* The receipt. A definition list because that is what it is: labelled values. */
.bw-modal-rows {
    display: grid;
    grid-template-columns: auto 1fr;
    gap: 6px 14px;
    margin: 0 0 16px;
    text-align: left;
    font-size: 0.95rem;
}
.bw-modal-rows dt { color: var(--c-dim); }
.bw-modal-rows dd { margin: 0; color: var(--c-gold); font-weight: bold; overflow-wrap: anywhere; }

.bw-modal-tx { margin: 0 0 16px; font-size: 0.9rem; }
.bw-modal-tx a { color: var(--c-gold); }

/* ---- Result effects: money rain (≥5x wins) and smoke (busts) ----
   Particles are emoji text nodes; all motion is here, in keyframes. The layer
   is fixed to the viewport, never takes a pointer event, and sits BELOW the
   modal's z-index 1000 on purpose — the effect plays around the result, never
   over the number the player is trying to read. */
.bw-fx-layer {
    position: fixed;
    inset: 0;
    z-index: 900;
    overflow: hidden;
    pointer-events: none;
}
.bw-fx {
    position: absolute;
    top: 0;
    font-size: var(--bw-fx-size, 26px);
    line-height: 1;
    will-change: transform, opacity;
    user-select: none;
}
.bw-fx-rain { animation: bwFxRain var(--bw-fx-dur, 2.4s) linear var(--bw-fx-delay, 0s) 1 both; }
@keyframes bwFxRain {
    0%   { transform: translate3d(0, -14vh, 0) rotate(0deg); opacity: 0; }
    8%   { opacity: 1; }
    90%  { opacity: 1; }
    100% { transform: translate3d(var(--bw-fx-drift, 0), 106vh, 0) rotate(var(--bw-fx-spin, 360deg)); opacity: 0; }
}
.bw-fx-smoke { animation: bwFxSmoke var(--bw-fx-dur, 2.2s) ease-out var(--bw-fx-delay, 0s) 1 both; }
@keyframes bwFxSmoke {
    0%   { transform: translate3d(0, 0, 0) scale(0.55); opacity: 0; }
    18%  { opacity: 0.9; }
    100% { transform: translate3d(var(--bw-fx-drift, 0), -230px, 0) scale(2.1); opacity: 0; }
}
/* Heavier/bigger drops for the jackpot money-rain burst. */
.bw-fx-rain.bw-fx-big {
    font-size: calc(var(--bw-fx-size, 26px) * 1.5);
    filter: drop-shadow(0 0 6px rgba(255, 207, 72, 0.85));
}
/* The jackpot screen-edge flash. */
.bw-fx-layer.bw-fx-jackpot::before {
    content: "";
    position: fixed;
    inset: 0;
    background: radial-gradient(ellipse at center, rgba(255, 207, 72, 0) 50%, rgba(255, 122, 24, 0.6) 100%);
    animation: bwJackpotFlash 900ms ease-out;
}
@keyframes bwJackpotFlash {
    0%   { opacity: 0; }
    16%  { opacity: 1; }
    100% { opacity: 0; }
}

/* ---- THE JACKPOT CELEBRATION (owner brief, 2026-08-09) ----
   blazewheel.js's celebrateJackpot() reuses .bw-fx-jackpot above for the
   screen flash and .bw-fx-rain.bw-fx-big for the cash cascade (same falling
   keyframe as the ordinary money rain, just bigger glyphs and a longer,
   staggered spawn) — this block adds only what is genuinely new: the
   casino-floor edge strobe. steps(1,end) instead of a smooth transition is
   deliberate — a strobe SNAPS between colours, it does not fade. The 0.42s
   cadence sits close to playSiren()'s own wail period so the lights and the
   siren read as one moment, not two effects that happen to overlap. */
.bw-fx-layer.bw-jackpot-strobe::after {
    content: "";
    position: fixed;
    inset: 0;
    pointer-events: none;
    box-shadow:
        inset 0 0 0 10px var(--c-red),
        inset 0 0 46px 16px rgba(255, 69, 0, 0.85);
    animation: bwJackpotStrobe 0.42s steps(1, end) infinite;
}
@keyframes bwJackpotStrobe {
    0%, 100% {
        box-shadow:
            inset 0 0 0 10px var(--c-red),
            inset 0 0 46px 16px rgba(255, 69, 0, 0.85);
    }
    50% {
        box-shadow:
            inset 0 0 0 10px var(--c-gold),
            inset 0 0 54px 20px rgba(255, 207, 72, 0.9);
    }
}

/* ---- The jackpot mute control ----
   Persistent, not just-during-the-celebration: a player can pre-mute before
   ever hitting the sliver. Pinned to the hero's own corner (.bw-hero gets
   position:relative below) rather than inside #bwWheelBox, which carries
   aria-hidden="true" and would make a REAL control invisible to assistive
   tech. */
.bw-mute-btn {
    position: absolute;
    top: 0;
    right: 0;
    z-index: 3;
    min-width: var(--touch);
    min-height: var(--touch);
    border-radius: 50%;
    border: none;
    background: rgba(0, 0, 0, 0.6);
    color: var(--c-gold);
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 0 8px rgba(255, 69, 0, 0.5);
    transition: opacity 0.2s;
}
.bw-mute-btn:hover { opacity: 0.85; }
.bw-mute-btn[aria-pressed="true"] { opacity: 0.55; }
.bw-mute-btn:focus-visible { outline: 3px solid var(--c-gold); outline-offset: 2px; }

/* ---- Motion + small screens ---- */
@media (prefers-reduced-motion: reduce) {
    /* No spin: the rotor is PLACED on the wedge instantly (blazewheel.js also
       sets --bw-spin-ms to 0 and never opens the suspense window when this
       query matches — both directions covered). No drift, no pointer pulse,
       no particles, no modal slide. The result itself is fully readable; what
       is removed is the movement, not the information. */
    .bw-rotor { transition: none; }
    .bw-wheelbox[data-state="idle"] svg { animation: none; }
    .bw-wheelbox[data-state="armed"] .bw-pointer { animation: none; }
    .bw-wedge-jackpot { animation: none; } /* Review 2026-08-07: idle jackpot glow honors reduced motion */
    .bw-wedge-hit.bw-wedge-jackpot { animation: none; }
    .bw-fx-layer { display: none; }
    .bw-fx { animation: none; }
    .bw-fx-layer.bw-fx-jackpot::before { display: none; }
    .bw-fx-layer.bw-jackpot-strobe::after { display: none; }
    .bw-modal-headline.jackpot { animation: none; }
    .viewer-modal-card { animation: none; }
    .wallet-btn, .bw-chip, .bw-budget-chip { transition: none; }
    /* Mascots keep their static tilt (declared as a plain `transform` on
       .bw-mascot-cat/.bw-mascot-rabbit, not just inside the keyframes) —
       only the bob stops. */
    .bw-mascot { animation: none; }
}

/* Below ~1200px three columns stop fitting: the spine needs real width for the
   wheel. So the spine goes full width FIRST and the two reference columns pair
   up underneath it. */
@media screen and (max-width: 1199px) {
    .container { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .game-panel { grid-column: 1 / -1; order: -1; }
    .left-panel { order: 0; }
    .right-panel { order: 1; }
}

@media screen and (max-width: 768px) {
    /* One column, and the SPINE IS FIRST — the wheel and the action button
       have to be the first thing a thumb reaches. Rules second, numbers
       third. EXPLICIT single-column tracks + explicit order — the faucet
       lesson (2026-08-07): a stacking that merely FALLS OUT of available
       width is not a stacking, and tests/blazewheel.spec.js asserts the
       computed order, not the accident. */
    :root { --bw-size: 360px; }
    .container { grid-template-columns: minmax(0, 1fr); padding: 1rem; gap: 16px; }
    .game-panel { grid-column: 1; order: -1; padding: 18px; }
    .info-panel { padding: 18px; }

    /* Clarity outranks decoration (owner directive): below the point the
       wheel itself shrinks, the corner mascots would crowd the console
       right under it — drop them rather than risk a wedge label. */
    .bw-mascot { display: none; }

    .viewer-modal-card { padding: 22px 16px 18px; }
    .viewer-modal-done { width: 100%; }
}

@media screen and (max-width: 380px) {
    /* The narrowest phones: the wheel yields to the panel padding. */
    :root { --bw-size: 300px; }
}

/* 🔁 Replay last spin (2026-08-14). Same pill as the crash page's Replay flight
   and the plinko page's Replay drop — one affordance taught once, three games. */
.bw-replay-btn {
    display: block;
    /* The result banner directly below has margin-top:0, so a 0 bottom margin
       left the pill sitting flush on its border — it read as overlapping.
       12px matches the readout gap above it, so the button sits evenly between
       the two rather than clinging to one. */
    margin: 6px auto 12px;
    padding: 5px 14px;
    font-family: inherit;
    font-size: 0.85rem;
    color: var(--c-gold);
    background: rgba(255, 207, 72, 0.10);
    border: 1px solid rgba(255, 207, 72, 0.55);
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.15s, transform 0.15s;
}
.bw-replay-btn:hover:not(:disabled) { background: rgba(255, 207, 72, 0.22); transform: translateY(-1px); }
.bw-replay-btn:disabled { opacity: 0.45; cursor: default; }
.bw-replay-btn[hidden] { display: none; }
/* [hidden] is set by JS when there is nothing to replay, but the UA rule it
   relies on (display:none) LOSES to the display above — so the button stayed
   visible on every page load and, on the wheel, clickable. Reported live
   2026-08-15. Class+attribute outranks the bare class, so this settles it. */


/* ============================ HUD READOUTS ============================
   Owner request, 2026-08-14: "too much information ... doesn't look like the
   UI of a game."

   The diagnosis was hierarchy, not volume. Every readout was
   `Label: value UNIT` at ONE weight and ONE colour, so "House float" and
   "1,309,996" competed as equals and the panel read as a spec sheet. A game HUD
   does the opposite: the label recedes to a caption, the NUMBER is the thing.

   So: labels shrink, uppercase and dim; the value — which is always the
   element carrying the id — goes big, bold and gold with tabular figures so it
   cannot jitter as it repolls. Row rules give the eye a grid to run down.

   CSS ONLY, and deliberately so. Every value already sits in a span[id] that
   the page scripts write into and the tests assert on by exact text, so
   restyling without touching markup means no id moves, no unit sneaks inside a
   value, and nothing to re-verify but the look.

   `:has(span[id])` is what keeps it honest — it selects only rows that actually
   carry a readout. Prose paragraphs in the same panels (the Jackpot Winners
   taunt, the "not audited" note) are left completely alone; uppercasing those
   would have been the obvious way to wreck this. */
.stats p:has(span[id]) {
    display: block;
    margin: 0;
    padding: 6px 0;
    font-size: 12px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    /* OPAQUE, and 7:1 on the panel. The first version used
       rgba(255,255,255,.42) and the dice page's readability guard caught it
       immediately: translucent text over a translucent panel over a photographic
       background has no fixed contrast ratio at all — it is whatever pixel
       happens to sit behind it. --c-dim is the token that exists for this exact
       job (14.5:1 here). The label still recedes, by SIZE, CASE and TRACKING
       rather than by fading out. */
    color: var(--c-dim, #ded6c6);
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}
.stats p:has(span[id]):last-child { border-bottom: none; }

.stats p:has(span[id]) span[id] {
    display: inline-block;
    margin-left: 2px;
    font-size: 15px;
    font-weight: bold;
    letter-spacing: 0;
    text-transform: none;
    color: var(--c-gold, #ffcf48);
    font-variant-numeric: tabular-nums;
}

/* Panel titles are h2 since 2026-09-13 (as h3 straight under the page's h1 they
   skipped a level in the screen-reader outline). This zero-specificity rule
   restores the browser's h3 metrics they were designed at, so the retag changes
   the outline and not the look; any real panel rule above still wins. */
:where(.stats) h2 { font-size: 1.17em; margin-block: 1em; }
