/**
 * Scroll-reveal animations.
 *
 * A fade + rise as each section's content enters the viewport. No library (this
 * is ~30 lines of IntersectionObserver in assets/js/scroll-animations.js) and no
 * editor control — it's applied structurally, so it covers content already in
 * the database and anything added later without touching patterns or markup.
 *
 * SCOPED TO `main`: the footer (and the header) sit outside it, so neither
 * animates.
 *
 * WHAT ANIMATES — two non-overlapping sets, so one animated element is never
 * nested inside another (which would double-fade):
 *   1. A section container's direct children — EXCEPT grid wrappers.
 *   2. The items inside those grids, so a grid ripples in rather than the whole
 *      slab appearing at once.
 * `.grid` (the Every Layout primitive) is the marker for "this holds items";
 * some blocks ARE the grid (checklist, case-study-grid), others wrap one
 * (icon-grid), hence the two grid selectors.
 *
 * pad/image-slider is a third case: on the front end it dissolves into its
 * parent grid with `display: contents`, so it generates NO BOX — opacity and
 * transform have nothing to act on, and IntersectionObserver can never see it.
 * It's skipped, and its two real parts (the Swiper and the arrows/button
 * footer) are animated instead.
 *
 * PROGRESSIVE ENHANCEMENT: the hidden start state is scoped to `.js-animate`, a
 * class the script adds to <html> in the document head. With JS off — or if the
 * script errors, or the browser lacks `:has()` (which invalidates the selector
 * list) — the class never lands or the rule never applies, and everything is
 * simply visible. Content can never be trapped invisible.
 *
 * Only `opacity` and `transform` animate (compositor-friendly, no layout work).
 *
 * NOTE: these selectors are mirrored in scroll-animations.js, which observes the
 * same elements. Change one, change the other.
 */

/* The `:not()`/`:has()` tests are wrapped in `:where()`, which contributes ZERO
   specificity. That is load-bearing, not stylistic: the reveal rule below has to
   outrank every hidden selector here, and a bare `:not()` chain grows in
   specificity each time an exclusion is added — enough of them and the hidden
   state starts beating the reveal, leaving content permanently invisible.
   `:where()` pins these at a flat weight so exclusions can be added freely. */
.js-animate
  main
  .wp-block-pad-container-block
  > *:where(:not(.grid, .pad-image-slider)):where(:not(:has(> .grid))),
.js-animate main .wp-block-pad-container-block > .grid > *,
.js-animate main .wp-block-pad-container-block > *:where(:has(> .grid)) > .grid > *,
.js-animate main .wp-block-pad-container-block > .pad-image-slider > * {
  opacity: 0;
  transform: translateY(var(--wp--custom--motion--animate-distance, 2rem));
  transition:
    opacity var(--wp--custom--motion--animate-duration, 800ms)
      var(--wp--custom--motion--animate-ease, ease-out),
    transform var(--wp--custom--motion--animate-duration, 800ms)
      var(--wp--custom--motion--animate-ease, ease-out);
}

/* Doubled class so this outranks the (more specific) hidden selectors above. */
.js-animate main .wp-block-pad-container-block .is-visible.is-visible {
  opacity: 1;
  transform: none;
}

/* Stagger, so content cascades instead of arriving together. Applies in both
   contexts — a container's own children, and the items within a grid. Capped at
   the 6th so a long grid's last items don't lag noticeably behind. */
.js-animate main .wp-block-pad-container-block > :nth-child(2),
.js-animate main .wp-block-pad-container-block .grid > :nth-child(2),
.js-animate main .wp-block-pad-container-block .pad-image-slider > :nth-child(2) {
  transition-delay: calc(1 * var(--wp--custom--motion--animate-stagger, 120ms));
}

.js-animate main .wp-block-pad-container-block > :nth-child(3),
.js-animate main .wp-block-pad-container-block .grid > :nth-child(3) {
  transition-delay: calc(2 * var(--wp--custom--motion--animate-stagger, 120ms));
}

.js-animate main .wp-block-pad-container-block > :nth-child(4),
.js-animate main .wp-block-pad-container-block .grid > :nth-child(4) {
  transition-delay: calc(3 * var(--wp--custom--motion--animate-stagger, 120ms));
}

.js-animate main .wp-block-pad-container-block > :nth-child(5),
.js-animate main .wp-block-pad-container-block .grid > :nth-child(5) {
  transition-delay: calc(4 * var(--wp--custom--motion--animate-stagger, 120ms));
}

.js-animate main .wp-block-pad-container-block > :nth-child(n + 6),
.js-animate main .wp-block-pad-container-block .grid > :nth-child(n + 6) {
  transition-delay: calc(5 * var(--wp--custom--motion--animate-stagger, 120ms));
}

/* Scroll-reveal never applies inside a hero — a hero is on screen immediately,
   so there's no scroll to wait for. Its text gets its own load animation below.
   Belt-and-braces in case a hero ever nests a container block. */
.js-animate main .pad-hero .wp-block-pad-container-block > *,
.js-animate main .pad-hero .wp-block-pad-container-block .grid > * {
  opacity: 1;
  transform: none;
  transition: none;
  transition-delay: 0s;
}

/* ---- Hero text -----------------------------------------------------------
   The hero is above the fold, so its text plays on ARRIVAL rather than waiting
   for a scroll that may never come — a CSS animation, not the observer.
   One rule covers the home slider and the internal page heroes, since both
   render pad/cover-slide-block.

   The hero IMAGE is deliberately untouched: `.pad-cover-slide__image` is a
   SIBLING of the text wrapper, so targeting the wrapper's children can never
   reach it.

   TRADE-OFF: the hero heading is usually the LCP element, and starting it at
   opacity 0 defers LCP until it fades in. Accepted here for the entrance — if
   Core Web Vitals ever outweigh it, drop `opacity` from the keyframes below and
   the text will rise while staying fully visible (LCP unaffected). */
@keyframes pad-hero-rise {
  from {
    opacity: 0;
    transform: translateY(var(--wp--custom--motion--animate-distance, 2rem));
  }

  to {
    opacity: 1;
    transform: none;
  }
}

.js-animate .pad-cover-slide__content > * {
  animation: pad-hero-rise var(--wp--custom--motion--animate-duration, 1100ms)
    var(--wp--custom--motion--animate-ease, ease-out) both;
}

.js-animate .pad-cover-slide__content > :nth-child(2) {
  animation-delay: calc(1 * var(--wp--custom--motion--animate-stagger, 120ms));
}

.js-animate .pad-cover-slide__content > :nth-child(3) {
  animation-delay: calc(2 * var(--wp--custom--motion--animate-stagger, 120ms));
}

.js-animate .pad-cover-slide__content > :nth-child(n + 4) {
  animation-delay: calc(3 * var(--wp--custom--motion--animate-stagger, 120ms));
}

/* Respect a reduced-motion preference: show everything, animate nothing. */
@media (prefers-reduced-motion: reduce) {
  .js-animate main .wp-block-pad-container-block > *,
  .js-animate main .wp-block-pad-container-block .grid > *,
  .js-animate main .wp-block-pad-container-block .pad-image-slider > * {
    opacity: 1;
    transform: none;
    transition: none;
    transition-delay: 0s;
  }

  .js-animate .pad-cover-slide__content > * {
    animation: none;
  }
}
