@keyframes tab-in {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes float-up {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: translateY(0); }
}

@keyframes glow-pulse {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

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

/* Scroll-reveal as progressive enhancement.
   Content is visible by default; it is only hidden once JS confirms it is
   ready (html.js) and will reveal it. If JS/observer ever fails, a safety
   timeout in reveal_controller adds .is-in, and the final state below holds. */
html.js [data-reveal] { opacity: 0; transform: translateY(24px); }
html.js .stagger > * { opacity: 0; }

html.js [data-reveal].is-in { opacity: 1; transform: none; animation: float-up 0.8s var(--ease-out) forwards; }
html.js .stagger.is-in > * { opacity: 1; animation: float-up 0.7s var(--ease-out) forwards; }
.stagger.is-in > *:nth-child(1) { animation-delay: 0.04s; }
.stagger.is-in > *:nth-child(2) { animation-delay: 0.10s; }
.stagger.is-in > *:nth-child(3) { animation-delay: 0.16s; }
.stagger.is-in > *:nth-child(4) { animation-delay: 0.22s; }
.stagger.is-in > *:nth-child(5) { animation-delay: 0.28s; }
.stagger.is-in > *:nth-child(6) { animation-delay: 0.34s; }
.stagger.is-in > *:nth-child(7) { animation-delay: 0.40s; }
.stagger.is-in > *:nth-child(8) { animation-delay: 0.46s; }
.stagger.is-in > *:nth-child(n+9) { animation-delay: 0.5s; }

/* View-transition naming for the persistent shell (no morph on sidebar). */
.sidebar { view-transition-name: sidebar; }
::view-transition-old(sidebar),
::view-transition-new(sidebar) { animation: none; }

::view-transition-old(root) { animation: vt-out 0.32s var(--ease) both; }
::view-transition-new(root) { animation: vt-in 0.42s var(--ease-out) both; }
@keyframes vt-out { to { opacity: 0; transform: translateY(-8px); } }
@keyframes vt-in  { from { opacity: 0; transform: translateY(12px); } }
