/* Loading the speakers (Jon, 3 October 2026: keep a loading screen, make it
   better). While the live list loads or refreshes, the grid holds skeleton
   person cards: the same card, grid and sizes as the real ones, so nothing
   jumps when they arrive. A calm light passes over the dotted portrait
   squares; reduced motion keeps them still. One polite status line says
   what is happening. If the list does not come, the static names show with
   a quiet note and a way to try again. speakers.js drives all of it. */

/* Static list of every speaker: what shows without scripting, and the
   fallback when the live list cannot load. */
.speaker-fallback { grid-column: 1 / -1; columns: 3; column-gap: var(--s-6); margin: 0; padding: 0; list-style: none; }
.speaker-fallback li { break-inside: avoid; padding: var(--s-2) 0; border-bottom: 1px solid var(--line); overflow-wrap: anywhere; }
.speaker-fallback strong { font-weight: 600; }
.speak-future { margin-top: 24px; }
@media (max-width: 900px) { .speaker-fallback { columns: 2; } }
@media (max-width: 640px) { .speaker-fallback { columns: 1; } }

/* The status line: empty and out of the way until there is something to say.
   It takes the same slot as the way-in line under the pillar row, so the
   grid does not move when one gives way to the other. */
.speaker-load { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3); margin-top: var(--s-4); min-height: 44px;
  color: var(--ink-2); font-size: var(--t-small); }
.speaker-load:not([data-state]) { min-height: 0; margin-top: 0; }
/* not the site's boxed form status: a plain line, and a quiet coral rule when it is a note */
.speaker-load[role="status"], .speaker-load[role="status"][data-state] { padding: 0; border-radius: 0; background: none; color: var(--ink-2); font: 400 var(--t-small)/1.5 var(--f-body); }
.speaker-load[role="status"][data-state="error"] { padding-left: var(--s-3); box-shadow: inset 2px 0 0 var(--coral); }
.speaker-load__text { margin: 0; }
.speaker-load__dots { display: inline-flex; gap: 5px; }
.speaker-load__dots i { width: 7px; height: 7px; border-radius: 50%; background: var(--teal); }
.speaker-load__dots i:nth-child(2) { background: var(--teal-lt); }
.speaker-load__dots i:nth-child(3) { background: var(--coral); }
.speaker-load:not([data-state="loading"]) .speaker-load__dots { display: none; }
.speaker-load__retry { min-height: 44px; padding: 0 var(--s-1); border: 0; background: none; color: var(--teal); cursor: pointer;
  font: 600 var(--t-small)/1 var(--f-ui); text-decoration: underline; text-underline-offset: 3px; }
.speaker-load__retry:hover { color: var(--ink); }
.speaker-load__retry:focus-visible { outline: 2px solid var(--coral); outline-offset: 2px; }
.speaker-load__retry[hidden] { display: none; }

/* The grid's column always takes the full width, so skeletons (no pictures
   yet) and real cards (with pictures) lay out to the same columns. */
.scr > .shell:has(> .speaker-grid) { width: 100%; }

/* Skeleton person cards. Each borrows a pillar for its tint, in turn, as
   the real grid does. Bars stand in for the name, the role and Read More. */
.speaker-skeleton { pointer-events: none; }
.speaker-grid .card.speaker-skeleton .pic { background-image: radial-gradient(circle, color-mix(in srgb, var(--pc, var(--teal)) 34%, transparent) 1.4px, transparent 1.8px);
  background-size: 16px 16px; background-position: center; }
.speaker-skeleton .pic::after, .speaker-skeleton .bar::after { content: ""; position: absolute; inset: 0;
  background: linear-gradient(100deg, transparent 20%, rgba(255,255,255,.55) 50%, transparent 80%); transform: translateX(-100%); }
/* Each bar sits in a line box the size of the text it stands for, so a
   skeleton is as tall as a typical real card. */
.speaker-skeleton .bar { position: relative; display: block; overflow: hidden; height: 12px; margin-block: 4px; border-radius: 6px; background: var(--tint); }
.speaker-skeleton .bar--name { width: 68%; height: 20px; margin-block: 3px; border-radius: 4px; }
.speaker-skeleton .bar--role { width: 86%; }
.speaker-skeleton .bar--role2 { width: 70%; }
.speaker-skeleton .bar--role3 { width: 48%; }
.speaker-skeleton .bar--pillar { width: 30%; height: 9px; margin-block: 1px; background: color-mix(in srgb, var(--pc, var(--teal)) 30%, var(--tint)); }
.speaker-skeleton .bar--more { width: 36px; height: 36px; border-radius: 50%; align-self: flex-end; margin-top: auto; background: color-mix(in srgb, var(--coral) 28%, var(--tint)); }
@media (prefers-reduced-motion: no-preference) {
  .speaker-skeleton .pic::after, .speaker-skeleton .bar::after { animation: speaker-skeleton-light 1.8s ease-in-out infinite; }
  .speaker-skeleton:nth-child(4n + 2) .pic::after, .speaker-skeleton:nth-child(4n + 2) .bar::after { animation-delay: .12s; }
  .speaker-skeleton:nth-child(4n + 3) .pic::after, .speaker-skeleton:nth-child(4n + 3) .bar::after { animation-delay: .24s; }
  .speaker-skeleton:nth-child(4n + 4) .pic::after, .speaker-skeleton:nth-child(4n + 4) .bar::after { animation-delay: .36s; }
  .speaker-load[data-state="loading"] .speaker-load__dots i { animation: speaker-load-dot 1.4s ease-in-out infinite; }
  .speaker-load[data-state="loading"] .speaker-load__dots i:nth-child(2) { animation-delay: .18s; }
  .speaker-load[data-state="loading"] .speaker-load__dots i:nth-child(3) { animation-delay: .36s; }
}
@keyframes speaker-skeleton-light { to { transform: translateX(100%); } }
@keyframes speaker-load-dot { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }
/* Reduced motion: the skeletons and dots hold still. */
@media (prefers-reduced-motion: reduce) {
  .speaker-skeleton .pic::after, .speaker-skeleton .bar::after { animation: none; display: none; }
  .speaker-load__dots i { animation: none; }
}

/* Find your way in: a pillar brings its people forward; the rest fade back
   but stay readable and in reach. Buttons reuse .speaker-filter (site.css). */
.speaker-toolbar:has(.speaker-way) { flex-direction: column; align-items: stretch; gap: var(--s-4); }
.speaker-way { display: flex; flex-wrap: wrap; justify-content: start; align-items: center; gap: var(--s-2); min-width: 0; }
.speaker-way .speaker-filters { justify-content: start; }
.speaker-filter[data-pillar] { display: inline-flex; align-items: center; gap: 8px; }
.speaker-filter[data-pillar]::before { content: ""; width: 8px; height: 8px; border-radius: 50%; background: var(--pc); flex: 0 0 auto; }
.speaker-shuffle { border-style: dashed; }
.speaker-way-status { display: flex; align-items: center; margin-top: var(--s-4); min-height: 44px; }
/* the pillar row waits, visible but resting, until the cards arrive */
.speaker-filter:disabled { opacity: .5; cursor: default; }
.speaker-filter:disabled:hover { border-color: var(--line); }
.speaker-way-status[hidden] { display: none; }
.speaker-grid .speaker { transition: opacity var(--t-surface) ease, filter var(--t-surface) ease; }
.speaker.is-quiet { opacity: .5; filter: grayscale(1); }
.speaker.is-quiet:hover, .speaker.is-quiet:focus-within { opacity: 1; filter: none; }
/* Deep links (speakers.html#speaker-<slug>) land below the sticky header. */
.speaker[id], .speaker-fallback li[id] { scroll-margin-top: calc(var(--head-h) + 24px); }
.speaker-fallback li.is-target { box-shadow: inset 3px 0 0 var(--coral); padding-left: var(--s-3); }
.speaker-fallback li:focus { outline: 2px solid var(--teal); outline-offset: 2px; }
@media (max-width: 640px) {
  .speaker-way { display: grid; grid-template-columns: minmax(0, 1fr); justify-content: stretch; }
  .speaker-way .speaker-filter[data-pillar] { justify-content: center; }
  .speaker-shuffle { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .speaker-grid .speaker { transition: none; }
}
@media print {
  .speaker-toolbar, .speaker-way-status { display: none; }
  .speaker.is-quiet { opacity: 1; filter: none; }
}
