/*
 * Skeleton → content — placeholders shimmer briefly, then the real content
 * cross-fades in at the same place.
 *
 *   <div class="us-swap" aria-busy="true">
 *     <div class="us-swap-skeleton" aria-hidden="true">
 *       <span class="us-skeleton" style="inline-size: 60%"></span> …
 *     </div>
 *     <div class="us-swap-content">…real content…</div>
 *   </div>
 *   el.setAttribute("aria-busy", "false");   // when data has arrived
 *
 * Skeleton and content share one grid cell, so nothing shifts (no CLS). The
 * shimmer runs three times (≈4 s, under WCAG 2.2.2's 5 s) and then rests.
 * It sweeps in the reading direction (mirrored in RTL). Reduced motion: no
 * shimmer, an instant swap.
 */
.us-skeleton {
  display: block;
  position: relative;
  overflow: hidden;
  min-block-size: 1em;
  border-radius: var(--ui-radius-sm, 0.25rem);
  background: var(--ui-muted, color-mix(in oklch, CanvasText 8%, Canvas));
}

.us-skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    90deg,
    transparent,
    color-mix(in oklch, var(--ui-background, Canvas) 70%, transparent),
    transparent
  );
  animation: us-shimmer 1.4s linear 3;
}

.us-skeleton:dir(rtl)::after {
  animation-direction: reverse;
}

@keyframes us-shimmer {
  to {
    transform: translateX(100%);
  }
}

.us-swap {
  display: grid;
}

.us-swap > .us-swap-skeleton,
.us-swap > .us-swap-content {
  grid-area: 1 / 1;
  transition:
    opacity var(--ui-duration-slow, 300ms) var(--ui-ease-out, cubic-bezier(0, 0, 0.2, 1)),
    visibility var(--ui-duration-slow, 300ms) allow-discrete;
}

.us-swap[aria-busy="true"] > .us-swap-content,
.us-swap:not([aria-busy="true"]) > .us-swap-skeleton {
  opacity: 0;
  visibility: hidden;
}

@media (prefers-reduced-motion: reduce) {
  .us-skeleton::after {
    animation: none;
    display: none;
  }
  .us-swap > .us-swap-skeleton,
  .us-swap > .us-swap-content {
    transition: none;
  }
}
