/* ──────────────────────────────────────────────────────────
   Shared motion + utility layer for all three directions.
   Reveals use a keyframe animation (not transitions) with
   animation-fill-mode:both so the visible end-state is always
   applied — robust across webviews, print, and reduced-motion.
   Hooks:
     [data-reveal]                fade/slide in on scroll
     [data-reveal="up|left|right|scale|blur"]   variant
     style="--rv-delay:120ms"     stagger delay
     [data-count]                 animated number counter
     [data-parallax]              translateY on scroll (data-speed)
   ────────────────────────────────────────────────────────── */

[data-reveal]{
  opacity:0;
  --rv-tx:0px; --rv-ty:34px; --rv-sc:1; --rv-bl:0px;
}
[data-reveal="left"]{ --rv-tx:-42px; --rv-ty:0px }
[data-reveal="right"]{ --rv-tx:42px; --rv-ty:0px }
[data-reveal="scale"]{ --rv-ty:0px; --rv-sc:.94 }
[data-reveal="blur"]{ --rv-ty:20px; --rv-bl:14px }

[data-reveal].rv-in{
  animation:rvIn .92s cubic-bezier(.16,.84,.44,1) var(--rv-delay,0ms) both;
}
@keyframes rvIn{
  from{ opacity:0; transform:translate(var(--rv-tx),var(--rv-ty)) scale(var(--rv-sc)); filter:blur(var(--rv-bl)) }
  to{ opacity:1; transform:none; filter:none }
}

@media (prefers-reduced-motion: reduce){
  [data-reveal]{opacity:1!important;animation:none!important}
}

/* Safety net: if the environment can't advance CSS timelines, JS adds this
   class so content is shown instantly instead of stuck at the from-state. */
html.reveal-snap [data-reveal]{opacity:1!important;animation:none!important;transform:none!important;filter:none!important}

[data-count]{font-variant-numeric:tabular-nums}
