/* ==========================================================================
   Infilabs — Animations
   --------------------------------------------------------------------------
   All keyframes plus the scroll-reveal system. Every animation here is
   composited (transform / opacity / filter only) so nothing triggers layout.

   The reveal system is opt-in via `data-reveal`, driven by an
   IntersectionObserver in assets/js/animations.js which adds `.is-in`.
   The `no-js` guard means content is visible even if scripts fail.
   ========================================================================== */

/* ----------------------------------------------------------- scroll reveal */

[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition:
    opacity var(--dur-reveal) var(--ease-out) var(--delay, 0ms),
    transform var(--dur-reveal) var(--ease-out) var(--delay, 0ms),
    filter var(--dur-reveal) var(--ease-out) var(--delay, 0ms);
  filter: blur(6px);
  will-change: opacity, transform;
}

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

/* Once revealed, drop the compositor hint so long pages do not retain
   hundreds of promoted layers. */
[data-reveal].is-done {
  will-change: auto;
}

/* Fail-safe: if JavaScript never runs, nothing stays invisible. */
html.no-js [data-reveal],
[data-reduced-motion='true'] [data-reveal] {
  opacity: 1;
  transform: none;
  filter: none;
  transition: none;
}

/* Above-the-fold hero content: no blur, shorter travel, minimal delay.
   The hero headline is the LCP element on most pages, so it must paint crisply
   and early — a 6px blur on it would push Largest Contentful Paint out by the
   full reveal duration for no real design benefit. */
.hero [data-reveal] {
  transform: translateY(14px);
  filter: none;
  transition-duration: 520ms;
}

/* Horizontal reveal variants used by rails and side panels. */
[data-reveal='left'] {
  transform: translateX(-28px);
}

[data-reveal='right'] {
  transform: translateX(28px);
}

[data-reveal='scale'] {
  transform: scale(0.96);
}

/* ------------------------------------------------------------- keyframes */

/* Hero eyebrow status dot */
@keyframes pulse-ring {
  0% {
    transform: scale(0.7);
    opacity: 0.9;
  }
  70% {
    transform: scale(2.1);
    opacity: 0;
  }
  100% {
    transform: scale(2.1);
    opacity: 0;
  }
}

/* Typewriter caret blink */
@keyframes caret {
  0%,
  49% {
    border-color: currentColor;
  }
  50%,
  100% {
    border-color: transparent;
  }
}

/* Scroll-hint dot travelling down the mouse outline */
@keyframes scroll-dot {
  0% {
    transform: translate(-50%, 0);
    opacity: 0;
  }
  25% {
    opacity: 1;
  }
  75% {
    transform: translate(-50%, 14px);
    opacity: 0;
  }
  100% {
    transform: translate(-50%, 14px);
    opacity: 0;
  }
}

/* Background mesh blobs — long, offset cycles so the motion never looks
   synchronised or loops visibly. */
@keyframes blob-drift-a {
  0% {
    transform: translate3d(0, 0, 0) scale(1);
  }
  50% {
    transform: translate3d(6vw, 4vh, 0) scale(1.12);
  }
  100% {
    transform: translate3d(-3vw, 7vh, 0) scale(0.96);
  }
}

@keyframes blob-drift-b {
  0% {
    transform: translate3d(0, 0, 0) scale(1.05);
  }
  50% {
    transform: translate3d(-7vw, 6vh, 0) scale(0.94);
  }
  100% {
    transform: translate3d(4vw, -3vh, 0) scale(1.1);
  }
}

@keyframes blob-drift-c {
  0% {
    transform: translate3d(0, 0, 0) scale(0.95);
  }
  50% {
    transform: translate3d(5vw, -5vh, 0) scale(1.15);
  }
  100% {
    transform: translate3d(-6vw, 2vh, 0) scale(1);
  }
}

/* Orbit rings and node counter-rotation */
@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Floating shapes — hero core, decorative cards */
@keyframes float-y {
  from {
    transform: translateY(-7px);
  }
  to {
    transform: translateY(7px);
  }
}

/* Partner marquee. Track contains the list twice, so -50% is a seamless loop. */
@keyframes marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* Utility: subtle vertical bob applied to decorative elements */
.float {
  animation: float-y 5.5s var(--ease) infinite alternate;
}

.float--slow {
  animation-duration: 8s;
}

/* -------------------------------------------------- parallax & tilt hooks */

/* Parallax layers get their offset from a CSS variable set by animations.js,
   keeping the JS to a single style write per frame. */
[data-parallax] {
  transform: translate3d(0, var(--py, 0px), 0);
  will-change: transform;
}

/* Card tilt. Rotation values come from pointer position in animations.js;
   the CSS owns the transition so the reset is smooth. */
[data-tilt] {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateY(var(--ty, 0px));
  transform-style: preserve-3d;
}

/* The card's own :hover lift is expressed through --ty so the two systems
   compose instead of overwriting each other's transform. */
.card[data-tilt]:hover,
.post[data-tilt]:hover,
.rail__card[data-tilt]:hover {
  --ty: -5px;
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) translateY(var(--ty));
}

/* --------------------------------------------------------- reduced motion */

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

  [data-tilt],
  [data-parallax] {
    transform: none !important;
  }

  .mesh__blob,
  .orbit__ring,
  .orbit__nodes,
  .orbit__spin,
  .orbit__core,
  .marquee__track,
  .pulse::after,
  .hero__scroll span::after,
  .float {
    animation: none !important;
  }

  .typewriter {
    border-right-color: transparent;
  }
}
