/* ═══════════════════════════════════════════════════════════════════════════════════════════════
   css/loading.css · PARTS 12 and 15 · the loading set: one definition for every wait, and the live dot (SH-UIUX 2026-10-19)
   Mike, 2026-10-19: "design a spinner… this will be the spinner or load graphic everywhere… if there are multiple
   spinner graphics needed lets create the various ones and reuse them across the web app appropriately."
   Linked on EVERY page, signed in and signed out, before any module's own sheet. Tokens only.
   The set (docs/06-loading.md says when to use which):
     1. .sh-spin                      inline: beside words, in a heading, inside a button. Sized to the text (1em, never under 14 px)
     2. .sh-loading + .sh-spin-lg     block: a panel whose shape isn't known yet (a viewer, a map). Appears only after 400 ms
     3. skeleton bars + .sh-wait      a list or record whose shape IS known (shell.css, already ruled; unchanged)
   And one that isn't a wait (Part 15):
     .sh-live                         something happening now (recording). Never used for loading
   Never a spinner without words beside it. The spinner is aria-hidden; the words carry role="status".
   ═══════════════════════════════════════════════════════════════════════════════════════════════ */
@keyframes sh-turn { to { transform:rotate(360deg); } }
@keyframes sh-breathe { 50% { opacity:.35; } }
@keyframes sh-arrive { from { opacity:0; } to { opacity:1; } }

/* 1 · inline: a ring in the surrounding text's colour, one quarter drawn, turning once every 0.8 s */
.sh-spin { display:inline-block; flex:none; box-sizing:border-box; width:1em; height:1em; min-width:14px; min-height:14px;
  border-radius:50%; border:2px solid color-mix(in srgb, currentColor 22%, transparent); border-top-color:currentColor;
  vertical-align:-.15em; animation:sh-turn .8s linear infinite; }
/* inside a button it sits before the label; the button keeps its width and is disabled while it shows */
.btn .sh-spin { margin-right:var(--space-3); }
/* in a heading it follows the words, a gap after them */
h1 .sh-spin, h2 .sh-spin, h3 .sh-spin { margin-left:var(--space-4); width:.8em; height:.8em; color:var(--accent-fg, var(--blue-700, currentColor)); }

/* 2 · block: centred in the space it's waiting to fill, with its caption under it, after 400 ms (a fast read never flashes it) */
.sh-loading { display:flex; flex-direction:column; align-items:center; justify-content:center; gap:var(--space-4);
  min-height:120px; padding:var(--space-8) var(--space-6); color:var(--text-secondary); font-size:var(--text-sm); text-align:center;
  animation:sh-arrive .2s ease .4s both; }
.sh-spin-lg { width:28px; height:28px; border-width:3px; color:var(--accent-fg, var(--blue-700, currentColor)); }

/* reduced motion: nothing turns; the ring breathes slowly instead, so "working" still reads */
@media (prefers-reduced-motion:reduce) {
  .sh-spin { animation:sh-breathe 1.6s ease-in-out infinite; border-color:currentColor; }
  .sh-loading { animation:sh-arrive 0s linear .4s both; }   /* no fade, but still the 400 ms: a fast read never flashes it */
}

/* 4 · the live dot: something happening now (a recording). Not a loader: rule 5 keeps pulses away from waits.
   An 8 px dot in the danger colour that breathes every 1.6 s; with reduced motion it holds still and the words say "Recording". */
@keyframes sh-live { 50% { opacity:.3; } }
.sh-live { display:inline-block; flex:none; width:8px; height:8px; border-radius:50%; background:var(--danger-fg, currentColor);
  vertical-align:middle; animation:sh-live 1.6s ease-in-out infinite; }
@media (prefers-reduced-motion:reduce) { .sh-live { animation:none; } }
