/* ==========================================
   SPEAKER / ARTIST CARD SKELETON

   Strategy: the skeleton IS a .speaker-card-bem with a --skeleton
   modifier. It gets its dimensions, padding, borders, grid behaviour
   and everything else from the real card CSS — guaranteeing the
   skeleton matches the real card's width + height by construction.
   All this file adds is: grey out the visible content and run a
   shimmer animation on top.
   ========================================== */

.speaker-card-bem--skeleton {
  pointer-events: none;
  user-select: none;
}

/* Image box — light fill in place of the real image */
.speaker-card-bem--skeleton .speaker-card-bem__image-container {
  background: rgba(255, 255, 255, 0.04);
}

/* Skeleton hides its text behind grey fills. Text stays in the DOM so
   the element occupies its natural line-height height, identical to
   the real card. */
.speaker-card-bem--skeleton .speaker-card-bem__name,
.speaker-card-bem--skeleton .speaker-card-bem__talk,
.speaker-card-bem--skeleton .speaker-card-bem__title {
  color: transparent;
  background: rgba(255, 255, 255, 0.06);
  border-radius: var(--radius-sm);
}

.speaker-card-bem--skeleton .speaker-card-bem__name {
  background: rgba(255, 255, 255, 0.09);
}

.speaker-card-bem--skeleton .speaker-card-bem__button {
  color: transparent;
  background: rgba(243, 27, 248, 0.18);
  border-color: transparent;
  cursor: default;
}

/* Hide any overlay gradient on skeletons (they'd look odd over grey fills) */
.speaker-card-bem--skeleton .speaker-card-bem__overlay {
  display: none;
}

/* Shimmer sweep — transform-based, GPU-accelerated */
.speaker-card-bem--skeleton::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    110deg,
    transparent 35%,
    rgba(255, 255, 255, 0.05) 50%,
    transparent 65%
  );
  transform: translateX(-100%);
  animation: speaker-card-skeleton-shimmer 1.8s linear infinite;
  pointer-events: none;
  z-index: 3;
  border-radius: inherit;
}

@keyframes speaker-card-skeleton-shimmer {
  from { transform: translateX(-100%); }
  to { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .speaker-card-bem--skeleton::after {
    animation: none;
    opacity: 0.5;
  }
}

/* ==========================================
   SKELETON → REAL CARDS CROSSFADE
   Stacked grids in a crossfade stage so skeleton fades out
   while real grid fades in, simultaneously.
   ========================================== */

.speakers__crossfade-stage {
  display: grid;
  /* Explicit column so the stack area spans the full container width.
     Without this, the column auto-sizes to child max-content, which makes
     the skeleton grid render narrower than the real grid (the skeleton
     has fewer/empty cards, so its max-content is smaller). */
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "stack";
}

.speakers__crossfade-stage > .speakers__grid {
  grid-area: stack;
  align-self: start;
  transition: opacity 400ms ease;
}

/* Real grid starts invisible while skeletons show, flipped to opacity 1
   once JS has populated it. */
.speakers__grid--pending {
  opacity: 0;
}

/* Skeleton grid fades out on command (set by JS after render). */
.speakers__grid--skeletons {
  opacity: 1;
  pointer-events: none;
}

.speakers__grid--skeletons.speakers__grid--hidden {
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .speakers__crossfade-stage > .speakers__grid {
    transition: none;
  }
}
