/* Scroll-driven section reveals
   ----------------------------------------------------------------------
   Content fades and rises as it scrolls into view. The playhead is the
   scroll position itself (animation-timeline: view()) rather than a timer,
   so a reveal tracks the scroll exactly — scrub back up and it plays
   backwards. No IntersectionObserver, no scroll listener, no JS.

   Everything is wrapped in @supports + prefers-reduced-motion:
   no-preference, so the *unstyled* state is the finished state. Browsers
   without scroll-timeline support, and anyone who asks for reduced motion,
   get the page fully visible with nothing hidden behind an animation that
   will never run. */

@supports (animation-timeline: view()){
  @media (prefers-reduced-motion: no-preference){

    .reveal,
    .reveal-stagger > *{
      animation: reveal-rise linear both;
      animation-timeline: view();
      /* Ranges stay inside the first fifth of the element's cover phase, so
         a reveal finishes shortly after the element appears. Keeping the end
         early matters for the last elements on the page: once the document
         can't scroll any further, an unfinished range would leave them
         stranded part-way faded. */
      animation-range: cover 0% cover 20%;
    }

    /* Lists cascade: each child starts a little further into its own cover
       phase. The children share a scroll position, so offsetting the range
       is what staggers them — a time delay would be ignored here. */
    .reveal-stagger > :nth-child(2){ animation-range: cover 2% cover 22% }
    .reveal-stagger > :nth-child(3){ animation-range: cover 4% cover 24% }
    .reveal-stagger > :nth-child(4){ animation-range: cover 6% cover 26% }
    .reveal-stagger > :nth-child(5){ animation-range: cover 8% cover 28% }
    .reveal-stagger > :nth-child(n+6){ animation-range: cover 10% cover 30% }

    /* Elements near the end of the document need a different range. A
       cover-phase range needs scroll runway *past* the element, and the last
       block before the footer doesn't have much — the page stops scrolling
       with the range unfinished, stranding it part-faded (measured: the final
       contact link sat at opacity .52 on a 390x844 viewport). An entry-phase
       range is bounded by the element's own height instead of by runway, so
       it is always reachable no matter how little page is left below. */
    .reveal-tail{ animation-range: entry 0% entry 100%; }
  }
}

@keyframes reveal-rise{
  from{ opacity:0; transform:translateY(14px) }
  to  { opacity:1; transform:none }
}
