/* ==========================================================================
   Hero video layer — index3.html only.
   Loads after styles.css and reveal.css; adds nothing to index.html/index2.html,
   which have no .hero__video element for any of this to match.

   The video sits *on top of* the mesh rather than replacing it. The mesh is
   painted from the first frame, so the hero is never empty; the video crossfades
   over it only once it is actually playing. Every path where the video does not
   arrive — mobile, save-data, reduced motion, autoplay refused, decode error —
   simply leaves the mesh in place, which is a finished-looking hero rather than
   a fallback.
   ========================================================================== */

.hero__video {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
  transition: opacity 900ms var(--ease);
}

/* Both layers carry the same overscan so the parallax in reveal.js can drift
   them together without either exposing an edge. */
.js-reveal .hero__video {
  top: -45px;
  height: calc(100% + 90px);
  will-change: transform;
}

/* The crossfade. .62 is the mesh's own opacity in styles.css — the video
   inherits the exact weight the design gave the hero art, so the scrim and the
   headline contrast are unchanged. */
.hero__art { transition: opacity 900ms var(--ease); }
.hero.has-video .hero__video { opacity: .62; }
.hero.has-video .hero__art { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .hero__video { display: none; }
  .hero.has-video .hero__art { opacity: .62; }
}
