/* ====================================================================
   Parallax primitives. Loaded by every page that uses <parallax-section>.
   Mobile-safe: no `background-attachment: fixed` (broken on iOS Safari).
   Honors prefers-reduced-motion AND a localStorage flag set by the footer
   toggle.
   ==================================================================== */

.parallax {
    position: relative;
    min-height: var(--parallax-min-h, 100vh);
    overflow: clip;
    isolation: isolate;
    contain: layout paint;
}

.parallax__layer {
    position: absolute;
    /* -18% bleed gives ~150px of room on a typical 800px hero, enough to
       absorb peak ~128px translate at depth 0.20 with the JS multiplier 640
       without revealing edges. Tested across hero-band (70vh), home-hero
       (100vh), hero-band--short (50vh). */
    inset: -18% 0;
    z-index: 0;
    will-change: transform;
    pointer-events: none;
}

.parallax__layer img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

.parallax__copy {
    position: relative;
    z-index: 2;
    padding: clamp(2rem, 8vw, 6rem) clamp(1rem, 5vw, 4rem);
}

.parallax__copy--center {
    display: grid;
    place-content: center;
    text-align: center;
    min-height: inherit;
}

/* Mobile: dial parallax intensity down via the --parallax-strength CSS var.
   Aggressive translate at touch-scroll speeds risks vestibular discomfort, so
   the JS multiplier is scaled by this value on small viewports. */
@media (max-width: 768px) {
    [data-parallax] { --parallax-strength: 0.55; }
}
@media (min-width: 769px) {
    [data-parallax] { --parallax-strength: 1; }
}

/* Reduced motion: kill all parallax + the long-read fade-in. The JS controller
   early-returns too, but this guarantees correctness even if JS fails to load. */
@media (prefers-reduced-motion: reduce) {
    [data-parallax-layer] { transform: none !important; }
    .longread__media { position: relative; height: 60vh; }
    .longread__body .stanza {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

/* Also kill motion when the user has flipped the footer toggle.
   The toggle adds the class .motion-reduced to <html>. */
.motion-reduced [data-parallax-layer] { transform: none !important; }
.motion-reduced .longread__media { position: relative; height: 60vh; }
.motion-reduced .longread__body .stanza {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
}

/* Progressive enhancement: where the browser supports CSS scroll-driven
   animations (Chrome, Edge, Safari 17.5+), the fade-in runs natively without
   any JS. The JS controller still works as a fallback for everyone else. */
@supports (animation-timeline: view()) {
    .longread__body .stanza {
        animation: fadeUp linear both;
        animation-timeline: view();
        animation-range: entry 0% cover 30%;
        opacity: 1;
        transform: none;
    }
    @keyframes fadeUp {
        from { opacity: 0; transform: translateY(24px); }
        to   { opacity: 1; transform: none; }
    }
}
