/* ============================================================
   AGENT LOADER  .sy-loader[data-state]   (DECISIONS #16, #18, #19)

   One loader for every model: a 3×3 dot grid. The state lives on
   the element itself:
     queued    one faint centre dot
     thinking  dots twinkle at random, slowly
     working   dots rain down the columns
     waiting   dots fold away; a cobalt ? takes the slot (needs you)
     error     the centre dot swells into a brick disc with a white !
     done      a green ✓ for a beat (then the caller shows the model)
   Motion comes from loader.js, which sets each dot's --v (0–1).
   ============================================================ */

.sy-loader {
  --s: 20px;
  --fill: var(--text);
  --d: calc((var(--s) * .68 - var(--s) * .2) / 3);   /* dot size: 3 dots + 2 gaps in 68% of the slot */
  position: relative;
  flex: none;
  display: inline-grid;
  grid-template-columns: repeat(3, var(--d));
  grid-auto-rows: var(--d);
  gap: calc(var(--s) * .1);
  justify-content: center;
  align-content: center;
  width: var(--s);
  height: var(--s);
  vertical-align: middle;
}
.sy-loader i {
  --v: .15;
  border-radius: 50%;
  background: var(--fill);
  opacity: calc(.12 + var(--v) * .88);
  transform: scale(calc(.72 + var(--v) * .28));
  transition: opacity 140ms linear, transform 220ms var(--ease);
}

/* The character that takes the slot when the agent stops */
.sy-loader .gl {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--fill);
  opacity: 0;
  transform: scale(.4);
  transition: opacity 160ms var(--ease), transform 380ms var(--ease-spring);
  pointer-events: none;
}
.sy-loader .gl::before { font-family: var(--font-ui); font-size: calc(var(--s) * .66); font-weight: var(--weight-strong); line-height: 1; }

.sy-loader:is([data-state="waiting"], [data-state="error"], [data-state="done"]) i { opacity: 0; transform: scale(0); }
.sy-loader:is([data-state="waiting"], [data-state="error"], [data-state="done"]) .gl { opacity: 1; transform: none; transition-delay: 120ms; }

/* Needs you: a plain cobalt ? */
.sy-loader[data-state="waiting"] { --fill: var(--highlight); }
.sy-loader[data-state="waiting"] .gl::before { content: "?"; }

/* Error: the centre dot swells into a solid brick disc, a white ! knocked out of it, one shake */
.sy-loader[data-state="error"] { --fill: var(--danger); animation: sy-loader-shake .42s cubic-bezier(.36,.07,.19,.97) both; }
.sy-loader[data-state="error"] i:nth-child(5) { opacity: 1; transform: scale(4.6); transition: opacity 120ms linear, transform 460ms var(--ease-spring) 60ms; }
.sy-loader[data-state="error"] .gl { color: #FFFFFF; transition-delay: 280ms; }
.sy-loader[data-state="error"] .gl::before { content: "!"; font-size: calc(var(--s) * .44); font-weight: 700; }

/* Done: a green check, before the model signs the result */
.sy-loader[data-state="done"] { --fill: var(--success); }
.sy-loader[data-state="done"] .gl::before {
  content: "check";
  font-family: 'Material Symbols Rounded';
  font-size: calc(var(--s) * .74);
  font-weight: normal;
  font-variation-settings: 'FILL' 0, 'wght' 600, 'GRAD' 0, 'opsz' 24;
}

@keyframes sy-loader-shake {
  15%, 85% { transform: translateX(-1px); }
  30%, 70% { transform: translateX(2px); }
  45%, 55% { transform: translateX(-3px); }
  50%      { transform: translateX(3px); }
}
@media (prefers-reduced-motion: reduce) { .sy-loader[data-state="error"] { animation: none; } }
