/* =====================================================
 *  animations.css — keyframes
 *
 *  Every keyframe in this file animates ONE of: opacity,
 *  background-position, transform on a small element, or
 *  stroke-dashoffset on a small SVG. No blurs animated, no
 *  filters, no big elements moving. Total per-frame cost is
 *  tiny because each animated element repaints only its own
 *  bounding box, not the whole page.
 * =================================================== */

/* Live status dot — pulsing opacity. Element is 6-8px so the repaint area
 * is microscopic. */
@keyframes dot-pulse {
  0%, 100% { opacity: 0.55; }
  50%      { opacity: 1;    }
}

/* Same idea, slightly stronger — used for active "now playing" indicators */
@keyframes live-pulse {
  0%, 100% { opacity: 0.6;  transform: scale(0.95); }
  50%      { opacity: 1;    transform: scale(1.1);  }
}

/* Single ring on the Discord status dot. Element is ~14px so even a scale
 * to 2.2x is a 30px area — trivial to repaint. */
@keyframes status-ring {
  0%   { transform: scale(1);   opacity: 0.7; }
  100% { transform: scale(2.2); opacity: 0;   }
}

/* Spotify disc — pure rotation on a 100x100 SVG. Cheap. */
@keyframes spin { to { transform: rotate(360deg); } }

/* Idle waveform bars — scaleY on 3px-wide spans. Bounding boxes are
 * 3x28 each so repaint cost is invisible. */
@keyframes waveform {
  0%, 100% { transform: scaleY(0.25); }
  50%      { transform: scaleY(1);    }
}

/* Skeleton shimmer — translates a gradient strip across the parent.
 * Was animating background-position; that forces a paint of the full
 * skeleton rect each frame because the painter has to resample the
 * gradient at the new offset. translateX on a pseudo-element only
 * touches the compositor, so it stays cheap when the GPU is off. */
@keyframes shimmer-slide {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%);  }
}

/* Hero name char reveal */
@keyframes char-in {
  from { opacity: 0; transform: translateY(0.6em); }
  to   { opacity: 1; transform: translateY(0);     }
}

/* Card entrance fallback (if GSAP fails) */
@keyframes card-fallback-in {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: translateY(0);    }
}

/* Terminal cursor blink — single 8x14 span, opacity only. */
@keyframes blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* Stroke-draw on a small SVG accent path (e.g. corner brackets) */
@keyframes stroke-draw {
  from { stroke-dashoffset: var(--len, 100); }
  to   { stroke-dashoffset: 0; }
}

/* Marquee — translateX on a small element only used when a track title
 * overflows. Loops slowly. */
@keyframes marquee {
  from { transform: translateX(0);     }
  to   { transform: translateX(-50%);  }
}

/* Number ticker — used by the stats hero. Fades in fresh values. */
@keyframes value-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: translateY(0);    }
}

/* Progress bar glow — uses a static box-shadow (no blur animation).
 * We only fade opacity in/out so the GPU doesn't have to re-blur. */
@keyframes glow-fade {
  0%, 100% { opacity: 0.7; }
  50%      { opacity: 1;   }
}

/* Aurora orb drift — tiny translate + scale on a blurred element.
 * Each orb keeps its own delay/duration so they desync naturally. */
@keyframes float-orb {
  0%, 100% { transform: translate(0, 0) scale(1); }
  33%      { transform: translate(30px, -20px) scale(1.05); }
  66%      { transform: translate(-20px, 15px) scale(0.97); }
}

/* Pulse-glow — used by the "available" pill dot. Was animating
 * box-shadow blur radius, which forces a paint each frame because
 * the rasterizer has to re-blur the shadow. Now scales + fades the
 * dot itself; the static box-shadow on `.pill-available .dot` stays
 * put so the glow is preserved, and the breathing pulse is delivered
 * cheaply by the compositor (transform + opacity only). */
@keyframes pulse-glow {
  0%, 100% { opacity: 1;   transform: scale(1);    }
  50%      { opacity: 0.6; transform: scale(1.15); }
}

/* Hero name glitch — two clipped pseudo layers offset on hover. */
@keyframes glitch-1 {
  0%, 100% { transform: translateX(-2px); }
  50%      { transform: translateX(2px);  }
}
@keyframes glitch-2 {
  0%, 100% { transform: translateX(2px);  }
  50%      { transform: translateX(-2px); }
}

/* Conic gradient halo rotation — used by the .glass.card::before halo.
 * Drives the @property --angle from 0deg → 360deg. */
@keyframes rotate-angle { to { --angle: 360deg; } }

/* Hero name gradient drift — animates background-position on .char so
 * the gradient inside the text shifts subtly forever. */
@keyframes gradient-shift {
  0%, 100% { background-position:   0% 50%; }
  50%      { background-position: 100% 50%; }
}

/* Aurora orb hue drift — gentle saturation pulse via opacity. */
@keyframes orb-hue {
  0%, 100% { opacity: 0.85; }
  50%      { opacity: 1.10; }
}

/* Underline draw — used on JSX section headers when a card is hovered. */
@keyframes underline-draw {
  from { transform: scaleX(0);   transform-origin: left;  }
  to   { transform: scaleX(1);   transform-origin: left;  }
}
