/* ==========================================================================
   Motion
   Every hidden-until-revealed state is scoped to `html.js`, so with JavaScript
   disabled nothing is ever stuck invisible.

   Note on IntersectionObserver: reveal states use opacity + transform only.
   A clip-path on an observed element collapses its intersection rectangle to
   zero, and the observer then never fires — so masks live on inner wrappers.
   ========================================================================== */

/* --- Cross-document page transitions (progressive; ignored where absent) -- */
@view-transition { navigation: auto; }

::view-transition-old(root) { animation: vt-out 260ms var(--ease) both; }
::view-transition-new(root) { animation: vt-in 420ms var(--ease-expo) both; }
@keyframes vt-out { to { opacity: 0; } }
@keyframes vt-in { from { opacity: 0; transform: translateY(10px); } }

/* --- Scroll reveals ------------------------------------------------------ */
.js [data-reveal] {
  opacity: 0;
  transform: translateY(var(--reveal-y, 22px));
  transition:
    opacity var(--t-slow) var(--ease) var(--reveal-delay, 0ms),
    transform var(--t-slow) var(--ease-expo) var(--reveal-delay, 0ms);
}
.js [data-reveal].is-in { opacity: 1; transform: none; }

.js [data-reveal='fade'] { transform: none; }
.js [data-reveal='left'] { transform: translateX(-24px); }
.js [data-reveal='right'] { transform: translateX(24px); }

/* Image mask reveal: the mask sits on the wrapper, the scale on the image. */
.js [data-reveal-img] > img,
.js [data-reveal-img] > picture > img {
  transform: scale(1.09);
  transition: transform 1200ms var(--ease-expo) var(--reveal-delay, 0ms);
}
.js [data-reveal-img]::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--paper);
  transform-origin: 50% 100%;
  transform: scaleY(1);
  transition: transform 900ms var(--ease-expo) var(--reveal-delay, 0ms);
  pointer-events: none;
  z-index: 1;
}
.js [data-reveal-img].is-in > img,
.js [data-reveal-img].is-in > picture > img { transform: scale(1); }
.js [data-reveal-img].is-in::after { transform: scaleY(0); }
/* Dark chapters wipe with their own ground colour, not the page ground. */
.js .chapter--dark [data-reveal-img]::after,
.js .hero [data-reveal-img]::after { background: var(--d-ground); }

/* --- Line-by-line text reveal ------------------------------------------- */
.lines .line { display: block; overflow: hidden; }
.lines .line > span { display: block; }
.js .lines .line > span {
  transform: translateY(105%);
  transition: transform 900ms var(--ease-expo) var(--line-delay, 0ms);
}
.js .lines.is-in .line > span { transform: none; }

/* --- Hero entrance ------------------------------------------------------- */
/* Scoped to [data-hero]: a `.hero` section without the hook is never given
   `.is-in`, and an unscoped rule here would leave its footer invisible and its
   photograph stuck at 106%. */
.js [data-hero] .hero__media img { transform: scale(1.06); transition: transform 1800ms var(--ease-expo); }
.js [data-hero].is-in .hero__media img { transform: scale(1); }

.js [data-hero] .hero__foot,
.js [data-hero] .hero__cta,
.js [data-hero] .hero__cue {
  opacity: 0;
  transform: translateY(18px);
  transition: opacity var(--t-slow) var(--ease) var(--d, 0ms), transform var(--t-slow) var(--ease-expo) var(--d, 0ms);
}
.js [data-hero].is-in .hero__foot,
.js [data-hero].is-in .hero__cta,
.js [data-hero].is-in .hero__cue { opacity: 1; transform: none; }

/* --- Stagger helper ------------------------------------------------------ */
.js [data-stagger] > * { --reveal-delay: calc(var(--i, 0) * 70ms); }

/* --- Parallax (translate only, driven by js/animations.js) --------------- */
.js [data-parallax] { will-change: transform; }

/* --- Counters ------------------------------------------------------------ */
.stat__value { font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Reduced motion — the whole system collapses to "just show it".
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  .js [data-reveal],
  .js .lines .line > span,
  .js .hero__foot,
  .js .hero__cta,
  .js .hero__cue { opacity: 1 !important; transform: none !important; }

  .js [data-reveal-img] > img,
  .js [data-reveal-img] > picture > img,
  .js [data-hero] .hero__media img { transform: none !important; }

  .js [data-reveal-img]::after { display: none; }

  .js [data-parallax] { transform: none !important; }

  /* The scroll cue's animated line is removed outright. */
  .scroll-cue__line::after { animation: none; transform: scaleX(1); }
  .locmap__pin { animation: none; }

  @view-transition { navigation: none; }
}
