/* AiB Platform — feedback: how a page tells the user it is working, what
   it has done and that there is nothing yet. Spinner, skeleton, progress
   bar (determinate and indeterminate), stage steps, toasts and the empty
   state. Honest waiting only: real stage narration (.steps) or an
   indeterminate indicator — never an invented percentage. Every animation
   here stops under reduced motion (base.css); each keeps a readable still. */

/* ---------- spinner ---------- */
/* Decoration beside words that say what is happening — aria-hidden, the
   text (or a role="status" parent) carries the meaning. */
.spinner {
  display: inline-block;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  border: 2px solid var(--hairline-strong);
  border-top-color: var(--ink);
  border-radius: 50%;
  vertical-align: -3px;
  animation: spin var(--dur-spin) linear infinite;
}
.spinner--sm { width: 12px; height: 12px; border-width: 1.5px; vertical-align: -1px; }

/* ---------- skeleton ---------- */
/* The shape of the answer before it lands: ragged lines and an optional
   block, shimmering in the canvas grey. aria-hidden — the region holding
   it says aria-busy="true". */
.skeleton { display: grid; gap: 10px; }
.skeleton-line,
.skeleton-block {
  display: block;
  border-radius: 5px;
  background: linear-gradient(90deg, var(--canvas) 0%, var(--skeleton-highlight) 40%, var(--canvas) 80%);
  background-size: 200% 100%;
  animation: skeleton-shimmer var(--dur-loop) linear infinite;
}
.skeleton-line { height: 10px; }
.skeleton-line:nth-child(3n + 2) { width: 86%; }
.skeleton-line:nth-child(3n) { width: 62%; }
.skeleton-block { height: 120px; border-radius: var(--radius-sm); }
@keyframes skeleton-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ---------- progress bar ---------- */
/* Determinate (staff pages: .progress-fill carries its width inline, a
   real count) and indeterminate (no count known: a segment travels the
   track). Tool templates never set inline styles — they narrate with
   .steps or use --indeterminate. */
.progress { display: flex; align-items: center; gap: var(--space-3); }
.progress-track { flex: 1; height: 4px; border-radius: var(--radius-pill); background: var(--hairline); }
.progress-fill { height: 100%; border-radius: var(--radius-pill); background: var(--ink); }
.progress .pct { font-family: var(--font-mono); font-size: 11px; color: var(--grey); }
.progress--indeterminate .progress-track { overflow: hidden; }
.progress--indeterminate .progress-fill {
  width: 38%;
  animation: progress-travel var(--dur-loop) var(--ease-out-quick) infinite;
}
@keyframes progress-travel {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(270%); }
}
/* still: a parked 38% segment would read as a real fraction — the whole
   track tints instead, saying "working" without a number */
@media (prefers-reduced-motion: reduce) {
  .progress--indeterminate .progress-fill { width: 100%; opacity: 0.3; }
}

/* ---------- steps ---------- */
/* What the work is doing, stage by stage, as it really happens. Done =
   ok check, active = the lit (sodium) dot — it is running — and pending =
   a dashed ring. Mark the active one aria-current="step"; put the state
   in words too (a .visually-hidden "Done:") — the marker is decoration. */
.steps { list-style: none; margin: 0; padding: 0; }
.step {
  position: relative;
  padding: 7px 0 7px var(--space-6);
  color: var(--grey);
  font-size: var(--fs-md);
  line-height: 1.55;
}
.step + .step { border-top: 1px dashed var(--hairline); }
/* the marker centres on the first line: padding-top + half a line - 6px */
.step::before {
  content: '';
  position: absolute;
  left: 0;
  top: calc(1px + 0.775em);
  box-sizing: border-box;
  width: 12px;
  height: 12px;
  border: 1.5px dashed var(--hairline-strong);
  border-radius: 50%;
}
.step.is-done { color: var(--ink); }
.step.is-done::before { border: 1.5px solid var(--ok); background: var(--ok-face); }
.step.is-done::after {
  content: '';
  position: absolute;
  left: 4.5px;
  top: calc(3.5px + 0.775em);
  width: 3px;
  height: 6px;
  border: solid var(--ok);
  border-width: 0 1.5px 1.5px 0;
  transform: rotate(45deg);
}
.step.is-active { color: var(--ink); font-weight: 500; }
.step.is-active::before {
  border: 0;
  background: var(--sodium);
  animation: mark-pulse var(--dur-pulse) ease-in-out infinite;
}

/* ---------- toasts ---------- */
/* One polite live region per page (the runtime's AiBTool.toast fills it):
   bottom-right above everything, bottom full-width on phones, lifted
   clear of the cookie pill. Toasts confirm (copied, exported) or report a
   failure; a result never lives only in a toast. */
.toast-region {
  position: fixed;
  right: var(--pad);
  bottom: clamp(16px, 3vh, 28px);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-2);
  width: min(380px, calc(100vw - 2 * var(--pad)));
  pointer-events: none;
}
.toast {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  max-width: 100%;
  padding: 10px var(--space-4);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--radius-sm);
  background: var(--white);
  box-shadow: var(--shadow-pop);
  color: var(--ink);
  font-size: var(--fs-md);
  line-height: 1.45;
  overflow-wrap: anywhere;
  pointer-events: auto;
  animation: toast-in var(--dur-base) var(--ease-out);
}
.toast::before {
  content: '';
  flex: 0 0 auto;
  width: 7px;
  height: 7px;
  margin-top: 6px;
  border-radius: 50%;
  background: var(--ink);
}
.toast--ok { background: var(--ok-face); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
.toast--ok::before { background: var(--ok); }
.toast--danger { background: var(--danger-face); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.toast--danger::before { background: var(--danger); }
@keyframes toast-in {
  from { opacity: 0; transform: translateY(8px); }
}
@media (max-width: 719px) {
  .toast-region {
    left: 16px;
    right: 16px;
    bottom: calc(64px + env(safe-area-inset-bottom));
    width: auto;
    align-items: stretch;
  }
}

/* ---------- empty state ---------- */
/* Nothing here yet — said plainly, with the one action that fills it. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: 200px;
  padding: var(--space-7) var(--space-6);
  border: 1px dashed var(--hairline-strong);
  border-radius: var(--radius-sm);
  background: var(--white);
  text-align: center;
}
.empty-state-title {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 640;
  font-size: var(--fs-lg);
  letter-spacing: -0.01em;
}
.empty-state-sub { margin: 0; max-width: 46ch; color: var(--grey); font-size: var(--fs-md); }
/* the optional action breathes off the copy above it */
.empty-state-sub + *,
.empty-state-title + :not(.empty-state-sub) { margin-top: var(--space-2); }
@media (max-width: 719px) {
  .empty-state { min-height: 160px; padding: var(--space-6) var(--space-4); }
}
