/* ============================================================================
   utilities.css — helpers + scroll-reveal animation states
   ============================================================================ */

.text-center { text-align: center; }
.text-balance { text-wrap: balance; }
.mt-0 { margin-top: 0 !important; }
.mt-lg { margin-top: var(--sp-lg); }
.mt-xl { margin-top: var(--sp-xl); }
.mt-2xl { margin-top: var(--sp-2xl); }
.full { width: 100%; }
.accent { color: var(--c-accent); }
.theme-paper .accent { color: var(--c-accent-deep); }
.serif { font-family: var(--font-display); }
.mono { font-family: var(--font-mono); }
.nowrap { white-space: nowrap; }
.hide-mobile { display: none; }
@media (min-width: 760px) { .hide-mobile { display: revert; } }

/* — Scroll reveal — opacity + translate, choreographed via --i ———————————— */
[data-reveal] {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity var(--dur-reveal) var(--ease-out),
              transform var(--dur-reveal) var(--ease-out);
  transition-delay: calc(var(--i, 0) * 80ms);
  will-change: opacity, transform;
}
[data-reveal="fade"] { transform: none; }
[data-reveal="left"] { transform: translateX(-26px); }
[data-reveal="scale"] { transform: scale(.96); }
[data-reveal].in-view { opacity: 1; transform: none; }

/* Decorative orbit-line — animated draw via stroke-dashoffset */
.orbit-ring {
  fill: none;
  stroke: currentColor;
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}
.draw {
  stroke-dasharray: var(--len, 1000);
  stroke-dashoffset: var(--len, 1000);
}
.in-view .draw,
.draw.in-view { animation: draw 1.6s var(--ease-out) forwards; }
@keyframes draw { to { stroke-dashoffset: 0; } }

/* slow continuous orbit rotation */
.spin-slow { animation: spin 60s linear infinite; transform-origin: center; }
.spin-rev  { animation: spin 90s linear infinite reverse; transform-origin: center; }
@keyframes spin { to { transform: rotate(360deg); } }
