/* ==========================================================================
   360Bounce — motion layer
   Everything here is progressive: content is visible without JS, without
   GSAP and with reduced motion. JS adds `.js` (boot.js) and `.anim-ready`
   (main.js once GSAP is running).
   ========================================================================== */

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes dash { to { stroke-dashoffset: -64; } }
@keyframes grain {
  0%, 100% { transform: translate(0, 0); }
  10% { transform: translate(-5%, -10%); }
  30% { transform: translate(3%, -15%); }
  50% { transform: translate(12%, 9%); }
  70% { transform: translate(9%, 4%); }
  90% { transform: translate(-1%, 7%); }
}
@keyframes sf-orbit { to { transform: translate(-50%, -50%) rotateX(68deg) rotateZ(360deg); } }
@keyframes sf-pulse { 50% { opacity: 0.55; transform: translate(-50%, -50%) scale(1.08); } }
@keyframes preloader-out { to { opacity: 0; visibility: hidden; } }
@keyframes failsafe-show { to { opacity: 1; transform: none; } }
@keyframes cue-bob { 50% { transform: translateY(4px); } }

.sf-ring--a { animation: sf-orbit 40s linear infinite; }
.sf-ring--b { animation: sf-orbit 46s linear infinite reverse; }
.sf-ring--c { animation: sf-orbit 18s linear infinite; }
.sf-glow { animation: sf-bob 5s var(--ease-io) infinite; }
@keyframes sf-bob { 50% { transform: translate(-50%, -62%); } }
.scroll-cue .meta { animation: cue-bob 2.4s var(--ease-io) infinite; }

/* ---------- preloader ---------- */
.preloader {
  position: fixed; inset: 0; z-index: 300; display: grid; place-items: center; background: var(--color-background);
  /* CSS failsafe: disappears even if JavaScript never runs */
  animation: preloader-out 0.6s ease 4s forwards;
}
html:not(.js) .preloader, .preloader.is-skipped { display: none; }
.preloader.is-done { animation: none; opacity: 0; visibility: hidden; transition: opacity 0.7s var(--ease-out), visibility 0s linear 0.7s; }
.anim-ready .preloader { animation: none; }
.preloader-inner { position: relative; display: grid; place-items: center; width: min(78vw, 360px); aspect-ratio: 1; }
.preloader-ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.preloader-track { fill: none; stroke: var(--color-border); stroke-width: 1; }
.preloader-arc { fill: none; stroke: var(--brand-lime); stroke-width: 1.2; stroke-dasharray: 100; stroke-dashoffset: 100; stroke-linecap: round; }
.preloader-word { line-height: 0; }
.preloader-count { position: absolute; bottom: 22%; color: var(--color-text-secondary); }

/* ---------- page transition wipe ---------- */
.page-wipe {
  position: fixed; inset: 0; z-index: 250; pointer-events: none; background: var(--color-surface);
  transform: scaleY(0); transform-origin: bottom; display: grid; place-items: center;
}
.page-wipe-mark { width: 64px; height: 64px; border-radius: 16px; opacity: 0; }
.page-wipe.is-leaving { transform: scaleY(1); transition: transform 0.38s var(--ease-io); }
.page-wipe.is-leaving .page-wipe-mark { opacity: 1; animation: wipe-pulse 0.9s var(--ease-io) infinite alternate; transition: opacity 0.3s 0.3s; }
@keyframes wipe-pulse { from { transform: scale(0.94); } to { transform: scale(1.04); } }
.page-wipe.is-entering { transform: scaleY(1); transform-origin: top; }
.page-wipe.is-entering.is-run { transform: scaleY(0); transition: transform 0.7s var(--ease-io); }

/* ---------- initial states for the hero intro ----------
   Only applied with JS and motion allowed. A failsafe animation reveals the
   content after 3.5s if main.js never takes over. */
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .line-i {
  transform: translateY(110%);
  animation: failsafe-show 0.01s linear 3.5s forwards;
}
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .hero-top,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .hero-bottom,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .page-hero-foot,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .section-kicker,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .breadcrumbs,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .hero-cta-row,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .article-meta,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .article-byline,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .legal-draft,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .error-links,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .svc-hero-copy > .lead,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) [data-hero] .page-hero-copy > .lead,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) .page-hero[data-hero] > .lead,
.js:not(.reduced-motion):not(.no-gsap):not(.anim-ready) .article-hero[data-hero] > .lead {
  opacity: 0;
  animation: failsafe-show 0.01s linear 3.5s forwards;
}

/* ---------- reduced motion: everything still, everything visible ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .grain { animation: none; }
  .preloader { display: none; }
  .art-dash { animation: none; }
}
.reduced-motion .preloader { display: none; }
