/* ============================================================
   Loading.

   Two jobs: say what is happening, and hold the shape of what is
   coming. A spinner does neither — it reads as "stuck" the moment it
   lasts more than a second, and the page jumps when content lands.
   ============================================================ */

/* ---------- Branded stage indicator ---------- */
.loadstage {
  display: flex; align-items: center; gap: var(--space-5);
  padding: var(--space-5) var(--space-6);
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  margin-bottom: var(--space-5);
  overflow: hidden;
  position: relative;
}
/* A sweep across the card, so the whole block reads as "working". */
.loadstage::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg,
    transparent 0%,
    color-mix(in srgb, var(--color-primary) 7%, transparent) 45%,
    transparent 90%);
  transform: translateX(-100%);
  animation: sweep 1.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes sweep { to { transform: translateX(100%); } }

.loadstage__mark {
  position: relative; flex: 0 0 auto;
  width: 40px; height: 40px;
  display: grid; place-items: center;
}
/* Three arcs at different speeds — reads as machinery, not a spinner. */
.loadstage__mark i {
  position: absolute; inset: 0;
  border: 2px solid transparent;
  border-radius: 50%;
  border-top-color: var(--color-primary);
  animation: orbit 1.1s cubic-bezier(.6,.2,.4,.8) infinite;
}
.loadstage__mark i:nth-child(2) { inset: 6px; border-top-color: var(--color-ai); animation-duration: 1.5s; animation-direction: reverse; }
.loadstage__mark i:nth-child(3) { inset: 12px; border-top-color: var(--color-accent); animation-duration: .9s; }
@keyframes orbit { to { transform: rotate(360deg); } }

.loadstage__body { min-width: 0; flex: 1 1 auto; }
.loadstage__title { font: var(--font-label); color: var(--text-strong); }
.loadstage__step {
  font: var(--font-caption); color: var(--text-muted); margin-top: 3px;
  display: flex; align-items: center; gap: 7px;
}
.loadstage__step b {
  font-family: var(--font-mono); font-size: var(--text-2xs);
  color: var(--color-primary); font-weight: 600;
}
.loadstage__bar {
  margin-top: 9px; height: 3px; border-radius: 2px;
  background: var(--surface-sunken); overflow: hidden;
}
.loadstage__fill {
  height: 100%; border-radius: 2px;
  background: linear-gradient(90deg, var(--color-primary), var(--color-ai));
  width: 8%;
  transition: width 700ms cubic-bezier(.2,.8,.2,1);
}

/* ---------- Skeletons that hold the real shape ---------- */
.sk {
  position: relative; overflow: hidden;
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
}
.sk::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-subtle) 11%, transparent), transparent);
  transform: translateX(-100%);
  animation: sweep 1.4s ease-in-out infinite;
}
.sk--tile { height: 128px; border-radius: var(--radius-lg); }
.sk--chart { height: 300px; border-radius: var(--radius-lg); }
.sk--chart-sm { height: 240px; border-radius: var(--radius-lg); }
.sk--row { height: 42px; margin-bottom: 8px; }
.sk--line { height: 11px; }
.sk--line.is-short { width: 42%; }

/* Stagger so the block resolves left to right rather than blinking. */
.sk-stagger > * { animation: skfade 480ms both; }
.sk-stagger > *:nth-child(1) { animation-delay: 0ms; }
.sk-stagger > *:nth-child(2) { animation-delay: 70ms; }
.sk-stagger > *:nth-child(3) { animation-delay: 140ms; }
.sk-stagger > *:nth-child(4) { animation-delay: 210ms; }
.sk-stagger > *:nth-child(5) { animation-delay: 280ms; }
.sk-stagger > *:nth-child(6) { animation-delay: 350ms; }
@keyframes skfade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ---------- Arrival ---------- */
/* Content fades up as it replaces its skeleton, so nothing snaps in. */
.arrive { animation: arrive 460ms cubic-bezier(.2,.8,.2,1) both; }
.arrive--1 { animation-delay: 40ms; }
.arrive--2 { animation-delay: 100ms; }
.arrive--3 { animation-delay: 160ms; }
.arrive--4 { animation-delay: 220ms; }
@keyframes arrive { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .loadstage::after, .sk::after, .loadstage__mark i { animation: none; }
  .sk-stagger > *, .arrive { animation: none; }
  .loadstage__fill { transition: none; }
}
