/* ============================================================
   MOTION — scroll reveals, page-load entrance, reduced motion.
   Timing/easing tokens live in base/tokens.css.
   ============================================================ */

/* Reveal-on-scroll
   Animates `opacity` and `clip-path` only, never `transform`, so it never
   shares a property name with the `transform`-based hover effects. `transition` itself IS a single
   shorthand, though: when a card declares its own hover `transition`, it
   must fold `var(--reveal-transition)` into that same declaration (see
   .news-card, .pub-tile, .tech-item, .team-card...).

   --reveal-transition is (re)defined HERE on .reveal rather than only on
   :root so that `--reveal-i` resolves per element: the stagger delay rides
   inside the reveal segments only, never delaying a card's hover. The
   reveal module in assets/js/modules/reveal.js sets --reveal-i on siblings
   that enter the viewport together. */
/* Text blocks arrive as a fade only, no rise: sliding them in moves the
   text by fractions of a pixel on a temporary layer, so labels and
   buttons look blurry until the slide ends and then visibly snap sharp
   (worst on Windows at 125/150% scaling). */
.reveal {
    --reveal-delay: calc(var(--reveal-i, 0) * var(--stagger));
    --reveal-transition:
        opacity var(--dur-reveal) var(--ease-out) var(--reveal-delay),
        clip-path 1300ms var(--ease-out) var(--reveal-delay);
    opacity: 0;
    transition: var(--reveal-transition);
}
.reveal.is-visible { opacity: 1; }

/* Figures and photos "uncover" top-down instead of sliding — it tells an
   image apart from text arriving next to it. */
.reveal--clip { translate: none; clip-path: inset(0 0 100% 0); opacity: 1; }
.reveal--clip.is-visible { clip-path: inset(0 0 0 0); }

/* Hover zooms on photos: keep each zooming image on its own compositor
   layer for its whole life, so it doesn't "snap" as it settles back.
   Only images and image frames — never anything that carries text: a
   promoted layer is rasterised like a bitmap, which blurs text on screens
   with fractional scaling (Windows 125/150%). Buttons and text cards are
   left on the normal paint path; see base.css for buttons. */
.news-card__img img, .contact-gallery__item, .team-card__photo img,
.tech-card__front img, .lab-grid__link img, .model-detail__figure {
    will-change: transform;
    backface-visibility: hidden;
}
/* The rounded clip around a zooming photo must be its own layer too, or the
   corners flicker square for a frame */
.news-card__img, .team-card__photo, .lab-grid__link, .tech-card__front, .contact-gallery__item {
    isolation: isolate; transform: translateZ(0);
}

/* Paired columns: the second half follows a beat behind the first. */
.deck__tabs.reveal { --reveal-i: 1; }
.deck__panel.reveal { --reveal-i: 2; }
.pi-feature__body.reveal { --reveal-i: 2; }
.research-line__grid .research-line__figure.reveal,
.research-line__grid--reverse .research-line__copy.reveal { --reveal-i: 2; }

/* Home hero — staged entrance on page load (not scroll): eyebrow, title,
   lead, actions, 80ms apart, while the photo settles from a slight zoom.
   Keyframes are fine here: it runs once and is never re-triggered.
   Fade only for the copy, for the same reason as .reveal above. */
@keyframes arz-rise {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes arz-settle {
    from { scale: 1.06; opacity: .4; }
    to   { scale: 1;    opacity: 1; }
}
.hero__copy > * { animation: arz-rise 900ms var(--ease-out) both; }
.hero__copy > :nth-child(1) { animation-delay: 120ms; }
.hero__copy > :nth-child(2) { animation-delay: 200ms; }
.hero__copy > :nth-child(3) { animation-delay: 280ms; }
.hero__copy > :nth-child(4) { animation-delay: 360ms; }
.hero__media { animation: arz-settle 1800ms var(--ease-out) both; }
/* Desktop with the hero video: no entrance on the media layer at all.
   Zooming it made the masked video shimmer, and even a fade animates a
   layer that sits under the copy, which pulls the title and buttons onto
   a temporary layer too — blurry until it ends, then a visible snap. */
@media (min-width: 901px) {
    .hero__media--video { animation: none; }
}

/* Reduced motion: fewer and gentler, not zero. Nothing moves or uncovers;
   content still fades in so arrivals don't feel broken. */
@media (prefers-reduced-motion: reduce) {
    .reveal {
        clip-path: none;
        --reveal-transition: opacity 300ms ease-out;
    }
    .hero__copy > *, .hero__media { animation: none; }
}
