/* =====================================================
   ===== STRYV4 LUCKY TICKETS · SHOP CSS · ANIMATIONS =====
   ===== Phase C3 · file 3 / 4                       =====
   ===== Tag: STR · file: shop_animations_STR.css    =====
   ===================================================== */

/*
 * All keyframe animations for the shop. Kept in one file for easy
 * tuning and overrides.
 *
 * Honoured cheats:
 *   MOTION_OFF (body.stryv-no-motion)         — kills all animations
 *   SKIP_REVEAL (body.stryv-skip-reveal)      — skips ball-spin animation
 *
 * House conventions: CAPS headers with ===== wrappers, CodeNumber X.Y
 * block labels, British English.
 */





/* =====================================================
   ===== SECTION 1: LUCKY BALL REVEAL ANIMATIONS    =====
   ===================================================== */

/* CodeNumber 1.1 — Lotto-machine spin animation.
   When a new draw is published and within-the-hour, balls spin like a
   slot machine and then settle on their number with a soft pop.
   Each ball is staggered left-to-right by 0.6s.

   The number inside the ball is what changes mid-spin — JS cycles
   through digits during the spin, settling on the final number when
   the spin animation completes. */

@keyframes lucky-ball-spin {
    0% {
        transform: scale(0.5) rotateY(0deg);
        opacity: 0;
    }
    8% {
        opacity: 1;
        transform: scale(1.1) rotateY(0deg);
    }
    /* tumble phase: ball spins on Y axis like a slot reel */
    25%  { transform: scale(1) rotateY(180deg); }
    40%  { transform: scale(1) rotateY(360deg); }
    55%  { transform: scale(1) rotateY(540deg); }
    65%  { transform: scale(1) rotateY(720deg); }
    /* settle bounce */
    78%  { transform: scale(1.18) rotateY(720deg); }
    88%  { transform: scale(0.94) rotateY(720deg); }
    100% { transform: scale(1) rotateY(720deg); opacity: 1; }
}

.lucky-ball.is-spinning {
    animation: lucky-ball-spin 2s cubic-bezier(0.5, 0.05, 0.3, 1) forwards;
}

/* Stagger each ball by 0.6s */
.lucky-ball:nth-child(1).is-spinning { animation-delay: 0s; }
.lucky-ball:nth-child(2).is-spinning { animation-delay: 0.6s; }
.lucky-ball:nth-child(3).is-spinning { animation-delay: 1.2s; }
.lucky-ball:nth-child(4).is-spinning { animation-delay: 1.8s; }
.lucky-ball:nth-child(5).is-spinning { animation-delay: 2.4s; }
.lucky-ball:nth-child(6).is-spinning { animation-delay: 3.0s; }

/* CodeNumber 1.2 — On-settle spark.
   A small radial pulse around each ball as it lands on its final number. */

@keyframes lucky-ball-spark {
    0%   { transform: translate(-50%, -50%) scale(0); opacity: 1; }
    60%  { transform: translate(-50%, -50%) scale(1); opacity: 0.6; }
    100% { transform: translate(-50%, -50%) scale(1.8); opacity: 0; }
}

.lucky-ball__spark {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 80px;
    height: 80px;
    border-radius: 50%;
    background: radial-gradient(circle,
        var(--gold-bright) 0%,
        var(--gold-light) 20%,
        transparent 60%);
    pointer-events: none;
    opacity: 0;
}

.lucky-ball.is-settled .lucky-ball__spark {
    animation: lucky-ball-spark 0.7s ease-out forwards;
}

/* CodeNumber 1.3 — SKIP_REVEAL cheat short-circuit.
   When active, balls just fade in without spinning. */

body.stryv-skip-reveal .lucky-ball.is-spinning {
    animation: lucky-ball-quickfade 0.25s ease-out forwards;
}

@keyframes lucky-ball-quickfade {
    from { opacity: 0; transform: scale(0.92); }
    to   { opacity: 1; transform: scale(1); }
}





/* =====================================================
   ===== SECTION 2: CARNIVAL-NAME SHIMMER            =====
   ===================================================== */

/* CodeNumber 2.1 — A slow gold shimmer that crosses the name text of
   carnival-named tickets. Subtle. The shimmer-delay CSS variable lets
   each card have its own offset so they don't all flash in unison. */

@keyframes name-shimmer {
    0%, 30% {
        background-position: 200% 0;
    }
    50% {
        background-position: 0% 0;
    }
    70%, 100% {
        background-position: -200% 0;
    }
}





/* =====================================================
   ===== SECTION 3: STAR PULSE                       =====
   ===================================================== */

/* CodeNumber 3.1 — A gentle pulse for the ★ badge on starred (EV-edge)
   tickets. Slow, not distracting. */

@keyframes star-pulse {
    0%, 100% {
        transform: scale(1);
        filter: drop-shadow(0 0 4px rgba(255, 216, 112, 0.5));
    }
    50% {
        transform: scale(1.18);
        filter: drop-shadow(0 0 10px rgba(255, 216, 112, 0.9));
    }
}





/* =====================================================
   ===== SECTION 4: TICKET CARD SELECTION EFFECTS    =====
   ===================================================== */

/* CodeNumber 4.1 — Card "wobble in" when selected.
   The sparkle particle burst is driven by JS (Phase C4); this is the
   sub-second "satisfied click" animation on the card itself. */

@keyframes card-select-pop {
    0%   { transform: translateY(-3px) scale(1.02); }
    30%  { transform: translateY(-4px) scale(1.06); }
    55%  { transform: translateY(-2px) scale(1.01); }
    80%  { transform: translateY(-3px) scale(1.025); }
    100% { transform: translateY(-3px) scale(1.02); }
}

.ticket-card.is-selecting {
    animation: card-select-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* CodeNumber 4.2 — "Breathing" glow on selected cards.
   Continuous very-slow pulse from the family colour. */

@keyframes card-breathing-glow {
    0%, 100% {
        box-shadow:
            0 16px 48px rgba(0, 0, 0, 0.65),
            0 0 0 2px var(--card-c3),
            0 0 28px color-mix(in srgb, var(--card-c3) 60%, transparent);
    }
    50% {
        box-shadow:
            0 16px 48px rgba(0, 0, 0, 0.65),
            0 0 0 2px var(--card-c3),
            0 0 42px color-mix(in srgb, var(--card-c3) 80%, transparent);
    }
}

.ticket-card.is-selected {
    animation: card-breathing-glow 2.4s ease-in-out infinite;
}





/* =====================================================
   ===== SECTION 5: SPARKLE BURST PARTICLES         =====
   ===================================================== */

/* CodeNumber 5.1 — The "magic sparkly stars explode out of the icon"
   effect. JS injects 12-18 individual <span> elements into the
   .ticket-card__sparkles container; each gets an inline --angle and
   --distance CSS variable. Animation flies them outward, fades, and
   removes the elements when done. */

.sparkle {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 8px;
    height: 8px;
    pointer-events: none;
    transform: translate(-50%, -50%);
    --angle: 0deg;
    --distance: 80px;
    --colour: var(--card-c3, #e6c63a);
    --size: 8px;
    animation: sparkle-fly 0.7s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}

.sparkle::before {
    content: '';
    position: absolute;
    inset: 0;
    background: radial-gradient(circle,
        var(--gold-bright) 0%,
        var(--colour) 35%,
        transparent 70%);
    border-radius: 50%;
    width: var(--size);
    height: var(--size);
    box-shadow:
        0 0 6px var(--colour),
        0 0 12px color-mix(in srgb, var(--colour) 60%, transparent);
}

@keyframes sparkle-fly {
    0% {
        transform:
            translate(-50%, -50%)
            rotate(var(--angle))
            translateX(0)
            rotate(calc(-1 * var(--angle)))
            scale(0.4);
        opacity: 0;
    }
    20% {
        opacity: 1;
        transform:
            translate(-50%, -50%)
            rotate(var(--angle))
            translateX(calc(var(--distance) * 0.25))
            rotate(calc(-1 * var(--angle)))
            scale(1.2);
    }
    70% {
        opacity: 1;
        transform:
            translate(-50%, -50%)
            rotate(var(--angle))
            translateX(calc(var(--distance) * 0.85))
            rotate(calc(-1 * var(--angle)))
            scale(0.9);
    }
    100% {
        opacity: 0;
        transform:
            translate(-50%, -50%)
            rotate(var(--angle))
            translateX(var(--distance))
            rotate(calc(-1 * var(--angle)))
            scale(0.3);
    }
}

/* CodeNumber 5.2 — Star-shaped sparkle variant.
   When the sparkle has class .sparkle--star, render as a 4-point star
   using clip-path. */

.sparkle--star::before {
    background: var(--gold-bright);
    clip-path: polygon(
        50% 0%,
        61% 39%,
        100% 50%,
        61% 61%,
        50% 100%,
        39% 61%,
        0% 50%,
        39% 39%
    );
    border-radius: 0;
}





/* =====================================================
   ===== SECTION 6: VENDOR-SLOT FILL-IN              =====
   ===================================================== */

/* CodeNumber 6.1 — When a card is added to the vendor strip,
   the empty slot brightens up with a small reveal animation. */

@keyframes vendor-slot-fill-in {
    0% {
        opacity: 0.4;
        transform: scale(0.94);
    }
    60% {
        opacity: 1;
        transform: scale(1.04);
    }
    100% {
        opacity: 1;
        transform: scale(1);
    }
}

.vendor-slot.is-filled.just-filled {
    animation: vendor-slot-fill-in 0.45s var(--ease-pop);
}

/* CodeNumber 6.2 — When a slot is removed, it briefly flashes red then fades. */

@keyframes vendor-slot-remove {
    0% {
        background: rgba(176, 138, 46, 0.25);
        opacity: 1;
    }
    30% {
        background: rgba(198, 58, 58, 0.4);
    }
    100% {
        background: rgba(0, 0, 0, 0.2);
        opacity: 0.6;
    }
}

.vendor-slot.is-removing {
    animation: vendor-slot-remove 0.5s var(--ease-soft) forwards;
}





/* =====================================================
   ===== SECTION 7: GENERATE TICKET DRUMROLL         =====
   ===================================================== */

/* CodeNumber 7.1 — When "Generate Ticket" is clicked, the trademark
   lights border briefly accelerates into a chase before the GIF appears.
   We achieve this by speeding up the lights-march animation duration
   on a temporary class. */

.trademark-frame.is-drumroll::before,
.trademark-frame.is-drumroll::after,
.trademark-frame.is-drumroll .trademark-frame__sides::before,
.trademark-frame.is-drumroll .trademark-frame__sides::after {
    animation-duration: 0.5s;
    filter: drop-shadow(0 0 8px rgba(255, 230, 156, 0.6));
}

/* CodeNumber 7.2 — Button shake on click to give satisfying feedback. */

@keyframes generate-btn-thump {
    0%   { transform: scale(1) translateY(0); }
    25%  { transform: scale(0.96) translateY(2px); }
    55%  { transform: scale(1.04) translateY(-2px); }
    100% { transform: scale(1) translateY(0); }
}

.ticket-vendor-strip__generate-btn.is-thumping {
    animation: generate-btn-thump 0.4s var(--ease-pop);
}





/* =====================================================
   ===== SECTION 8: CARD ARRIVAL ON PAGE LOAD       =====
   ===================================================== */

/* CodeNumber 8.1 — Cards stagger-fade in on initial page render.
   Each card uses an inline --card-index CSS variable (set by JS)
   so they can stagger up to 24 deep. After 24, all subsequent cards
   share the same delay (otherwise the last card waits forever). */

@keyframes card-fade-in {
    from {
        opacity: 0;
        transform: translateY(12px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.ticket-card {
    opacity: 0;
    animation: card-fade-in 0.45s var(--ease-soft) forwards;
    animation-delay: calc(min(var(--card-index, 0), 24) * 0.04s);
}

body.cards-pre-loaded .ticket-card {
    animation: none;
    opacity: 1;
}





/* =====================================================
   ===== SECTION 9: CHEAT POPUP FLASH                =====
   ===================================================== */

/* CodeNumber 9.1 — Already styled in cheat-engine inline CSS; this is
   a hook for shop-side amplification (e.g. the trademark lights briefly
   pulse the new colours when a Lights cheat activates). */

@keyframes lights-just-changed-pulse {
    0%   { filter: brightness(1) drop-shadow(0 0 4px rgba(255, 230, 156, 0.25)); }
    35%  { filter: brightness(1.6) drop-shadow(0 0 18px rgba(255, 230, 156, 0.7)); }
    100% { filter: brightness(1) drop-shadow(0 0 4px rgba(255, 230, 156, 0.25)); }
}

.trademark-frame.is-lights-changed::before,
.trademark-frame.is-lights-changed::after,
.trademark-frame.is-lights-changed .trademark-frame__sides::before,
.trademark-frame.is-lights-changed .trademark-frame__sides::after {
    animation: trademark-lights-march 4s linear infinite,
               lights-just-changed-pulse 0.8s ease-out;
}





/* =====================================================
   ===== SECTION 10: GENERATED TICKET BALL SPIN     =====
   ===================================================== */

/* CodeNumber 10.1 — When the GIF panel appears, the numbers on the
   ticket spin once (lotto machine style) before settling.
   Uses the same lucky-ball-spin keyframes as the top-region balls,
   but applied to .ticket-ball elements on the generated panel. */

.generated-ticket-panel__balls {
    display: inline-flex;
    gap: 12px;
    justify-content: center;
    margin: 18px 0;
    perspective: 800px;
}

.ticket-ball {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background:
        radial-gradient(circle at 30% 28%,
            var(--gold-shimmer) 0%,
            var(--gold-light) 18%,
            var(--gold-mid) 60%,
            var(--gold-deep) 100%);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: var(--font-numerals);
    font-weight: 700;
    font-size: 22px;
    color: var(--ink);
    box-shadow:
        0 4px 12px rgba(0, 0, 0, 0.55),
        inset -2px -3px 6px rgba(0, 0, 0, 0.25),
        inset 1px 2px 4px rgba(255, 230, 156, 0.6);
    /* Triggered by adding .is-spinning class via JS */
}
.ticket-ball.is-spinning {
    animation: lucky-ball-spin 1.8s cubic-bezier(0.5, 0.05, 0.3, 1) forwards;
}
.ticket-ball:nth-child(1).is-spinning { animation-delay: 0s; }
.ticket-ball:nth-child(2).is-spinning { animation-delay: 0.35s; }
.ticket-ball:nth-child(3).is-spinning { animation-delay: 0.7s; }
.ticket-ball:nth-child(4).is-spinning { animation-delay: 1.05s; }
.ticket-ball:nth-child(5).is-spinning { animation-delay: 1.4s; }
.ticket-ball:nth-child(6).is-spinning { animation-delay: 1.75s; }





/* =====================================================
   ===== SECTION 11: MOTION-OFF ESCAPE HATCH         =====
   ===================================================== */

/* CodeNumber 11.1 — The MOTION_OFF cheat (body.stryv-no-motion) and
   prefers-reduced-motion are both honoured in shop_frame_STR.css.
   This section just kills the per-keyframe animations explicitly. */

body.stryv-no-motion .lucky-ball.is-spinning,
body.stryv-no-motion .lucky-ball__spark,
body.stryv-no-motion .ticket-card,
body.stryv-no-motion .ticket-card[data-named="true"] .ticket-card__name,
body.stryv-no-motion .ticket-card__star-badge svg,
body.stryv-no-motion .sparkle,
body.stryv-no-motion .trademark-frame::before,
body.stryv-no-motion .trademark-frame::after,
body.stryv-no-motion .trademark-frame__sides::before,
body.stryv-no-motion .trademark-frame__sides::after,
body.stryv-no-motion .ticket-ball.is-spinning {
    animation: none !important;
}
body.stryv-no-motion .ticket-card {
    opacity: 1;
}
