/* THE LOADER.
 *
 * A roster resolving, not a spinner. Every line is a venue we really read and
 * a count that really came back; see loader.js for why it refuses to invent
 * work or animate a percentage it cannot know.
 *
 * TYPE SIZES ARE STATED AT !important DELIBERATELY. instrument.css carries
 * `body :not(svg):not(svg *) { font-size: var(--t-sm) !important }`, a blanket
 * that flattens every element on the page to the body step. today.css declared
 * a whole scale that silently never applied — its 60px hero measured 15px on
 * the rendered page — and this file would have gone the same way. `!important`
 * beats specificity, so a plain selector can never win; these restate at the
 * same weight. Specificity breaks the tie: a single class is (0,1,0) against
 * the normaliser's (0,0,3), whose `:not()` contributes its argument.
 */

.ld {
  display: flex; flex-direction: column; gap: 14px;
  padding: 30px 0 34px;
  max-width: 420px;
}

.ld-t {
  margin: 0;
  font-size: 11px !important;
  text-transform: uppercase; letter-spacing: .09em;
  color: var(--ink3);
  display: flex; align-items: center; gap: 8px;
}

/* Three dots that breathe. The only part of this that is decoration rather
   than data, so it is the only part that moves on its own. */
.ld-e { display: inline-flex; gap: 3px; }
.ld-e i {
  width: 3px; height: 3px; border-radius: 50%;
  background: var(--accent);
  animation: ld-pulse 1.15s ease-in-out infinite;
}
.ld-e i:nth-child(2) { animation-delay: .16s; }
.ld-e i:nth-child(3) { animation-delay: .32s; }
@keyframes ld-pulse {
  0%, 100% { opacity: .25; transform: scale(.8); }
  50%      { opacity: 1;   transform: scale(1); }
}

/* ── THE ROSTER ────────────────────────────────────────────────────────── */
.ld-rows { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0; }

/* Each row arrives on a stagger and STAYS. Nothing here loops: a row that kept
   re-animating would be an idle decoration pretending to be progress. */
.ld-r {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 5px 0;
  border-bottom: 1px solid var(--line);
  font-size: 13px !important;
  color: var(--ink2);
  opacity: 0;
  animation: ld-in 320ms cubic-bezier(.22, .61, .36, 1) forwards;
}
.ld-r:last-child { border-bottom: 0; }
@keyframes ld-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

.ld-v { display: inline-flex; align-items: center; gap: 7px; min-width: 0; }
.ld-v .mk { flex: 0 0 auto; border-radius: 4px; }

.ld-n {
  color: var(--ink); font-weight: 600;
  font-variant-numeric: tabular-nums;
  font-size: 13px !important;
}
.ld-n em {
  font-style: normal; font-weight: 400; color: var(--ink3);
  font-size: 11px !important; margin-left: 5px;
}

.ld-f {
  margin: 0; color: var(--ink3);
  font-size: 12px !important;
  opacity: 0;
}
.ld-f.on { animation: ld-in 320ms cubic-bezier(.22, .61, .36, 1) forwards; }
.ld-f b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }

/* REDUCED MOTION: land on the finished roster, never on an empty box. The
   rows are the content; only their arrival was ever the animation. */
@media (prefers-reduced-motion: reduce) {
  .ld-r, .ld-f.on {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .ld-e i { animation: none !important; opacity: .7 !important; }
}
