/* ═══════════════════════════════════════════════════
   SPHRAGIO — Unified loading animation (upload in-flight + status page)

   Honest motion only (kill-circle doctrine): every animated element is keyed off
   the SERVER-marked step state. Loops attach ONLY to the active step (breathe +
   fixed-segment connector shimmer) so nothing implies advancement the pipeline
   hasn't made; pending steps never move. No fake clock, no percentage, no ring.

   DaisyUI pseudo-element roles in THIS build (verified): .step:after = the
   numbered dot/marker, .step:before = the connector line. The hooks below
   (.sfx-step-active / .sfx-step-done / .sfx-card) are layered ON TOP of the
   DaisyUI classes, which stay for the status-fragment test pins.
   Custom .sfx-* classes are Tailwind-unscanned — no rebuild needed.
   ═══════════════════════════════════════════════════ */

/* ── Loading-twin page title — the serif display voice every other page
   title carries since the academic restyle (#444) + marketing twin (#443).
   Layered onto the existing text-3xl utility (sizes untouched). ── */
.sfx-title {
  font-family: var(--font-display);
  letter-spacing: -0.01em;
  color: var(--navy);
}

/* ── Ambient brand calm — whole-card indigo glow (both surfaces) ──
   Surface override: the branded token sheet (white→indigo tint, hairline
   border) replaces DaisyUI's generic bg-base-200 gray. The DaisyUI classes
   stay in the markup for the status-fragment test pins — this rule simply
   loads later and wins. */
.sfx-card {
  animation: sfx-card-glow 6s ease-in-out infinite;
  background: linear-gradient(180deg, #fff 0%, var(--indigo-50) 100%);
  border: 1px solid var(--indigo-100);
}
@keyframes sfx-card-glow {
  0%, 100% { box-shadow: var(--shadow-card); }
  50%      { box-shadow: 0 6px 30px color-mix(in srgb, var(--navy-light) 16%, transparent); }
}

/* ── Active step — breathing dot (the marker = .step:after) ── */
.sfx-stepper .sfx-step-active:after {
  animation: sfx-breathe 2.4s ease-in-out infinite;
}
@keyframes sfx-breathe {
  0%, 100% { transform: scale(1);    box-shadow: 0 0 0 0   transparent; }
  50%      { transform: scale(1.12); box-shadow: 0 0 0 7px color-mix(in srgb, var(--navy-light) 16%, transparent); }
}

/* ── Active step — connector shimmer (the line = .step:before) ──
   A fixed-segment light travels the gap ABOVE the active dot only: reads
   "working here", never "progressing toward pending". Suppressed on the first
   step (nothing above it). */
.sfx-stepper .sfx-step-active:not(:first-child):before {
  background-image: linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--off-white) 75%, transparent) 50%, transparent 100%);
  background-size: 100% 55%;
  background-repeat: no-repeat;
  animation: sfx-shimmer 2.8s linear infinite;
}
@keyframes sfx-shimmer {
  from { background-position: 0 -120%; }
  to   { background-position: 0 220%; }
}

/* ── Done step — calm settle (no re-firing pop under HTMX poll swaps) ── */
.sfx-stepper .sfx-step-done:after { transition: background-color 0.4s ease, color 0.4s ease; }

/* ── Entry: whole-wrapper fade so each surface assembles the same way. The
   status wrapper uses .sfx-status (fade only) NOT .sfx-loading, because its
   stepper lives inside the HTMX-swapped #status-container — the per-li stagger
   below must not re-fire on every 3 s poll. ── */
.sfx-loading,
.sfx-status { animation: sfx-fadein 0.3s ease-out both; }
@keyframes sfx-fadein {
  from { opacity: 0.92; transform: translateY(4px); }
  to   { opacity: 1;    transform: none; }
}

/* ── Upload-only staggered step reveal (static phase; never under HTMX swap) ── */
.sfx-loading .sfx-stepper > li { animation: sfx-step-in 0.45s ease-out both; }
@keyframes sfx-step-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
.sfx-loading .sfx-stepper > li:nth-child(1) { animation-delay: 0.00s; }
.sfx-loading .sfx-stepper > li:nth-child(2) { animation-delay: 0.06s; }
.sfx-loading .sfx-stepper > li:nth-child(3) { animation-delay: 0.12s; }
.sfx-loading .sfx-stepper > li:nth-child(4) { animation-delay: 0.18s; }
.sfx-loading .sfx-stepper > li:nth-child(5) { animation-delay: 0.24s; }
.sfx-loading .sfx-stepper > li:nth-child(6) { animation-delay: 0.30s; }
.sfx-loading .sfx-stepper > li:nth-child(7) { animation-delay: 0.36s; }

/* ── Cross-page handoff: fade-out added by loading-animation.js before nav ── */
.sfx-leaving {
  opacity: 0.92;
  transform: translateY(2px);
  transition: opacity 0.18s cubic-bezier(0.4, 0, 0.2, 1), transform 0.18s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ── Reduced motion: calm, fully-legible static fallback (geometry unchanged) ── */
@media (prefers-reduced-motion: reduce) {
  .sfx-card,
  .sfx-stepper .sfx-step-active:after,
  .sfx-stepper .sfx-step-active:not(:first-child):before,
  .sfx-loading,
  .sfx-status,
  .sfx-loading .sfx-stepper > li,
  .sfx-leaving { animation: none; transition: none; }
  .sfx-stepper .sfx-step-active:after { box-shadow: 0 0 0 3px var(--indigo-100); }
  .sfx-stepper .sfx-step-active:not(:first-child):before { background-image: none; }
  .sfx-leaving { opacity: 1; transform: none; }
}
