/* ============================================================
   Animations
   All decorative keyframe animations and the helper classes
   that drive them (reveal, hero configurator, spec/process
   pipeline, rotating cube, edge-light, glow border).
   Every block respects prefers-reduced-motion.
   ============================================================ */

/* ---- Reveal animation (used by Reveal component) ---- */
@keyframes revealUp {
  from { opacity: 0; transform: translateY(var(--reveal-y, 24px)); }
  to   { opacity: 1; transform: none; }
}

/* ---- Looping scan line (payload budget section) ---- */
@keyframes scanLoop {
  0%   { left: -30%; }
  100% { left: 110%; }
}

/* hero configurator animations */
@keyframes hcFloat { 0%,100% { transform: translate(-50%,-50%) translateY(0) rotateZ(0deg); } 50% { transform: translate(-50%,-50%) translateY(-8px) rotateZ(-0.4deg); } }
.hc-float { animation: hcFloat 7s var(--ease) infinite; }
@keyframes hcSweep { 0% { left: -45%; } 55%,100% { left: 130%; } }
.hc-sweep { animation: hcSweep 6s var(--ease) infinite; }
@keyframes hcPing { from { transform: scale(0.5); opacity: 0.9; } to { transform: scale(1.5); opacity: 0; } }

/* ---- Spec card: "server-side document generation" pulse ----
   Full cycle 4.5s:  0.0s SHARE click → packet → ~0.7s URL flash
   → packet → ~1.5s PDF pop → ~2s hold → repeat. */
/* ⚠ These three keyframe blocks must NOT animate `transform`.
   A transform inside an infinite keyframe promotes the box to its own composited layer
   (measured, Chrome CDP LayerTree.compositingReasons -> ["ActiveTransformAnimation"]), and
   inside .cap-card's backdrop-filter containing block the inline <svg> injected by <Icon>
   can drop out of that layer's raster — leaving an empty grey box. It then STAYS empty,
   because from 14% to 100% every animated property holds a constant value, so no repaint is
   scheduled for ~3.9s of the 4.5s loop; the next cycle's ramp dirties the element and the
   icon reappears. That is exactly the reported symptom: SHARE and PDF blank, URL fine
   (.spec-box-url has never had a transform), recovering when the loop restarts.
   Convey press/pop with PAINT properties only. */
/* SHARE box presses in at the very start of the cycle */
.spec-box-share {
  animation: specPress 4.5s ease-in-out infinite;
}
@keyframes specPress {
  0%       { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
  3%       { border-color: var(--accent); background: var(--accent); box-shadow: 0 0 16px color-mix(in oklab, var(--accent) 50%, transparent), inset 0 0 0 3px color-mix(in oklab, #000 22%, transparent); color: var(--accent-fg); }
  14%      { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
  100%     { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
}
/* data packet travelling along each arrow */
.spec-packet {
  position: absolute; top: 50%; left: 0;
  width: 7px; height: 7px; margin-top: -3.5px; border-radius: 50%;
  background: #fff; opacity: 0;
  box-shadow: 0 0 6px var(--accent), 0 0 12px var(--accent);
  animation: specPacket 4.5s ease-in-out infinite;
}
@keyframes specPacket {
  0%   { left: 0%;   opacity: 0; }   /* waits for the SHARE press */
  6%   { left: 0%;   opacity: 0; }
  8%   { opacity: 1; }
  15%  { opacity: 1; }
  17%  { left: 100%; opacity: 0; }
  100% { left: 100%; opacity: 0; }
}
/* URL box flashes when the packet arrives */
.spec-box-url {
  animation: specFlash 4.5s ease-in-out infinite;
  animation-delay: 0.7s;
}
@keyframes specFlash {
  0%, 100% { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
  3%       { border-color: var(--accent); background: var(--accent); box-shadow: 0 0 16px color-mix(in oklab, var(--accent) 50%, transparent); color: var(--accent-fg); }
  12%      { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
}
/* PDF box flashes + bounces when the packet arrives */
.spec-box-pdf {
  animation: specPop 4.5s ease-in-out infinite;
  animation-delay: 1.5s;
}
/* No transform here either — see the note above specPress. The "pop" is a glow overshoot
   (22px at the peak, settling to 16px) instead of a geometric bounce. */
@keyframes specPop {
  0%, 100% { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
  3%       { border-color: var(--accent); background: var(--accent); box-shadow: 0 0 22px color-mix(in oklab, var(--accent) 62%, transparent); color: var(--accent-fg); }
  9%       { box-shadow: 0 0 16px color-mix(in oklab, var(--accent) 50%, transparent); }
  14%      { border-color: var(--line-1); background: var(--surface-veil); box-shadow: none; color: var(--fg-1); }
}
@media (prefers-reduced-motion: reduce) {
  .spec-packet, .spec-box-share, .spec-box-url, .spec-box-pdf { animation: none; }
}

/* ---- Smart-camera card: rotating 3D cube ---- */
.cube-scene { perspective: 360px; }
.cube {
  position: relative; transform-style: preserve-3d;
  animation: cubeSpin 12s ease-in-out infinite;
}
.cube .face {
  position: absolute; top: 50%; left: 50%;
  border: 1px solid var(--accent);
  background: color-mix(in oklab, var(--accent) 16%, rgba(10,12,18,0.55));
  box-shadow: inset 0 0 24px color-mix(in oklab, var(--accent) 22%, transparent);
}
@keyframes cubeSpin {
  0%   { transform: rotateX(-18deg) rotateY(0deg); }
  50%  { transform: rotateX(-18deg) rotateY(-50deg); }
  100% { transform: rotateX(-18deg) rotateY(0deg); }
}
@media (prefers-reduced-motion: reduce) {
  .cube { animation: none; }
}

/* ---- Dims card: value recalculation flash ---- */
.dim-readout { animation: dimFlash 0.6s var(--ease); }
@keyframes dimFlash {
  0%   { opacity: 0.25; transform: translateX(-3px); }
  100% { opacity: 1;    transform: translateX(0); }
}

/* SKU tile: pops big + accent-flashes, then settles back to normal size & colour */
@keyframes skuPop {
  0%   { transform: scale(1);    border-color: var(--line-1); box-shadow: none; }
  30%  { transform: scale(1.16); border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 32%, transparent), 0 12px 28px color-mix(in oklab, var(--accent) 38%, transparent); }
  100% { transform: scale(1);    border-color: var(--line-1); box-shadow: none; }
}

/* CORE WEBGL ENGINE: pops as the data packet arrives, then settles */
@keyframes enginePulse {
  0%   { transform: scale(1);    box-shadow: 0 14px 34px rgba(0,0,0,0.35), 0 0 30px color-mix(in oklab, var(--accent) 24%, transparent); }
  40%  { transform: scale(1.09); box-shadow: 0 14px 34px rgba(0,0,0,0.35), 0 0 34px color-mix(in oklab, var(--accent) 38%, transparent); }
  100% { transform: scale(1);    box-shadow: 0 14px 34px rgba(0,0,0,0.35), 0 0 30px color-mix(in oklab, var(--accent) 24%, transparent); }
}

/* Performance stats: glossy shimmer sweep across the big numbers + breathing symbol */
/* Stay inside 0%–100%. With background-clip:text the glyphs are only visible
   where the gradient actually covers them; positions outside that range leave
   part of the text uncovered (invisible) and force background-repeat to fill
   the gap — which is what dragged a second highlight band into view. */
@keyframes statShimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: 0% 0; }
}
.stat-shimmer { animation: statShimmer 5s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .stat-shimmer { animation: none; }
}

/* ---- Process pipeline loop animations ---- */
/* Energy pulse travelling down the central pipe, forever (constant speed) */
@keyframes pipeFlow {
  0%   { top: -25%; opacity: 0; }
  10%  { opacity: 1; }
  90%  { opacity: 1; }
  100% { top: 100%; opacity: 0; }
}
.pipe-pulse { animation: pipeFlow 4s linear infinite; }

/* Bright fill that follows the pulse down the pipe, then resets each loop */
.pipe-fill {
  position: absolute; top: 0; left: 0; width: 100%;
  border-radius: 99px;
  background: linear-gradient(to bottom, var(--accent) 80%, transparent);
  box-shadow: 0 0 16px var(--accent), 0 0 40px color-mix(in oklab, var(--accent) 60%, transparent);
  pointer-events: none;
  animation: pipeFill 4s linear infinite;
}
@keyframes pipeFill {
  0%   { height: 0%;   opacity: 1; }
  90%  { height: 100%; opacity: 1; }
  95%  { height: 100%; opacity: 0; }
  100% { height: 0%;   opacity: 0; }
}

/* Connector node: dim until the pulse reaches it, then lights up and stays lit
   — all nodes go dark together at ~92% so the wave restarts clean each loop. */
.pipe-node { animation-duration: 4s; animation-timing-function: linear; animation-iteration-count: infinite; }
.pipe-node-1 { animation-name: nodeLit1; }
.pipe-node-2 { animation-name: nodeLit2; }
.pipe-node-3 { animation-name: nodeLit3; }
.pipe-node-4 { animation-name: nodeLit4; }
.pipe-node-5 { animation-name: nodeLit5; }
@keyframes nodeLit1 {
  0%, 14%  { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
  19%, 90% { border-color: var(--accent); background: color-mix(in oklab, var(--bg-0) 45%, var(--accent)); box-shadow: 0 0 14px var(--accent), 0 0 34px color-mix(in oklab, var(--accent) 60%, transparent); color: var(--fg-0); }
  95%, 100% { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
}
@keyframes nodeLit2 {
  0%, 30%  { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
  35%, 90% { border-color: var(--accent); background: color-mix(in oklab, var(--bg-0) 45%, var(--accent)); box-shadow: 0 0 14px var(--accent), 0 0 34px color-mix(in oklab, var(--accent) 60%, transparent); color: var(--fg-0); }
  95%, 100% { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
}
@keyframes nodeLit3 {
  0%, 46%  { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
  51%, 90% { border-color: var(--accent); background: color-mix(in oklab, var(--bg-0) 45%, var(--accent)); box-shadow: 0 0 14px var(--accent), 0 0 34px color-mix(in oklab, var(--accent) 60%, transparent); color: var(--fg-0); }
  95%, 100% { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
}
@keyframes nodeLit4 {
  0%, 62%  { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
  67%, 90% { border-color: var(--accent); background: color-mix(in oklab, var(--bg-0) 45%, var(--accent)); box-shadow: 0 0 14px var(--accent), 0 0 34px color-mix(in oklab, var(--accent) 60%, transparent); color: var(--fg-0); }
  95%, 100% { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
}
@keyframes nodeLit5 {
  0%, 78%  { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
  83%, 90% { border-color: var(--accent); background: color-mix(in oklab, var(--bg-0) 45%, var(--accent)); box-shadow: 0 0 14px var(--accent), 0 0 34px color-mix(in oklab, var(--accent) 60%, transparent); color: var(--fg-0); }
  95%, 100% { border-color: var(--line-1); background: color-mix(in oklab, var(--bg-0) 78%, var(--accent)); box-shadow: none; color: var(--fg-2); }
}

/* White light running straight DOWN the card's side borders,
   in the same 4s rhythm as the central pipe pulse */
.edge-light {
  position: absolute; width: 2px; height: 46%;
  border-radius: 99px; z-index: 3; opacity: 0;
  background: linear-gradient(180deg, transparent, #fff 38%, var(--accent) 72%, transparent);
  box-shadow: 0 0 8px var(--accent), 0 0 16px color-mix(in oklab, var(--accent) 60%, transparent);
  pointer-events: none;
  animation: edgeRun 4s linear infinite;
  animation-delay: var(--d, 0s);
}
.edge-light.left  { left: 0; }
.edge-light.right { right: 0; }
/* descends top -> bottom over ~1s, in sync with the pulse passing,
   then idle until the next loop */
@keyframes edgeRun {
  0%   { top: -46%; opacity: 0; }
  4%   { opacity: 1; }
  20%  { opacity: 1; }
  25%  { top: 100%; opacity: 0; }
  100% { top: 100%; opacity: 0; }
}

/* ---- Premium soft pulse border (breathing) ---- */
.glow-border {
  border: 1px solid color-mix(in oklab, var(--accent) 40%, var(--line-0));
  background: var(--bg-1);
  animation: softBreathe 4s ease-in-out infinite alternate;
}
@keyframes softBreathe {
  0% {
    border-color: color-mix(in oklab, var(--accent) 15%, var(--line-0));
    box-shadow:
      0 0 10px color-mix(in oklab, var(--accent) 0%, transparent),
      inset 0 0 10px color-mix(in oklab, var(--accent) 0%, transparent);
  }
  100% {
    border-color: color-mix(in oklab, var(--accent) 60%, var(--line-0));
    box-shadow:
      0 0 35px color-mix(in oklab, var(--accent) 25%, transparent),
      inset 0 0 15px color-mix(in oklab, var(--accent) 10%, transparent);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* .pipe-fill was missing here, and it is the one loop on the page that animates `height`
     — layout on every frame, forever. Of all the animations to leave running for someone who
     asked for less motion, that was the worst one. Its `opacity` stays put: unlike the other
     three it is the filled part of the pipe, not an effect on top, so hiding it would empty
     the diagram rather than still it. */
  .pipe-fill { animation: none; }
  .pipe-pulse, .pipe-node, .edge-light { animation: none; opacity: 0; }
  .glow-border { animation: none; }
}
