/* ---------------------------------------------------------------------------
   Skeleton shimmer — what the page shows while books.js is still awaiting.

   books.js reads the shelf out of Supabase and decodes every photo cover before
   it lets shelf.js / reader.js run, and until then nothing on the page is drawn:
   no background canvas, no slabs, no pages. This file is plain CSS in /public,
   linked straight from the HTML, precisely so it does not wait for that module
   graph — in dev Vite injects the imported stylesheets from JS, i.e. after the
   await, which is too late to cover it.

   `html.is-loading` is set in the markup and taken off by skeleton.js once the
   page has something real to show. The shapes are sized off the same camera
   bands as layout.js (bookVh / pageVh at ZOOM 1.5), so the placeholder slab is
   the size the real one lands at and nothing jumps when it swaps.
   --------------------------------------------------------------------------- */

:root {
  /* bookVh() per camera band: aspect < 1, < 1.4, and wider */
  --skel-book: 61.9vh;
  --skel-page: 73.1vh;   /* × PAGE_SCALE 1.18 */
  --skel-ink: rgba(43, 29, 38, 0.1);
  --skel-card: rgba(255, 255, 255, 0.5);
  --skel-shine: rgba(255, 255, 255, 0.75);
}
@media (max-aspect-ratio: 1399/1000) { :root { --skel-book: 48.6vh; --skel-page: 57.4vh; } }
@media (max-aspect-ratio: 999/1000)  { :root { --skel-book: 36.8vh; --skel-page: 43.4vh; } }

/* The background canvas is not up yet either; a still wash in its place beats
   a white page. */
html.is-loading {
  background: linear-gradient(160deg, #f7ece4 0%, #f1e2e6 55%, #e9e3ef 100%);
}

/* --- the shimmer itself --------------------------------------------------- */

.skel-block {
  position: relative;
  overflow: hidden;
  background: var(--skel-card);
  border-radius: 4px;
}
.skel-bar {
  position: relative;
  display: inline-block;
  overflow: hidden;
  height: 0.8em;
  max-width: 100%;
  vertical-align: middle;
  border-radius: 999px;
  background: var(--skel-ink);
}
/* One highlight band sweeping left to right. A transform, not a moving
   background-position, so it stays on the compositor. */
.skel-block::after,
.skel-bar::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(
    100deg,
    transparent 20%,
    var(--skel-shine) 50%,
    transparent 80%
  );
  animation: skel-shimmer 1.5s cubic-bezier(0.4, 0, 0.2, 1) infinite;
}
@keyframes skel-shimmer {
  to { transform: translateX(100%); }
}

/* --- the overlay that holds the placeholder shapes ----------------------- */

.skel {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  transition: opacity 420ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
html:not(.is-loading) .skel { opacity: 0; }

/* A row of three squares centred on the viewport — the slab row on the shelf,
   the page rail in the reader. Neighbours sit where the real ones do. */
.skel__row {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  transform: translate(-50%, -50%);
}
.skel__row > .skel-block { flex: 0 0 auto; }
.skel__row > .skel-block:not(:nth-child(2)) { opacity: 0.55; }

.skel--shelf .skel__row { gap: calc(var(--skel-book) * 0.314); }   /* SPACING / SIZE - 1 */
.skel--shelf .skel-block { width: var(--skel-book); height: var(--skel-book); }

.skel--reader .skel__row { gap: 8vw; }

/* Both placeholders are in index.html; the address decides which one shows
   (the inline script there sets data-route before anything paints). The
   playlist has no placeholder of its own — its loader is part of the view. */
html:not([data-route='shelf']) .skel--shelf,
html:not([data-route='book']) .skel--reader { display: none; }
.skel--reader .skel-block { width: var(--skel-page); height: var(--skel-page); }

/* The shelf's caption, in the strip under the slab (see shelf.css). */
.skel__caption {
  position: absolute;
  left: 50%;
  top: calc(50% + var(--skel-book) / 2);
  bottom: 0;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
}
.skel__caption .skel-bar:nth-child(1) { width: 56px; height: 9px; }
.skel__caption .skel-bar:nth-child(2) { width: min(260px, 60vw); height: clamp(20px, 3.2vh, 30px); }
.skel__caption .skel-bar:nth-child(3) { width: min(180px, 45vw); height: 11px; }

/* While loading, the real caption is only placeholder copy ("01 / 03" and an
   empty title) — keep it out of sight, and hold its fade-in back so it plays
   when the class comes off rather than into nothing. */
html.is-loading #shelf {
  animation: none;
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .skel-block::after,
  .skel-bar::after { animation: none; }
  .skel { transition: none; }
}
