/* The Soundverse loader: what a visitor sees while the app (6.7 MB of JS) is still loading,
   after a tap on an early landing page (public/sv-landing-taps.js) or on a cold load of an app
   route such as /home right after signing in (app/[[...slug]]/AppShell.tsx).

   Every animation here is transform/opacity on HTML elements, which the browser runs off the
   main thread. That matters: while the app boots the main thread is frozen for seconds at a
   time on a slow phone, and anything JS-driven (timers, SVG attribute animation) would freeze
   with it. The messages cycle by CSS too, for the same reason. */

.sv-loader {
  position: fixed;
  inset: 0;
  z-index: 2147483000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #07060c;
  color: #fff;
  font: 500 15px/1.3 system-ui, -apple-system, "Segoe UI", sans-serif;
  animation: sv-loader-in 0.25s ease-out both;
}
/* Over a page the visitor can already see (a tap on the early landing copy), dim it rather than
   replacing it. */
.sv-loader--over {
  background: rgba(7, 6, 12, 0.82);
}
.sv-loader__box {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 26px;
}
.sv-loader__mark {
  position: relative;
  width: 88px;
  height: 88px;
}
.sv-loader__glow {
  position: absolute;
  inset: -28px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 120, 90, 0.22) 0%, rgba(157, 92, 255, 0.14) 45%, transparent 70%);
  animation: sv-loader-glow 3.6s ease-in-out infinite;
  will-change: transform, opacity;
}
/* The Soundverse mark, as agent chat draws it: this static copy (public/soundverse-mark.svg,
   the same dots as src/agentone/AgentOrb.tsx) shows from first paint, at the size and place
   SoundverseOrb draws it (92% of its box, centred). Once the page's JS runs, AppShell lays the
   live orb's canvas over it in its "loading" state and hides this. */
.sv-loader__icon {
  position: absolute;
  inset: 4%;
  width: 92%;
  height: 92%;
}
.sv-loader__orb {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.sv-loader__msgs {
  position: relative;
  width: 280px;
  height: 22px;
  text-align: center;
  color: rgba(255, 255, 255, 0.86);
}
.sv-loader__msgs span {
  position: absolute;
  inset: 0;
  opacity: 0;
  animation: sv-loader-msg 12.5s ease-in-out infinite;
  will-change: transform, opacity;
}
.sv-loader__msgs span:nth-child(2) { animation-delay: 2.5s; }
.sv-loader__msgs span:nth-child(3) { animation-delay: 5s; }
.sv-loader__msgs span:nth-child(4) { animation-delay: 7.5s; }
.sv-loader__msgs span:nth-child(5) { animation-delay: 10s; }

/* Progress: the bar's first stretch runs from first paint with no JS at all (the main thread may
   be busy); public/sv-landing-taps.js (window.svLoader) moves it on as the app actually loads. */
.sv-loader__bar {
  width: 220px;
  height: 4px;
  border-radius: 4px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.1);
}
.sv-loader__bar i {
  display: block;
  height: 100%;
  transform-origin: left center;
  transform: scaleX(0.04);
  background: linear-gradient(90deg, #ffaa2e, #ff4d86, #c654e2, #9d5cff, #5288ff);
  animation: sv-loader-progress 14s cubic-bezier(0.1, 0.6, 0.2, 1) forwards;
  will-change: transform;
}
/* Gone without waiting for React to remove the node: the app's boot can hold React's commits
   for seconds, and an invisible loader must never block a tap. */
/* A note about the connection (sv-landing-taps.js) replaces the cycling lines; the bar stays. */
.sv-loader.is-noted .sv-loader__msgs {
  display: none;
}
.sv-loader__note {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  max-width: 300px;
  text-align: center;
  animation: sv-loader-in 0.3s ease-out both;
}
.sv-loader__note strong {
  font-weight: 600;
  color: #fff;
}
.sv-loader__note span {
  font-size: 14px;
  color: rgba(255, 255, 255, 0.7);
}
.sv-loader__retry {
  margin-top: 10px;
  padding: 9px 20px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.08);
  color: #fff;
  font: inherit;
  font-size: 14px;
  cursor: pointer;
}
.sv-loader__retry:hover {
  background: rgba(255, 255, 255, 0.14);
}

.sv-loader.is-leaving {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  /* Holds for the orb's "done" flash, then fades. */
  transition: opacity 0.4s ease 0.55s, visibility 0s linear 0.95s;
}

@keyframes sv-loader-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes sv-loader-progress { from { transform: scaleX(0.04); } to { transform: scaleX(0.72); } }
@keyframes sv-loader-glow {
  0%, 100% { opacity: 0.6; }
  50% { opacity: 1; }
}
/* Each message holds for ~2.2 s of its 2.5 s slot: rise in, hold, drift out. */
@keyframes sv-loader-msg {
  0% { opacity: 0; transform: translateY(8px); }
  3% { opacity: 1; transform: translateY(0); }
  17% { opacity: 1; transform: translateY(0); }
  20% { opacity: 0; transform: translateY(-8px); }
  100% { opacity: 0; transform: translateY(-8px); }
}

@media (prefers-reduced-motion: reduce) {
  .sv-loader__glow,
  .sv-loader__msgs span { animation: none; }
  .sv-loader__bar i { animation: none; transform: scaleX(0.5); }
  .sv-loader__msgs span:first-child { opacity: 1; }
}
