/**
 * @file
 * MVSD motion system — scroll-reveal + entrance stagger (Pazazz Phase 1).
 *
 * Reusable: add .reveal (+ optional .reveal--left/--right/--scale) to any
 * element; motion.js adds .is-visible via IntersectionObserver as it scrolls
 * into view. On the district front, known blocks are revealed without markup.
 *
 * Robustness (deliberate):
 *  - Hidden state applies ONLY under html.mvsd-js (set by an inline head script
 *    BEFORE paint). No JS => the class is never added => nothing hides =>
 *    content stays visible. Belt-and-suspenders: an @media (scripting: none)
 *    block below ALSO force-reveals every target, so a JS-disabled browser or
 *    a no-JS bot can never paint blank even if the class somehow lands.
 *  - prefers-reduced-motion => everything visible, zero motion.
 *  - Animates transform + opacity only (GPU-cheap; protects the perf budget).
 */

:root {
  /* Prompt but PERCEPTIBLE reveal (2026-08-26, 2nd pass).
     First pass (220ms/14px) fixed the "feels blank" complaint but overshot:
     the ease below is heavily front-loaded (90% of the change lands in the
     first ~37% of the duration), so a 220ms fade completed 90% of its travel
     in ~97ms — under the threshold where motion registers at all, and it read
     as no animation. Duration and distance are what make it perceptible, so
     they carry most of this correction (~2x back up, still well short of the
     sluggish 600/700ms original).
     --motion-stagger-max stays: it clamps the cumulative per-item delay so a
     long feed can never accumulate a large lag on its last card (the original
     was uncapped — 110ms x 8 cards = 880ms on the last one). */
  --motion-duration: 460ms;
  --motion-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --motion-distance: 22px;
  --motion-stagger-step: 65ms;
  --motion-stagger-max: 180ms;
}

/* Showcase pages push bolder, still prompt. mvsd-front-motion = every front
   page + dept landings; broaden the inline-script condition to roll out
   further. The larger stagger step here is deliberate: on SCROLL the first
   card of a row is essentially settled by the time it reaches reading
   position (see the rootMargin note in motion.js), so it is the STAGGERED
   members of a feed that carry the visible in-view motion. */
html.mvsd-front-motion {
  --motion-duration: 520ms;
  --motion-distance: 32px;
  --motion-stagger-step: 75ms;
  --motion-stagger-max: 200ms;
}

/* ---- Reusable .reveal primitive (gated on JS being active) ---- */
html.mvsd-js .reveal {
  opacity: 0;
  transform: translateY(var(--motion-distance));
  transition:
    opacity var(--motion-duration) var(--motion-ease),
    transform var(--motion-duration) var(--motion-ease);
  transition-delay: min(calc(var(--reveal-i, 0) * var(--motion-stagger-step)), var(--motion-stagger-max, 200ms));
}
html.mvsd-js .reveal.reveal--left  { transform: translateX(calc(-1 * var(--motion-distance))); }
html.mvsd-js .reveal.reveal--right { transform: translateX(var(--motion-distance)); }
html.mvsd-js .reveal.reveal--scale { transform: scale(0.94); }
html.mvsd-js .reveal.is-visible { opacity: 1; transform: none; }

/* ---- District front-page application (Phase 1) ----
   Targets existing front markup, so no template edits. Pre-hidden from first
   paint because html.mvsd-front-motion is set by the inline head script. */
html.mvsd-front-motion .news-card--primary,
html.mvsd-front-motion .news-card--teaser,
html.mvsd-front-motion .mission-panel,
html.mvsd-front-motion .split-panel,
html.mvsd-front-motion .dept-content > *,
html.mvsd-front-motion [id$="school-highlights"] {
  opacity: 0;
  transform: translateY(var(--motion-distance));
  transition:
    opacity var(--motion-duration) var(--motion-ease),
    transform var(--motion-duration) var(--motion-ease);
  transition-delay: min(calc(var(--reveal-i, 0) * var(--motion-stagger-step)), var(--motion-stagger-max, 200ms));
  will-change: opacity, transform;
}
html.mvsd-front-motion .news-card--primary.is-visible,
html.mvsd-front-motion .news-card--teaser.is-visible,
html.mvsd-front-motion .mission-panel.is-visible,
html.mvsd-front-motion .split-panel.is-visible,
html.mvsd-front-motion .dept-content > .is-visible,
html.mvsd-front-motion [id$="school-highlights"].is-visible {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* School Spotlight (district front) fades in as ONE unit: the whole section
   reveals together; cards inside ride with it and do not animate individually
   (stakeholder, 2026-05-21). */
html.mvsd-front-motion [id$="school-highlights"] .news-card--primary,
html.mvsd-front-motion [id$="school-highlights"] .news-card--teaser {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

/* ---- Accessibility: reduced motion = always visible, no motion ---- */
@media (prefers-reduced-motion: reduce) {
  html.mvsd-js .reveal,
  html.mvsd-js .reveal.is-visible,
  html.mvsd-front-motion .news-card--primary,
  html.mvsd-front-motion .news-card--teaser,
  html.mvsd-front-motion .mission-panel,
  html.mvsd-front-motion .split-panel,
  html.mvsd-front-motion .dept-content > *,
  html.mvsd-front-motion [id$="school-highlights"] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }
}

/* ---- Failsafe: JS disabled = everything visible, no motion ----
   Mirror of the reduced-motion un-hide, keyed on the browser reporting no
   scripting. The html.mvsd-js gate already keeps content visible when JS is
   off (the inline head script never runs), but this makes the guarantee
   explicit and independent of that class ever being present — a no-JS bot or
   a hard-disabled browser must never see a blank first screen. Added
   2026-08-26 alongside the faster-reveal tune. */
@media (scripting: none) {
  html .reveal,
  html .reveal.reveal--left,
  html .reveal.reveal--right,
  html .reveal.reveal--scale,
  html .news-card--primary,
  html .news-card--teaser,
  html .mission-panel,
  html .split-panel,
  html .dept-content > *,
  html [id$="school-highlights"] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }
}

/* Dept navigation is chrome, not content — exclude it from the content
   stagger so it never renders as a washed-out / fading menu (was most
   noticeable on sub-dept pages catching it mid-reveal). Always solid. */
html.mvsd-front-motion .dept-content > .dept-nav {
  opacity: 1 !important;
  transform: none !important;
  transition: none !important;
}

/* ---- Print: reveal everything ------------------------------------------
   Scroll-reveal (IntersectionObserver) never fires for below-the-fold content
   in a print render, so any element still in its hidden start-state prints
   INVISIBLE. This silently dropped department-landing cards (every `page`
   node with field_department gets html.mvsd-front-motion -> .dept-content
   children start at opacity:0) and any reusable .reveal block. Mirror the
   reduced-motion un-hide for print so the printed page matches the screen.
   Added 2026-06-22 (printing rethink: print "as viewed"). */
@media print {
  html.mvsd-js .reveal,
  html.mvsd-js .reveal.is-visible,
  html.mvsd-js .reveal.reveal--left,
  html.mvsd-js .reveal.reveal--right,
  html.mvsd-js .reveal.reveal--scale,
  html.mvsd-front-motion .news-card--primary,
  html.mvsd-front-motion .news-card--teaser,
  html.mvsd-front-motion .mission-panel,
  html.mvsd-front-motion .split-panel,
  html.mvsd-front-motion .dept-content > *,
  html.mvsd-front-motion [id$="school-highlights"] {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }
}
