/* ============================================================
   Keyframes & scroll-reveal placeholder
   Per-component transitions live alongside their components in
   components.css; only globally-named animations are defined here.
   ============================================================ */

/* status LED in the brand mark */
@keyframes led-pulse { 50% { opacity: 0.5; } }

/* horizontal kinetic strip / marquee */
@keyframes marq {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ============ scroll reveal — intro animation per component ============
   Every container wrapped with className="reveal" starts hidden +
   slightly displaced. The useReveal() hook in primitives.jsx adds
   `.in` once the element enters the viewport (or after a 2s safety
   timeout), triggering this transition.

   Variants (apply alongside `.reveal`):
     .reveal-fade   — fade only, no displacement
     .reveal-up     — fade + slide up   (default behavior)
     .reveal-scale  — fade + slight scale
     .reveal-blur   — fade + clear blur
     .reveal-stagger — children stagger via :nth-child delays */

.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity .9s cubic-bezier(.22, .8, .2, 1),
    transform .9s cubic-bezier(.22, .8, .2, 1),
    filter .9s cubic-bezier(.22, .8, .2, 1);
  will-change: opacity, transform;
}
.reveal.in {
  opacity: 1;
  transform: translateY(0);
  filter: blur(0);
}

/* — variants — */
.reveal.reveal-fade        { transform: none; }
.reveal.reveal-fade.in     { transform: none; }

.reveal.reveal-scale       { transform: translateY(16px) scale(0.96); }
.reveal.reveal-scale.in    { transform: translateY(0) scale(1); }

.reveal.reveal-blur        { filter: blur(10px); }
.reveal.reveal-blur.in     { filter: blur(0); }

/* Container that staggers its direct children */
.reveal-stagger > * {
  opacity: 0;
  transform: translateY(18px);
  transition:
    opacity .8s cubic-bezier(.22, .8, .2, 1),
    transform .8s cubic-bezier(.22, .8, .2, 1);
}
.reveal-stagger.in > *:nth-child(1)  { opacity: 1; transform: none; transition-delay: 40ms; }
.reveal-stagger.in > *:nth-child(2)  { opacity: 1; transform: none; transition-delay: 90ms; }
.reveal-stagger.in > *:nth-child(3)  { opacity: 1; transform: none; transition-delay: 140ms; }
.reveal-stagger.in > *:nth-child(4)  { opacity: 1; transform: none; transition-delay: 190ms; }
.reveal-stagger.in > *:nth-child(5)  { opacity: 1; transform: none; transition-delay: 240ms; }
.reveal-stagger.in > *:nth-child(6)  { opacity: 1; transform: none; transition-delay: 290ms; }
.reveal-stagger.in > *:nth-child(7)  { opacity: 1; transform: none; transition-delay: 340ms; }
.reveal-stagger.in > *:nth-child(8)  { opacity: 1; transform: none; transition-delay: 390ms; }
.reveal-stagger.in > *:nth-child(n+9) { opacity: 1; transform: none; transition-delay: 440ms; }

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

/* ============ hero — orchestrated entrance ============
   Page load → staged reveal of the hero elements:
     0     →  page paints with hero section dark, all content invisible
     0–1200ms  →  WebGL canvas fades from 0 → 1 (shader pattern emerges)
     400–1100ms → eyebrow fades up
     ~mount     → particle reveal kicks off on the lead headline
                  (handled by scroll-reveal-text.js via .t-reveal-particles)
     1900–2700ms → sub headline fades up
     2300–3100ms → CTA pills fade up

   Solid dark background on the section so the canvas opacity fade
   doesn't briefly expose the page bg-soft underneath. */
.theme-dark.hero-shader-section,
section.hero-shader-section.theme-dark {
  background: #080808;
}

@keyframes hero-canvas-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes hero-fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}

.hero-shader-section .hero-canvas {
  opacity: 0;
  animation: hero-canvas-in 800ms cubic-bezier(.22, .8, .2, 1) 80ms forwards;
}
.hero-shader-section .hero-eyebrow {
  opacity: 0;
  animation: hero-fade-up 550ms cubic-bezier(.22, .8, .2, 1) 280ms forwards;
}
.hero-shader-section .hero-shader-line--sub {
  opacity: 0;
  animation: hero-fade-up 600ms cubic-bezier(.22, .8, .2, 1) 1250ms forwards;
}
.hero-shader-section .hero-shader-ctas {
  opacity: 0;
  animation: hero-fade-up 600ms cubic-bezier(.22, .8, .2, 1) 1500ms forwards;
}

@media (prefers-reduced-motion: reduce) {
  .hero-shader-section .hero-canvas,
  .hero-shader-section .hero-eyebrow,
  .hero-shader-section .hero-shader-line--sub,
  .hero-shader-section .hero-shader-ctas {
    opacity: 1 !important;
    animation: none !important;
    transform: none !important;
  }
}

/* ============ particle text reveal ============
   Disabled at the JS layer — previous implementation hid chars at
   opacity 0 before the animation could fire, leaving headings
   invisible. CSS rule defaults chars to fully visible so even if
   the splitter accidentally runs, text remains readable. */
.reveal-char {
  display: inline;
  opacity: 1;
}

/* ============ tile reveal — per-card scroll animation ============
   Driven by scroll-reveal-tiles.js. Each tile gets `.tile-reveal` plus
   an `--i` index for stagger. CSS custom properties on :root (set from
   the tweaks panel) control distance, scale, blur, rotation, duration,
   stagger, easing. The `data-tile-style` attribute on <html> picks the
   variant — combinations of translate/scale/blur/rotate. */
:root {
  --tile-distance: 36px;
  --tile-scale: 0.94;
  --tile-blur: 6px;
  --tile-rotate: 0deg;
  --tile-duration: 900ms;
  --tile-stagger: 70ms;
  --tile-easing: cubic-bezier(.22, .8, .2, 1);
}

.tile-reveal {
  opacity: 0;
  transform: translateY(var(--tile-distance)) scale(1);
  filter: none;
  transition:
    opacity var(--tile-duration) var(--tile-easing),
    transform var(--tile-duration) var(--tile-easing),
    filter var(--tile-duration) var(--tile-easing);
  transition-delay: calc(var(--i, 0) * var(--tile-stagger));
  will-change: opacity, transform, filter;
}
.tile-reveal.in {
  opacity: 1;
  transform: none;
  filter: none;
}

/* Style variants — composed transforms layered onto translate */
html[data-tile-style="scale"] .tile-reveal {
  transform: translateY(var(--tile-distance)) scale(var(--tile-scale));
}
html[data-tile-style="blur"] .tile-reveal {
  transform: translateY(var(--tile-distance));
  filter: blur(var(--tile-blur));
}
html[data-tile-style="rotate"] .tile-reveal {
  transform: translateY(var(--tile-distance)) rotate(var(--tile-rotate));
}
html[data-tile-style="mixed"] .tile-reveal {
  transform: translateY(var(--tile-distance)) scale(var(--tile-scale)) rotate(var(--tile-rotate));
  filter: blur(var(--tile-blur));
}
html[data-tile-style="off"] .tile-reveal,
html[data-tile-style="off"] .tile-reveal.in {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
  transition: none !important;
}

@media (prefers-reduced-motion: reduce) {
  .tile-reveal,
  .tile-reveal.in {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
  }
}

/* ============ scroll-linked tiles — driven by scroll-link-cards.js ============
   Each card receives a unitless `--p` (0..1) updated on every scroll
   frame. Opacity / translate / blur interpolate directly from `--p`
   so the cards reveal in lockstep with scroll position and reverse if
   the user scrolls back up. Translate uses the standalone `translate`
   property so it composes with the hover `transform: translateY(-1px)`
   without fighting transitions. */
.scroll-link {
  --p: 0;
  --scroll-link-distance: 40px;
  --scroll-link-blur: 6px;
  opacity: var(--p);
  translate: 0 calc((1 - var(--p)) * var(--scroll-link-distance));
  filter: blur(calc((1 - var(--p)) * var(--scroll-link-blur)));
  will-change: opacity, translate, filter;
}

@media (prefers-reduced-motion: reduce) {
  .scroll-link {
    --p: 1 !important;
    translate: none !important;
    filter: none !important;
  }
}
