/* ============================================================
   Tiempo — motion
   One reveal-on-scroll system for the whole site.

   There were three, and they disagreed on everything:
     A  .reveal -> .visible     animation, 1.4s, 32px  (index, about, 7 spokes)
     A' .reveal -> .visible     paused animation, 0.7s, 28px  (work-with-us)
     B  .reveal -> .is-visible  transition, 0.8s, 20px  (product, blog)
   plus four pages with no reveal at all.

   It standardises on `.is-visible`, because:
     - `.visible` is already taken by .process-step-text, and the
       process JS removes that class by name
     - ~30 rules in the component sheet already key off `.is-visible`
     - it matches the existing .is-playing / is- convention

   Delay is an explicit custom property rather than :nth-child.
   The old ladder made an element's timing an accident of its
   position among siblings: the homepage CTA picked up a 0.30s
   delay purely because it happened to be the third child.

   One animation used to serve everything: a 32px rise over 1.4s,
   headline and video panel alike. Five kinds now, one modifier
   each, every timing in tokens.css so a kind can be retuned
   without reading this file:

     .reveal              text. Rises out of a blur
     .reveal--media       photos, video, the two embeds. Scales up
     .reveal--box         cards, panels, bands
     .reveal--diagram     process ring, calculator, simulators
     .reveal--control     CTAs and filter bars. Short and crisp

   Only text blurs. On a line of type an out-of-focus arrival reads
   as depth; on a card or a video panel it reads as a dropped frame,
   and it costs a composited layer per element on pages that carry
   37 of them.
   ============================================================ */

/* Two keyframes, not five. Everything except text shares this one and
   differs only in the custom properties feeding it. Scale defaults to 1
   so a kind that only rises composes the same declaration. */
@keyframes revealIn {
  from { opacity: 0; transform: translateY(var(--reveal-shift, 0px)) scale(var(--reveal-scale, 1)); }
  to   { opacity: 1; transform: none; }
}

/* Text. Every rule below fills `backwards`, not `both`: the animation
   covers the delay and then hands the element back to its own style, so
   nothing keeps a filter, a transform or a composited layer once it has
   arrived. That is why each rule restates `opacity: 1`. */
@keyframes revealInBlur {
  from { opacity: 0; transform: translateY(var(--reveal-shift, 0px)); filter: blur(var(--reveal-blur, 0px)); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* Reset here, not per kind. --reveal-scale inherits, and the kinds that do
   not scale — text and control — never set it, so a control sitting inside
   a card would have picked up the card's 0.985 and shrunk with it. Each
   scaling kind overrides this at higher specificity. */
.reveal { opacity: 0; --reveal-scale: 1; }

/* Text is the default: an element with no modifier gets it. */
.reveal.is-visible,
.reveal.visible {
  --reveal-shift: var(--reveal-text-shift);
  --reveal-blur:  var(--reveal-text-blur);
  animation: revealInBlur var(--reveal-dur, var(--reveal-text-dur)) var(--reveal-text-ease) backwards;
  animation-delay: var(--reveal-delay, 0s);
  opacity: 1;
}

.reveal--media.is-visible,
.reveal--media.visible {
  --reveal-shift: var(--reveal-media-shift);
  --reveal-scale: var(--reveal-media-scale);
  animation: revealIn var(--reveal-dur, var(--reveal-media-dur)) var(--reveal-media-ease) backwards;
  animation-delay: var(--reveal-delay, 0s);
  opacity: 1;
}

.reveal--box.is-visible,
.reveal--box.visible {
  --reveal-shift: var(--reveal-box-shift);
  --reveal-scale: var(--reveal-box-scale);
  animation: revealIn var(--reveal-dur, var(--reveal-box-dur)) var(--reveal-box-ease) backwards;
  animation-delay: var(--reveal-delay, 0s);
  opacity: 1;
}

.reveal--diagram.is-visible,
.reveal--diagram.visible {
  --reveal-shift: var(--reveal-diagram-shift);
  --reveal-scale: var(--reveal-diagram-scale);
  animation: revealIn var(--reveal-dur, var(--reveal-diagram-dur)) var(--reveal-diagram-ease) backwards;
  animation-delay: var(--reveal-delay, 0s);
  opacity: 1;
}

.reveal--control.is-visible,
.reveal--control.visible {
  --reveal-shift: var(--reveal-control-shift);
  animation: revealIn var(--reveal-dur, var(--reveal-control-dur)) var(--reveal-control-ease) backwards;
  animation-delay: var(--reveal-delay, 0s);
  opacity: 1;
}

/* ── Two-phase arrival ────────────────────────────────────────
   `data-fill` on a .reveal block means the block arrives bare and its
   contents arrive into it afterwards, rather than the two moving as one
   flat picture. reveal.js flips the block from .is-holding to .is-filled
   once its own reveal has visually landed (--reveal-handoff of the way
   through, not at animationend).

   Contents marked .fill get the default look for free: held while the
   block travels, then a short rise, --fill-step apart in DOM order.
   Nothing else to write.

     <div class="panel reveal reveal--box" data-fill>
       <h3 class="fill">…</h3>
       <ul class="fill">…</ul>
     </div>

   A section whose contents need something else — a background that has to
   wait, a column cascade that also replays on a tab switch — skips .fill
   and keys its own rules off .is-holding and .is-filled. The homepage
   charts and the product use-case board both do.

   Held off .is-holding, which only JS adds, so a no-JS render shows the
   contents normally instead of an empty panel.
   ─────────────────────────────────────────────────────────── */
[data-fill].is-holding .fill { opacity: 0; }

[data-fill].is-filled .fill {
  --reveal-shift: var(--fill-shift);
  --reveal-scale: 1;          /* the block scaled; its contents only rise */
  animation: revealIn var(--fill-dur) var(--fill-ease) backwards;
  animation-delay: var(--reveal-delay, 0s);
  opacity: 1;
}

/* Pair with any kind on an element that paints edge to edge. A band at
   0.98 stops ~18px short of each viewport edge and flashes the page ground
   down both sides for the length of the animation; this keeps the rise and
   drops the scale. It sits after the five kinds deliberately: same
   specificity, later wins. */
.reveal--flush.is-visible,
.reveal--flush.visible { --reveal-scale: 1; }

/* Opt a group of siblings into a stagger by wrapping them.
   reveal.js reads --reveal-step off the container and writes
   --reveal-delay onto each child. */
.reveal-group            { --reveal-step: 0.15s; }
.reveal-group[data-stagger="fast"] { --reveal-step: 0.08s; }
.reveal-group[data-stagger="slow"] { --reveal-step: 0.20s; }

/* No page disabled reveal motion before this. 18 prefers-reduced-motion
   blocks existed across the site and not one of them touched .reveal, so
   every page animated regardless of the visitor's setting. */
@media (prefers-reduced-motion: reduce) {
  [data-fill] .fill,
  .reveal,
  .reveal.is-visible,
  .reveal.visible {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
}

/* ── Smooth scroll ────────────────────────────────────────────
   The five rules Lenis needs, written out here rather than shipped
   as a second stylesheet. Copied from the package with the two
   !important flags dropped: `.lenis.lenis-smooth` already outweighs
   the element selectors it is overriding, so the flags were never
   doing any work.

   All five are inert until smooth-scroll.js puts .lenis on <html>,
   which it does not do when the visitor asks for reduced motion.
   ─────────────────────────────────────────────────────────── */
html.lenis                                  { height: auto; }
.lenis.lenis-smooth                         { scroll-behavior: auto; }
.lenis.lenis-smooth [data-lenis-prevent]    { overscroll-behavior: contain; }
.lenis.lenis-stopped                        { overflow: hidden; }
/* An iframe swallows the wheel mid-glide and the page stops dead under
   the cursor. Only set while momentum is running, so clicks still land. */
.lenis.lenis-scrolling iframe               { pointer-events: none; }
