/* ==========================================================================
   TATTOUH — motion
   Scroll reveals, ambient loops, and the hero/diagram animations.

   Every reveal is opt-in via [data-reveal] and is driven by an
   IntersectionObserver in site.js, which adds .is-in. If JS never runs, the
   .no-js fallback below shows everything immediately: content must never
   depend on script to be visible.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Scroll reveal
   -------------------------------------------------------------------------- */

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

[data-reveal='left'] {
  transform: translateX(-26px);
}
[data-reveal='right'] {
  transform: translateX(26px);
}
[data-reveal='scale'] {
  transform: scale(0.955);
}
[data-reveal='blur'] {
  filter: blur(9px);
  transform: translateY(14px);
}
[data-reveal='fade'] {
  transform: none;
}

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

/* Children stagger: the parent carries data-stagger, site.js sets the index. */
[data-stagger] > * {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-slower) var(--ease-out), transform var(--dur-slower) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 75ms);
}
[data-stagger].is-in > * {
  opacity: 1;
  transform: none;
}

/* Without JS (or with reveals disabled) nothing may stay hidden. */
.no-js [data-reveal],
.no-js [data-stagger] > * {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
}

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

/* --------------------------------------------------------------------------
   Keyframes
   -------------------------------------------------------------------------- */

@keyframes marquee {
  from {
    transform: translateX(0);
  }
  to {
    /* Track is duplicated in markup, so half a loop is one full cycle. */
    transform: translateX(-50%);
  }
}

@keyframes float-slow {
  0%,
  100% {
    transform: translateY(0) translateX(0);
  }
  50% {
    transform: translateY(-18px) translateX(8px);
  }
}

@keyframes aurora-drift {
  0%,
  100% {
    transform: translate(0, 0) scale(1);
    opacity: 0.85;
  }
  33% {
    transform: translate(6%, -5%) scale(1.12);
    opacity: 1;
  }
  66% {
    transform: translate(-5%, 4%) scale(0.95);
    opacity: 0.7;
  }
}

@keyframes pulse-ring {
  0% {
    transform: scale(0.85);
    opacity: 0.55;
  }
  70% {
    transform: scale(1.5);
    opacity: 0;
  }
  100% {
    transform: scale(1.5);
    opacity: 0;
  }
}

@keyframes dash-flow {
  to {
    stroke-dashoffset: -1000;
  }
}

@keyframes draw-in {
  to {
    stroke-dashoffset: 0;
  }
}

@keyframes blink {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.25;
  }
}

@keyframes shimmer {
  to {
    background-position: 200% 0;
  }
}

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

/* --------------------------------------------------------------------------
   Hero ambience
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  padding-top: calc(var(--nav-h) + clamp(3rem, 1.5rem + 6vw, 7rem));
  padding-bottom: var(--section-y);
  overflow: hidden;
}

.hero__aurora {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

.hero__aurora span {
  position: absolute;
  display: block;
  border-radius: 50%;
  filter: blur(80px);
  animation: aurora-drift 34s var(--ease) infinite;
}

.hero__aurora span:nth-child(1) {
  top: -22%;
  left: -8%;
  width: min(620px, 70vw);
  aspect-ratio: 1;
  background: var(--aurora-1);
}
.hero__aurora span:nth-child(2) {
  top: 8%;
  right: -12%;
  width: min(560px, 64vw);
  aspect-ratio: 1;
  background: var(--aurora-2);
  animation-duration: 42s;
  animation-direction: reverse;
}
.hero__aurora span:nth-child(3) {
  bottom: -28%;
  left: 34%;
  width: min(480px, 58vw);
  aspect-ratio: 1;
  background: var(--aurora-3);
  animation-duration: 50s;
}

.hero__inner {
  position: relative;
  z-index: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hero__aurora span {
    animation: none;
  }
}

/* --------------------------------------------------------------------------
   Animated logo mark
   -------------------------------------------------------------------------- */

.mark-anim .mark-ring {
  transform-origin: center;
  animation: float-slow 14s var(--ease) infinite;
}

/* --------------------------------------------------------------------------
   Counter (stats that count up on reveal)
   -------------------------------------------------------------------------- */

.counter {
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Lottie containers
   -------------------------------------------------------------------------- */

.lottie {
  width: 100%;
  height: auto;
  /* Reserve the box before the animation loads so nothing shifts. */
  aspect-ratio: var(--lottie-ratio, 1);
}

.lottie svg {
  display: block;
  width: 100% !important;
  height: 100% !important;
}

/* --------------------------------------------------------------------------
   Scroll progress bar
   -------------------------------------------------------------------------- */

.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: calc(var(--z-nav) + 1);
  height: 2px;
  width: 100%;
  transform-origin: 0 50%;
  transform: scaleX(var(--progress, 0));
  background: linear-gradient(90deg, var(--primary), var(--primary-deep), var(--accent));
  pointer-events: none;
  transition: transform 90ms linear;
}

/* --------------------------------------------------------------------------
   Skeleton shimmer (used while a Lottie or embed loads)
   -------------------------------------------------------------------------- */

.shimmer {
  background: linear-gradient(90deg, var(--surface) 25%, var(--surface-2) 50%, var(--surface) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.6s linear infinite;
}

/* --------------------------------------------------------------------------
   Link underline sweep
   -------------------------------------------------------------------------- */

.link-sweep {
  position: relative;
  color: var(--text);
}
.link-sweep::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: var(--primary);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--dur) var(--ease-out);
}
.link-sweep:hover {
  color: var(--primary);
}
.link-sweep:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}

/* --------------------------------------------------------------------------
   Tilt (subtle 3D on feature panels, driven by site.js)
   -------------------------------------------------------------------------- */

.tilt {
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform var(--dur) var(--ease-out);
  transform-style: preserve-3d;
}

@media (prefers-reduced-motion: reduce) {
  .tilt {
    transform: none !important;
  }
}

/* Lottie colours are baked into the JSON and cannot read CSS variables. The
   cyan is tuned for the dark theme, so the light theme darkens it slightly to
   keep contrast against the pale background. */
:root[data-theme='light'] .lottie {
  filter: saturate(1.15) brightness(0.82);
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme]) .lottie {
    filter: saturate(1.15) brightness(0.82);
  }
}
