/* ─────────────────────────────────────────────────────────────────────
 * Animation utilities — GPU-only (transform + opacity) for 60fps
 *
 * Design rules followed throughout this file:
 *   - Only `transform` and `opacity` get animated. Width/height/top/left
 *     trigger layout and kill smoothness, so they're avoided.
 *   - `will-change` is set ONLY on elements that are actually animating
 *     (over-using it allocates GPU layers unnecessarily).
 *   - Every animation has a `prefers-reduced-motion: reduce` override
 *     so the site stays usable for motion-sensitive visitors.
 *   - All transitions use the "out-quint" curve (0.16, 1, 0.3, 1) which
 *     feels organic without being bouncy.
 * ─────────────────────────────────────────────────────────────────── */


/* ── 1. Scroll-triggered reveals ────────────────────────────────────── */

/* Wrapped elements start invisible + 28px below their final position.
 * The ScrollAnimator composable adds `.is-visible` when the element
 * enters the viewport (IntersectionObserver, threshold 0.12). */
.scroll-reveal {
    opacity: 0;
    transform: translate3d(0, 28px, 0);
    transition:
        opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
        transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
    will-change: opacity, transform;
}
.scroll-reveal.is-visible {
    opacity: 1;
    transform: translate3d(0, 0, 0);
    /* Once revealed, drop will-change so the element doesn't keep a
     * GPU layer for animations that will never run again. */
    will-change: auto;
}

/* Stagger delays for grid children (apply alongside `.scroll-reveal`).
 * Capped at 8 — beyond that the wait feels like a stutter. */
.scroll-reveal.stagger-0 { transition-delay: 0ms; }
.scroll-reveal.stagger-1 { transition-delay: 70ms; }
.scroll-reveal.stagger-2 { transition-delay: 140ms; }
.scroll-reveal.stagger-3 { transition-delay: 210ms; }
.scroll-reveal.stagger-4 { transition-delay: 280ms; }
.scroll-reveal.stagger-5 { transition-delay: 350ms; }
.scroll-reveal.stagger-6 { transition-delay: 420ms; }
.scroll-reveal.stagger-7 { transition-delay: 490ms; }


/* ── 2. Hero floating orbs ──────────────────────────────────────────── */

/* Three large, heavily-blurred radial gradients drifting on their own
 * loops. Pure CSS keyframes — no JS, no event loop pressure. They run
 * on the compositor thread since only `transform` animates. */

@keyframes float-orb-1 {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
    50%      { transform: translate3d(40px, -36px, 0) scale(1.08); }
}
@keyframes float-orb-2 {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
    50%      { transform: translate3d(-32px, 44px, 0) scale(0.94); }
}
@keyframes float-orb-3 {
    0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
    50%      { transform: translate3d(28px, 30px, 0) scale(1.06); }
}

.hero-orbs {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    /* sits behind hero content; z-index 0 because parent is positioned */
    z-index: 0;
}
.floating-orb {
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
    /* `filter: blur` is GPU-accelerated in modern browsers. The blur
     * is large (90px) so the orbs feel like atmospheric light, not shapes. */
    filter: blur(90px);
    will-change: transform;
    /* Without explicit opacity the gradients can blow out highlights
     * when stacked over the hero glow. */
    opacity: 0.55;
}
.floating-orb-1 {
    width: 360px; height: 360px;
    background: radial-gradient(circle, rgba(159, 52, 240, 0.85), rgba(159, 52, 240, 0) 70%);
    top: -120px;
    left: -100px;
    animation: float-orb-1 13s ease-in-out infinite;
}
.floating-orb-2 {
    width: 320px; height: 320px;
    background: radial-gradient(circle, rgba(74, 144, 226, 0.75), rgba(74, 144, 226, 0) 70%);
    top: 30%;
    right: -140px;
    animation: float-orb-2 16s ease-in-out infinite;
}
.floating-orb-3 {
    width: 280px; height: 280px;
    background: radial-gradient(circle, rgba(255, 80, 160, 0.6), rgba(255, 80, 160, 0) 70%);
    bottom: -100px;
    left: 35%;
    animation: float-orb-3 18s ease-in-out infinite;
}


/* ── 3. Cursor-following gradient blob (desktop only) ──────────────── */

/* A single fixed-position blob that lerps toward the cursor. The
 * ScrollAnimator composable injects exactly one `.cursor-blob` div
 * into the body on init and writes its transform in a rAF loop. */
.cursor-blob {
    position: fixed;
    top: 0;
    left: 0;
    width: 480px;
    height: 480px;
    margin-left: -240px;   /* center on the cursor */
    margin-top: -240px;
    pointer-events: none;
    z-index: 1;
    border-radius: 50%;
    background: radial-gradient(circle,
        rgba(159, 52, 240, 0.22) 0%,
        rgba(74, 144, 226, 0.10) 40%,
        rgba(255, 255, 255, 0) 70%);
    filter: blur(28px);
    /* Start hidden — fades in on first mousemove */
    opacity: 0;
    transition: opacity 0.45s ease;
    will-change: transform, opacity;
    /* Below the navbar, above the page content; mix-blend-mode adds
     * a soft luminosity effect over dark backgrounds. */
    mix-blend-mode: screen;
}
.cursor-blob.is-active { opacity: 1; }

/* Mobile / touch devices: hide the blob entirely. There's no cursor
 * and the blob would otherwise sit static in the corner. */
@media (hover: none), (pointer: coarse) {
    .cursor-blob { display: none; }
}


/* ── 4. Scroll progress bar ─────────────────────────────────────────── */

/* Top-of-viewport 3px bar that fills as the user scrolls down.
 * Uses transform: scaleX (origin: left) — never animates `width`,
 * which would trigger layout on every frame. */
.scroll-progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    transform-origin: left center;
    transform: scaleX(0);
    background: linear-gradient(
        to right,
        rgba(159, 52, 240, 0.95),
        rgba(74, 144, 226, 0.95),
        rgba(255, 80, 160, 0.95)
    );
    z-index: 9999;
    pointer-events: none;
    will-change: transform;
    /* Short transition smooths the rAF ticks for ultra-cheap rendering;
     * we don't want a long delay or scrolling feels uncoupled. */
    transition: transform 0.08s linear;
    /* Subtle glow so it reads on light/dark themes alike */
    box-shadow: 0 0 8px rgba(159, 52, 240, 0.5);
}


/* ── 5. Hero subject spotlight ──────────────────────────────────────── */

/* A large soft radial that lives behind the hero photo so the figure
 * always has a deliberate light source haloing it. Unlike the section
 * background gradient (which is anchored to the *section* and shifts
 * around as breakpoints change), this spotlight is anchored to the
 * *photo container*, so it follows the subject across every layout
 * without manual repositioning.
 *
 * The negative inset (-15%/-8%) lets the halo bleed beyond the photo's
 * bounding box so the soft falloff is visible above the head and to
 * each side — that's what gives the "spotlight on a stage" feel rather
 * than a flat backdrop. */
.hero-spotlight {
    position: absolute;
    top: -15%;
    bottom: -10%;
    left: -8%;
    right: -8%;
    pointer-events: none;
    z-index: 0;
    background: radial-gradient(
        ellipse 70% 65% at 50% 50%,
        rgba(159, 52, 240, 0.55) 0%,
        rgba(159, 52, 240, 0.28) 30%,
        rgba(74, 144, 226, 0.18) 55%,
        transparent 75%
    );
    filter: blur(40px);
    will-change: transform, opacity;
    /* Gentle breathing so the spotlight feels alive without ever being
     * distracting. 7s cycle = slow enough to read as ambient. */
    animation: hero-spotlight-breathe 7s ease-in-out infinite;
}

@keyframes hero-spotlight-breathe {
    0%, 100% {
        transform: scale(1);
        opacity: 0.9;
    }
    50% {
        transform: scale(1.06);
        opacity: 1;
    }
}


/* ── 6. Reduced motion overrides ────────────────────────────────────── */

/* People who set "Reduce motion" in their OS get a deliberately calm
 * version: nothing translates or scales; opacity transitions stay
 * (very short) so content still has a soft fade-in. */
@media (prefers-reduced-motion: reduce) {
    .scroll-reveal {
        opacity: 1;
        transform: none !important;
        transition: opacity 0.2s ease !important;
    }
    .floating-orb,
    .cursor-blob {
        animation: none !important;
        display: none !important;
    }
    .hero-spotlight {
        animation: none !important;
        /* keep the static glow even in reduced-motion — it's a light
         * source, not motion — but lock its transform. */
        transform: none !important;
    }
    .scroll-progress {
        transition: none !important;
    }
}
