/* Pure CSS scroll-driven reveal via animation-timeline: view() - no JS,
   no IntersectionObserver. Known, accepted tradeoff: a view-timeline's
   progress is tied 1:1 to scroll offset, not time, so it only eases
   smoothly under continuous scroll input (trackpad momentum). A mouse
   wheel or a non-momentum trackpad delivers scroll in discrete jumps,
   and the animation snaps to whichever progress matches each new
   offset - there's no time dimension for a timing function to ease
   along. (This project tried this approach once before and reverted
   to a triggered-transition + IntersectionObserver instead specifically
   to avoid that choppiness; this reintroduces it deliberately.)

   Default state (no @supports match, or reduced motion) is fully
   visible - content is never hidden waiting on a feature that isn't
   there.

   The slide distance and scale each animate a typed custom property
   (--reveal-y, --reveal-scale) instead of `transform` directly. An
   actively-animating property always wins the cascade over a normal
   rule for that same property, regardless of specificity - so animating
   `transform` directly here would silently defeat
   .timeline-item:hover/.project-card:hover's own `transform` (verified:
   the hover background/border still applied, but the hover transform
   never did). Routing it through custom properties means only those
   properties are animation-owned; `transform` itself stays a normal,
   overridable declaration built from both. */
@property --reveal-y {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
@property --reveal-scale {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

.reveal {
  transform: translateY(var(--reveal-y)) scale(var(--reveal-scale));

  @supports (animation-timeline: view()) {
    animation: reveal-in linear both;
    animation-timeline: view();
    /* `entry` is bound by the SUBJECT's own height, not the viewport's -
       for a ~12px-tall .eyebrow label that's an ~12px-long entry phase,
       so `entry 0% entry 75%` completed within about 9px of scroll:
       imperceptibly fast on any input device, not just choppy under a
       mouse wheel. `cover`'s phase spans the element's entire transit
       through the viewport (viewport height + element height), which is
       viewport-dominated for most elements here - so scaling the same
       animation across the first 18% of *that* gives a consistent,
       clearly visible scroll distance regardless of how tall the
       revealing element itself is (measured: ~180px for the 12px-tall
       eyebrow), a ~20x improvement.

       18%, not more: Contact's social links are the last .reveal
       elements on the page, and there's only so much scroll room left
       below them once you've hit the bottom of the document. A larger
       percentage (tried 30%) required more scroll distance than that
       remaining room, so those last few items got permanently stuck
       partway through - verified 18% still reaches opacity 1 for every
       #contact .reveal element at max scroll, at both 900px and 700px
       viewport heights. */
    animation-range: cover 0% cover 18%;
  }
  @media (prefers-reduced-motion: reduce) {
    animation: none !important;
  }
}

@keyframes reveal-in {
  from {
    opacity: 0;
    --reveal-y: 200px;
    --reveal-scale: 0.85;
  }
  to {
    opacity: 1;
    --reveal-y: 0px;
    --reveal-scale: 1;
  }
}
