/* ============================================================
   animations.css — keyframes + scroll-driven reveal
   Reveals are SCROLL-position driven (animation-timeline: view())
   so they resolve correctly even when the document clock is paused
   (screenshots / background tabs). Default state is fully VISIBLE,
   so no-JS / unsupported / reduced-motion never blanks content.
   ============================================================ */

/* ---- reveal keyframes (animate `translate`/`opacity` ONLY, so JS
   `transform` tilt + parallax compose without fighting the cascade) ---- */
@keyframes reveal-in        { from { opacity: 0; translate: 0 30px; } to { opacity: 1; translate: 0 0; } }
@keyframes reveal-in-left   { from { opacity: 0; translate: -36px 0; } to { opacity: 1; translate: 0 0; } }
@keyframes reveal-in-right  { from { opacity: 0; translate: 36px 0; } to { opacity: 1; translate: 0 0; } }
@keyframes bar-grow         { from { width: 0; } to { width: var(--fill, 100%); } }

/* default: fully visible (bulletproof) */
.reveal, .reveal-left, .reveal-right { opacity: 1; }

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .reveal      { animation: reveal-in       both linear; animation-timeline: view(); animation-range: entry 5% cover 22%; }
    .reveal-left { animation: reveal-in-left  both linear; animation-timeline: view(); animation-range: entry 5% cover 22%; }
    .reveal-right{ animation: reveal-in-right both linear; animation-timeline: view(); animation-range: entry 5% cover 22%; }
    /* small stagger so grouped items cascade */
    .reveal[data-d="1"], .reveal-left[data-d="1"], .reveal-right[data-d="1"] { animation-range: entry 9% cover 26%; }
    .reveal[data-d="2"], .reveal-left[data-d="2"], .reveal-right[data-d="2"] { animation-range: entry 13% cover 30%; }
    .reveal[data-d="3"] { animation-range: entry 17% cover 34%; }
    .reveal[data-d="4"] { animation-range: entry 20% cover 37%; }
    .reveal[data-d="5"] { animation-range: entry 23% cover 40%; }
    .reveal[data-d="6"] { animation-range: entry 26% cover 43%; }
  }
}

/* ---- keyframes (decorative / time-based) ---- */
@keyframes eq {
  0%, 100% { height: 18%; }
  50% { height: 100%; }
}

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

@keyframes pulse-dot {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.35); opacity: 0.55; }
}

@keyframes spin-slow { to { transform: rotate(360deg); } }

@keyframes grain {
  0% { transform: translate(0,0); }
  25% { transform: translate(-4%, 3%); }
  50% { transform: translate(3%, -3%); }
  75% { transform: translate(-2%, 2%); }
  100% { transform: translate(0,0); }
}

@keyframes scroll-sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

@keyframes konami-fall {
  to { transform: translateY(110vh) rotate(540deg); opacity: 0.9; }
}

@keyframes float-soft {
  0%, 100% { translate: 0 0; }
  50% { translate: 0 -8px; }
}
.portrait { animation: float-soft 6s var(--ease-in-out) infinite; }
