/* ──────────────────────────────────────────────────────────────────────────
   states.css — visual layer for the "four vital states" primitive (states.js)
   ----------------------------------------------------------------------------
   The dashboard had ad-hoc skeleton/empty/error markup; every OTHER view
   (editor, field, team, messaging) had none, and errors surfaced as raw red
   inline strings with no recovery. These classes give every page one shared,
   theme-agnostic vocabulary for:

     .ui-skeleton  — shimmering placeholder that RESERVES height (no layout jump)
     .ui-empty     — friendly, actionable zero-results layout
     .ui-error     — localized error card with a Retry affordance

   All sizing/spacing uses the tokens.css scale so it renders correctly under the
   classic, ui-next and theme-calm skins without per-theme overrides. Colours read
   from existing theme vars with literal fallbacks (so it also works on pages that
   don't define the full palette, e.g. login/share).
   ────────────────────────────────────────────────────────────────────────── */

/* ── Skeleton ────────────────────────────────────────────────────────────── */
.ui-skeleton {
  --_sk-base: var(--bg-card, #1e2535);
  background: linear-gradient(
    90deg,
    var(--_sk-base) 0%,
    rgba(255, 255, 255, 0.06) 50%,
    var(--_sk-base) 100%
  );
  background-size: 200% 100%;
  animation: ui-shimmer 1.5s ease-in-out infinite;
  border-radius: var(--radius-sm, 6px);
}
.ui-skeleton--text  { height: var(--text-md, 14px); border-radius: var(--sp-1); }
.ui-skeleton--line  { height: var(--sp-3); margin-bottom: var(--sp-2); }
.ui-skeleton--block { height: var(--sp-12); border-radius: var(--radius-md, 8px); margin-bottom: var(--sp-2); }
.ui-skeleton--avatar{ width: var(--sp-10); height: var(--sp-10); border-radius: 50%; }

.ui-skeleton-stack { display: flex; flex-direction: column; }

@keyframes ui-shimmer {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
/* prefers-reduced-motion: tokens.css already neutralises the animation; we drop
   to a calm static tint so the placeholder is still clearly "loading". */
@media (prefers-reduced-motion: reduce) {
  .ui-skeleton { background: var(--bg-card, #1e2535); opacity: 0.7; }
}

/* ── Empty state ─────────────────────────────────────────────────────────── */
.ui-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-5);
  color: var(--text-secondary, #8a95aa);
  /* Gentle entry so the empty state doesn't snap in after the skeleton. */
  animation: ui-state-in var(--dur-3, 240ms) var(--ease-out, ease) both;
}
.ui-empty__icon {
  font-size: var(--sp-10);
  line-height: 1;
  color: var(--text-muted, #5a647a);
  opacity: 0.5;
  margin-bottom: var(--sp-1);
}
.ui-empty__title {
  font-size: var(--text-lg, 15px);
  font-weight: var(--weight-semibold, 600);
  color: var(--text-primary, #e8ecf4);
}
.ui-empty__msg {
  font-size: var(--text-base, 13px);
  line-height: var(--leading-normal, 1.55);
  max-width: 42ch;            /* defensive: never let copy run edge-to-edge */
  overflow-wrap: break-word;
}
.ui-empty__action { margin-top: var(--sp-2); }

/* ── Error card ──────────────────────────────────────────────────────────── */
.ui-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--sp-2);
  padding: var(--sp-6) var(--sp-5);
  margin: var(--sp-2) 0;
  border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
  border-radius: var(--radius-md, 8px);
  background: color-mix(in srgb, #ef4444 7%, transparent);
  color: var(--text-secondary, #8a95aa);
  animation: ui-state-in var(--dur-3, 240ms) var(--ease-out, ease) both;
}
.ui-error__icon { font-size: var(--sp-7); color: #f87171; line-height: 1; }
.ui-error__title {
  font-size: var(--text-md, 14px);
  font-weight: var(--weight-semibold, 600);
  color: var(--text-primary, #e8ecf4);
}
.ui-error__msg {
  font-size: var(--text-sm, 12px);
  line-height: var(--leading-normal, 1.55);
  max-width: 46ch;
  overflow-wrap: anywhere;    /* error strings can contain long unbroken ids/urls */
}
.ui-error__actions { display: flex; gap: var(--sp-2); margin-top: var(--sp-1); flex-wrap: wrap; justify-content: center; }

/* Shared minimal button used by empty/error states so the primitive is
   self-contained on pages that lack the app's .btn system. Inherits the accent
   token, so it matches whatever theme is active. */
.ui-state-btn {
  display: inline-flex; align-items: center; gap: var(--sp-1_5);
  padding: var(--sp-2) var(--sp-3_5);
  font-size: var(--text-sm, 12px); font-weight: var(--weight-semibold, 600);
  font-family: inherit; color: #fff; cursor: pointer;
  background: var(--accent, #3b82f6);
  border: 1px solid transparent; border-radius: var(--radius-sm, 6px);
  transition: background var(--dur-2, 150ms) var(--ease-standard, ease),
              transform var(--dur-1, 80ms) ease;
}
.ui-state-btn:hover  { background: var(--accent-hover, #2563eb); }
.ui-state-btn:active { transform: translateY(1px); }
.ui-state-btn--ghost {
  background: transparent; color: var(--text-secondary, #8a95aa);
  border-color: var(--border-strong, rgba(255, 255, 255, 0.12));
}
.ui-state-btn--ghost:hover { background: var(--surface-hover, rgba(255,255,255,0.05)); color: var(--text-primary, #e8ecf4); }

@keyframes ui-state-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* ── Staggered list/grid entry (driven by stagger.js) ────────────────────────
   stagger.js sets --stagger-i (a capped child index) on each item and adds
   .stagger-item. Items fade+rise in sequence so lists/grids mount fluidly
   instead of snapping. The delay is capped in JS so a long list never makes the
   user wait. tokens.css neutralises the *duration* under reduced-motion; we ALSO
   zero the *delay* here, otherwise motion-sensitive users would still watch a
   slow sequential reveal (each item invisible until its delay elapsed). */
.stagger-item {
  animation: ui-state-in var(--dur-3, 240ms) var(--ease-out, ease) both;
  animation-delay: calc(var(--stagger-i, 0) * 35ms);
  will-change: transform, opacity;
}
@media (prefers-reduced-motion: reduce) {
  .stagger-item { animation-delay: 0ms !important; }
}
