/* Light motion layer for the hub. Uses opacity / translate / background-size only (no layout), and the individual
   `translate` property so it never fights the `transform` used by hovers and existing animations. */

/* 1. Scroll reveal: hub.js marks below-the-fold elements .bp-reveal (with --d stagger) and adds .is-in when seen.
   Without JS nothing is marked, so content is never stuck hidden. */
.bp-reveal { opacity: 0; translate: 0 24px; transition: opacity 600ms ease-out var(--d, 0ms), translate 600ms cubic-bezier(.2, .7, .2, 1) var(--d, 0ms); }
.bp-reveal.is-in { opacity: 1; translate: none; }

/* 3. Section title bars wipe in from the left (same observer, different look).
   (background-size, not clip-path: a clipped target never reports as intersecting.) */
.bp-reveal.bp-hub-past-banner { opacity: 1; translate: none; background-repeat: no-repeat; background-size: 0% 100%; transition: background-size 900ms cubic-bezier(.65, 0, .35, 1); }
.bp-reveal.bp-hub-past-banner.is-in { background-size: 100% 100%; }
.bp-hub-past-banner .bp-reveal { transition-delay: 300ms; }

/* 2. Hero entrance on load: glow fades in, headline lines rise one by one, then the copy and the mouse. */
@keyframes bp-hub-rise { from { opacity: 0; translate: 0 28px; } to { opacity: 1; translate: none; } }
@keyframes bp-hub-glow-in { from { opacity: 0; } to { opacity: .8; } }
.bp-hub-hero-line { animation: bp-hub-rise 800ms cubic-bezier(.2, .7, .2, 1) backwards; }
.bp-hub-hero-line--1 { animation-delay: 150ms; }
.bp-hub-hero-line--2 { animation-delay: 300ms; }
.bp-hub-hero-line--3 { animation-delay: 450ms; }
.bp-hub-hero-text { animation: bp-hub-rise 800ms cubic-bezier(.2, .7, .2, 1) 650ms backwards; }
.bp-hub-mouse { animation: bp-hub-rise 800ms cubic-bezier(.2, .7, .2, 1) 850ms backwards, bp-hub-bob 2s ease-in-out 1650ms infinite; }
/* backwards fill only: once it ends, the breathe loop owns opacity again. */
.bp-hub-hero .bp-hub-crescent { animation: bp-hub-breathe 6s ease-in-out infinite, bp-hub-drift 14s ease-in-out infinite, bp-hub-glow-in 1400ms ease-out backwards; }

/* 4. Past-event cards: lift on hover, cover zooms slowly, ripple ring behind the play button. */
/* The list is a scroll box: give the lifted card (and its shadow) headroom so its top edge isn't clipped. */
.bp-hub-past-list { padding-top: 8px; margin-top: -8px; }
.bp-hub-past-card { transition: transform 300ms ease, box-shadow 300ms ease, border-color 300ms ease; }
.bp-hub-past-card:hover { transform: translateY(-4px); box-shadow: 0 18px 40px rgba(0, 0, 0, .45), 0 0 0 1px rgba(114, 170, 255, .25); border-color: rgba(114, 170, 255, .4); }
.bp-hub-past-cover img:first-child { transition: transform 900ms cubic-bezier(.2, .7, .2, 1); }
.bp-hub-past-card:hover .bp-hub-past-cover img:first-child { transform: scale(1.06); }
button.bp-hub-past-cover { --play: 104px; }
button.bp-hub-past-cover::after {
    content: ''; position: absolute; left: 50%; top: 50%; width: var(--play); height: var(--play); margin: calc(var(--play) / -2) 0 0 calc(var(--play) / -2);
    border-radius: 50%; border: 2px solid rgba(255, 255, 255, .7); opacity: 0; pointer-events: none;
}
button.bp-hub-past-cover:hover::after { animation: bp-hub-ripple 1.4s ease-out infinite; }
@keyframes bp-hub-ripple { from { opacity: .8; transform: scale(.9); } to { opacity: 0; transform: scale(1.7); } }
@media (min-width: 768px) and (max-width: 1099px) { button.bp-hub-past-cover { --play: 72px; } }
@media (max-width: 767px) { button.bp-hub-past-cover { --play: 36px; } }

/* 5. Glass shine: one light sweep across stat cards and glass buttons on hover. */
.bp-hub-stat, .bp-hub-glass-btn { position: relative; overflow: hidden; isolation: isolate; }
.bp-hub-stat::after, .bp-hub-glass-btn::after {
    content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
    background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, .22) 50%, transparent 65%);
    translate: -120% 0;
}
.bp-hub-stat:hover::after, .bp-hub-glass-btn:hover::after { translate: 120% 0; transition: translate 900ms ease; }

/* 6. Hero glow follows the pointer a little (hub.js sets --mx/--my, desktop pointers only). */
.bp-hub-hero .bp-hub-crescent { margin-left: var(--mx, 0px); margin-top: var(--my, 0px); transition: margin 700ms cubic-bezier(.2, .7, .2, 1); }

/* 7. Compass needle: spins in and settles with the headline, then idles with the existing swing.
   On desktop, hub.js points it at the cursor while the pointer is over the hero (.is-tracking). */
@keyframes bp-hub-needle-in {
    0% { transform: rotate(-720deg); }
    70% { transform: rotate(18deg); }
    85% { transform: rotate(-8deg); }
    100% { transform: rotate(0deg); }
}
.bp-hub-needle { animation: bp-hub-needle-in 1600ms cubic-bezier(.2, .7, .3, 1) 300ms backwards, bp-hub-needle 5s cubic-bezier(.36, .07, .19, .97) 1900ms infinite; }
.bp-hub-needle.is-idle { animation: bp-hub-needle 5s cubic-bezier(.36, .07, .19, .97) infinite; }
.bp-hub-needle.is-tracking { animation: bp-hub-quiver 2.4s ease-in-out infinite; transition: transform 450ms cubic-bezier(.34, 1.4, .64, 1); }
/* Quiver on the separate `rotate` property, so it layers on top of the JS-set transform: a still cursor still has a live needle. */
@keyframes bp-hub-quiver { 0%, 100% { rotate: 0deg; } 25% { rotate: 4deg; } 55% { rotate: -3deg; } 80% { rotate: 1.5deg; } }
