/* ============================================================
   FX — reveal choreography and the page-load sequence.

   One orchestrated entrance beats scattered effects, so the
   load sequence is a single timed cascade and scroll reveals
   are deliberately restrained: a short rise with a little
   depth, nothing spinning or bouncing.
   ============================================================ */

/* ---- Scroll reveal ----------------------------------------
   JS adds .is-in when the element crosses the threshold.
   --d staggers siblings. */

[data-reveal] {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity var(--dur-reveal) var(--ease-out) var(--d, 0ms),
    transform var(--dur-reveal) var(--ease-out) var(--d, 0ms);
}

[data-reveal].is-in {
  opacity: 1;
  transform: none;
}

/* Cards come in with a touch of rotation on the X axis so they
   feel like they are settling onto the sheet rather than sliding. */
[data-reveal="card"] {
  transform: perspective(900px) translateY(26px) rotateX(6deg);
  transform-origin: 50% 100%;
}

[data-reveal="card"].is-in {
  transform: perspective(900px) translateY(0) rotateX(0deg);
}

/* Timeline entries enter from the dimension line outward. */
[data-reveal="entry"] {
  transform: translateX(-14px);
}

[data-reveal="entry"].is-in {
  transform: none;
}

/* The dimension line draws itself downward once in view — the
   one place a drawing-in effect is literal enough to earn it. */
.timeline__run {
  transform-origin: 50% 0;
  transform: scaleY(0);
  transition: transform 1400ms var(--ease-out);
}

.timeline__run.is-in { transform: scaleY(1); }

/* ---- Page-load sequence -----------------------------------
   Fires once on first paint. Everything is keyed off a single
   cascade so the hero assembles in a readable order:
   name, then role, then tagline, then actions, then the stats,
   with the assembly exploding open last. */

.hero__lead > * {
  opacity: 0;
  animation: rise 900ms var(--ease-out) forwards;
}

.hero__lead > *:nth-child(1) { animation-delay: 120ms; }
.hero__lead > *:nth-child(2) { animation-delay: 200ms; }
.hero__lead > *:nth-child(3) { animation-delay: 280ms; }
.hero__lead > *:nth-child(4) { animation-delay: 360ms; }
.hero__lead > *:nth-child(5) { animation-delay: 440ms; }
/* Six is the current child count of the hero lead. `n+6` rather
   than `6` so that if content.js gains or loses a block, extra
   children inherit the last delay instead of falling back to 0
   and firing before everything above them. */
.hero__lead > *:nth-child(n+6) { animation-delay: 520ms; }

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

.hero__figure {
  opacity: 0;
  animation: fade 1100ms var(--ease-out) 420ms forwards;
}

@keyframes fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* The rail draws in from the left edge. */
.rail {
  animation: rail-in 900ms var(--ease-out) both;
}

@keyframes rail-in {
  from { opacity: 0; transform: translateX(-100%); }
  to   { opacity: 1; transform: none; }
}

/* ---- Ambient title-block scan -----------------------------
   A slow highlight travelling down the rail. Long period, very
   low contrast — presence rather than animation.

   Held paused until app.js adds .is-ready to <html>, so it does
   not start sweeping an empty rail before the nav is drawn. If
   JS never runs, the shimmer simply never starts, which costs
   nothing — the rail itself is styled and visible either way. */

.rail::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 120px;
  pointer-events: none;
  background: linear-gradient(180deg,
    transparent,
    var(--cyanotype-wash-soft),
    transparent);
  animation: scan 9s linear infinite;
  animation-play-state: paused;
}

.is-ready .rail::after { animation-play-state: running; }

@keyframes scan {
  from { transform: translateY(-140px); }
  to   { transform: translateY(100vh); }
}

/* ---- Reduced motion ---------------------------------------
   Everything above is motion, not content, so it all comes off
   cleanly. Layout and information are unchanged.
   ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  [data-reveal],
  [data-reveal="card"],
  [data-reveal="entry"] {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .timeline__run {
    transform: none;
    transition: none;
  }

  .hero__lead > *,
  .hero__figure,
  .rail {
    opacity: 1;
    animation: none;
    transform: none;
  }

  .rail::after { display: none; }

  .cert {
    transform: none;
    transition: none;
  }

  .axono__layer { transition: none; }
}
