/* ==========================================================================
   Components — content plane, hero, letter, HUD, lightbox
   ========================================================================== */

.content {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--s-6);
  padding:
    max(var(--s-16), env(safe-area-inset-top))
    max(var(--s-6), env(safe-area-inset-right))
    max(var(--s-16), env(safe-area-inset-bottom))
    max(var(--s-6), env(safe-area-inset-left));
  transform: translateZ(60px);
  text-align: center;
  /* The content plane spans the whole viewport and sits in front of the photo
     planes in 3D, so it must not swallow their pointer events. Only the parts
     that are genuinely interactive opt back in. */
  pointer-events: none;
}

/* --- Eyebrow: the 20/10 mark ------------------------------------------ */
.eyebrow {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--gold-400);
  font-size: var(--fs-eyebrow);
  font-weight: 600;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  opacity: 0.92;
}

.eyebrow__rule {
  display: block;
  width: clamp(24px, 8vw, 72px);
  height: 1px;
  background: linear-gradient(90deg, transparent, color-mix(in oklab, var(--gold-400) 70%, transparent), transparent);
}

.eyebrow__text {
  font-variant-numeric: tabular-nums;
}

/* --- Hero title ------------------------------------------------------- */
.hero {
  position: relative;
  width: 100%;
  isolation: isolate;
}

.hero__bloom,
.hero__title {
  display: block;
  font-size: var(--fs-display);
  font-weight: 900;
  line-height: var(--lh-display);
  letter-spacing: var(--track-display);
  text-align: center;
  text-wrap: balance;
  background-image: var(--title-gradient);
  background-size: 220% 100%;
  background-repeat: no-repeat;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}

.hero__bloom {
  position: absolute;
  inset: 0;
  z-index: 0;
  filter: blur(38px) saturate(1.25);
  opacity: calc(0.72 * var(--title-glow));
  pointer-events: none;
  user-select: none;
  animation: none;
}

.hero__title {
  position: relative;
  z-index: 1;
  animation: title-sheen 11s ease-in-out infinite alternate;
}

/* --- Letter -----------------------------------------------------------
   A glass keepsake. It scrolls internally so the composition of the
   single-viewport scene never breaks, however long the message is.
   ---------------------------------------------------------------------- */
.letter {
  position: relative;
  width: min(100%, 640px);
  pointer-events: auto;
  border-radius: var(--r-lg);
  background:
    linear-gradient(160deg, var(--glass-bg-hi), var(--glass-bg) 46%, color-mix(in oklab, var(--ink-850) 46%, transparent));
  backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(1.4);
  border: 1px solid var(--glass-line);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.letter::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: linear-gradient(150deg, color-mix(in oklab, var(--rose-300) 45%, transparent), transparent 42%, color-mix(in oklab, var(--violet-400) 30%, transparent));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
}

.letter__scroll {
  position: relative;
  max-height: min(44svh, 360px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--s-6) clamp(var(--s-4), 5vw, var(--s-9)) var(--s-6);
  mask-image: none;
}

/* The fade is shorter than the block padding, so it only ever softens empty
   space and never eats into the first or last line. */
.letter__scroll.is-scrollable {
  mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 calc(100% - 16px), transparent 100%);
}

.letter__scroll.is-scrollable.at-top {
  mask-image: linear-gradient(to bottom, #000 0, #000 calc(100% - 16px), transparent 100%);
}

.letter__scroll.is-scrollable.at-bottom {
  mask-image: linear-gradient(to bottom, transparent 0, #000 16px, #000 100%);
}

.letter__body {
  max-width: var(--measure);
  margin-inline: auto;
  color: var(--text);
  font-size: var(--fs-lead);
  line-height: var(--lh-body);
  font-weight: 400;
  text-wrap: pretty;
}

.letter__body > p + p {
  margin-top: var(--s-4);
}

.letter__sign {
  margin-top: var(--s-4);
  color: var(--rose-300);
  font-size: var(--fs-caption);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-shadow: 0 0 22px color-mix(in oklab, var(--rose-500) 60%, transparent);
}

/* --- Hint ------------------------------------------------------------- */
.hint {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  color: var(--text-faint);
  font-size: var(--fs-caption);
  letter-spacing: 0.04em;
  transition: opacity var(--dur-3) var(--ease-out-quint);
}

.hint svg {
  width: 16px;
  height: 16px;
  opacity: 0.7;
  animation: hint-nudge 3.2s ease-in-out infinite;
}

.hint.is-dismissed {
  opacity: 0;
  pointer-events: none;
}

/* Shown only when config.json could not be read, so a reader who edited the
   file and saw no change learns why instead of assuming the page is broken. */
.notice {
  max-width: min(100%, 34rem);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid color-mix(in oklab, var(--gold-400) 32%, transparent);
  background: color-mix(in oklab, var(--ink-850) 72%, transparent);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  color: var(--text-soft);
  font-size: var(--fs-xs);
  line-height: 1.7;
  letter-spacing: 0.01em;
  text-wrap: pretty;
}

/* Short or narrow viewports: the scene still has to hold one 3D composition,
   so give the text column back the room the generous desktop padding spent. */
@media (max-width: 640px), (max-height: 720px) {
  .content {
    gap: var(--s-4);
    padding-block: max(var(--s-9), env(safe-area-inset-top)) max(var(--s-9), env(safe-area-inset-bottom));
  }

  .letter__scroll {
    max-height: min(38svh, 300px);
  }

  .hint {
    gap: var(--s-2);
    font-size: var(--fs-xs);
    letter-spacing: 0.02em;
  }

  .hint svg {
    width: 14px;
    height: 14px;
  }
}

/* ==========================================================================
   HUD
   ========================================================================== */

.hud {
  position: fixed;
  z-index: 70;
  top: max(var(--s-4), env(safe-area-inset-top));
  right: max(var(--s-4), env(safe-area-inset-right));
  display: flex;
  gap: var(--s-1);
  padding: var(--s-1);
  border-radius: var(--r-pill);
  background: color-mix(in oklab, var(--ink-900) 46%, transparent);
  backdrop-filter: blur(18px) saturate(1.3);
  -webkit-backdrop-filter: blur(18px) saturate(1.3);
  border: 1px solid var(--glass-line);
  box-shadow: 0 18px 40px -28px oklch(0.04 0.05 288 / 0.9);
}

.hud__btn {
  position: relative;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-pill);
  color: var(--text-soft);
  transition: color var(--dur-2) var(--ease-out-quint), background-color var(--dur-2) var(--ease-out-quint), transform var(--dur-2) var(--ease-spring);
}

.hud__btn .hud__icon {
  grid-area: 1 / 1;
  width: 20px;
  height: 20px;
  transition: opacity var(--dur-2) var(--ease-out-quint), transform var(--dur-3) var(--ease-spring);
}

.hud__btn .hud__icon--off {
  opacity: 0;
  transform: scale(0.6) rotate(-25deg);
}

.hud__btn[aria-pressed="false"] .hud__icon--on {
  opacity: 0;
  transform: scale(0.6) rotate(25deg);
}

.hud__btn[aria-pressed="false"] .hud__icon--off {
  opacity: 1;
  transform: scale(1) rotate(0deg);
}

@media (hover: hover) {
  .hud__btn:hover {
    color: var(--text-strong);
    background: color-mix(in oklab, var(--rose-400) 20%, transparent);
    transform: translateY(-1px);
  }
}

.hud__btn:active {
  transform: scale(0.94);
  transition-duration: var(--dur-1);
}

/* Music is playing: the button holds a lit state so the sound is never a
   mystery the reader has to hunt for. */
.hud__btn[aria-pressed="true"] {
  color: var(--text-strong);
}

#btnSound[aria-pressed="true"] {
  color: var(--rose-300);
  background: color-mix(in oklab, var(--rose-500) 24%, transparent);
}

#btnSound[aria-pressed="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  border: 1px solid color-mix(in oklab, var(--rose-300) 65%, transparent);
  animation: sound-pulse 2.6s var(--ease-out-quint) infinite;
  pointer-events: none;
}

/* Waiting on a cold connection, or on a source that has not buffered yet. */
#btnSound[aria-busy="true"] {
  opacity: 0.5;
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   YouTube host. The embed provides the audio, so the player itself is parked
   off-screen. Deliberately not `display: none` or `visibility: hidden`:
   browsers are allowed to suspend media inside those.
   -------------------------------------------------------------------------- */
.yt-host {
  position: fixed;
  top: 0;
  left: -10000px;
  width: 220px;
  height: 220px;
  border: 0;
  pointer-events: none;
  opacity: 0.01;
}

.yt-host iframe {
  border: 0;
  display: block;
}

/* ==========================================================================
   Lightbox
   ========================================================================== */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 90;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  padding: clamp(var(--s-4), 3vw, var(--s-9));
  border: 0;
  background: transparent;
  color: var(--text-strong);
  opacity: 0;
  transition-property: opacity, display, overlay;
  transition-duration: var(--dur-3);
  transition-timing-function: var(--ease-out-expo);
  transition-behavior: allow-discrete;
}

.lightbox[open] {
  display: grid;
  place-items: center;
  grid-template-columns: auto 1fr auto;
  gap: clamp(var(--s-2), 2vw, var(--s-6));
}

.lightbox[open]:not([data-closing]) {
  opacity: 1;

  @starting-style {
    opacity: 0;
  }
}

.lightbox[data-closing] {
  opacity: 0;
}

.lightbox::backdrop {
  background: oklch(0.05 0.035 280 / 0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition-property: background-color, backdrop-filter, display, overlay;
  transition-duration: var(--dur-3);
  transition-timing-function: var(--ease-out-expo);
  transition-behavior: allow-discrete;
}

.lightbox[open]:not([data-closing])::backdrop {
  background: oklch(0.07 0.045 282 / 0.66);
  backdrop-filter: blur(16px) saturate(1.15);
  -webkit-backdrop-filter: blur(16px) saturate(1.15);

  @starting-style {
    background: oklch(0.05 0.035 280 / 0);
    backdrop-filter: blur(0px);
    -webkit-backdrop-filter: blur(0px);
  }
}

.lb__figure {
  display: grid;
  gap: var(--s-4);
  justify-items: center;
  min-width: 0;
}

.lb__frame {
  display: block;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow:
    0 60px 120px -40px oklch(0.03 0.04 288 / 0.95),
    0 0 0 1px var(--glass-line-hi),
    0 0 80px -20px color-mix(in oklab, var(--rose-500) 55%, transparent);
  will-change: transform;
}

.lb__img {
  display: block;
  width: auto;
  height: auto;
  max-width: min(92vw, 1100px);
  max-height: 76svh;
  object-fit: contain;
  background: var(--ink-900);
}

.lb__caption {
  display: flex;
  align-items: baseline;
  gap: var(--s-3);
  max-width: min(92vw, 720px);
  color: var(--text-soft);
  font-size: var(--fs-caption);
  letter-spacing: 0.03em;
  text-align: center;
}

.lb__count {
  color: var(--rose-300);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.lb__close,
.lb__nav {
  display: grid;
  place-items: center;
  width: 46px;
  height: 46px;
  border-radius: var(--r-pill);
  color: var(--text);
  background: color-mix(in oklab, var(--ink-800) 52%, transparent);
  backdrop-filter: blur(16px) saturate(1.3);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  border: 1px solid var(--glass-line);
  transition:
    background-color var(--dur-2) var(--ease-out-quint),
    color var(--dur-2) var(--ease-out-quint),
    transform var(--dur-2) var(--ease-spring);
}

.lb__close svg,
.lb__nav svg {
  width: 20px;
  height: 20px;
}

.lb__close {
  position: absolute;
  top: clamp(var(--s-4), 2.6vw, var(--s-9));
  right: clamp(var(--s-4), 2.6vw, var(--s-9));
}

.lb__nav.is-hidden {
  visibility: hidden;
  pointer-events: none;
}

@media (hover: hover) {
  .lb__close:hover,
  .lb__nav:hover:not(.is-hidden) {
    color: var(--text-strong);
    background: color-mix(in oklab, var(--rose-400) 34%, transparent);
    transform: scale(1.06);
  }
}

.lb__close:active,
.lb__nav:active {
  transform: scale(0.94);
}

@media (max-width: 720px) {
  .lightbox[open] {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto;
    align-content: center;
  }

  .lb__nav {
    position: absolute;
    bottom: max(var(--s-4), env(safe-area-inset-bottom));
  }

  .lb__nav--prev {
    left: calc(50% - 56px);
  }

  .lb__nav--next {
    right: calc(50% - 56px);
  }

  .lb__caption {
    padding-bottom: var(--s-16);
  }
}

/* --- Author credit ---------------------------------------------------
   Fixed at the foot of the page so it never disturbs the measured photo
   bands, and purely decorative: it is not a tap target.
   ---------------------------------------------------------------------- */
.credit {
  position: fixed;
  z-index: 65;
  inset: auto 0 max(var(--s-3), env(safe-area-inset-bottom)) 0;
  display: flex;
  justify-content: center;
  align-items: baseline;
  gap: var(--s-2);
  color: var(--text-faint);
  font-size: var(--fs-xs);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  text-align: center;
  pointer-events: none;
  opacity: 0.7;
}

.credit__name {
  color: var(--text-soft);
  font-weight: 600;
}

/* Author `display` outranks the UA rule for `[hidden]`. */
.credit[hidden] {
  display: none;
}

/* ==========================================================================
   Loader — the gate in front of the scene
   ========================================================================== */

.loader {
  position: fixed;
  inset: 0;
  /* Above the grain (60), the HUD (70) and the lightbox (90): nothing of the
     scene shows through until the reader opens the letter. */
  z-index: 100;
  display: grid;
  place-items: center;
  padding:
    max(var(--s-6), env(safe-area-inset-top))
    max(var(--s-6), env(safe-area-inset-right))
    max(var(--s-6), env(safe-area-inset-bottom))
    max(var(--s-6), env(safe-area-inset-left));
  text-align: center;
  background:
    radial-gradient(120% 90% at 50% 4%, color-mix(in oklab, var(--violet-500) 34%, transparent), transparent 62%),
    radial-gradient(110% 90% at 50% 100%, color-mix(in oklab, var(--rose-500) 28%, transparent), transparent 60%),
    var(--ink-950);
  transition: opacity 640ms var(--ease-out-expo), visibility 640ms;
}

.loader.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

.loader__inner {
  display: grid;
  justify-items: center;
  gap: var(--s-4);
  width: min(100%, 26rem);
}

.loader__mark {
  font-size: clamp(2.5rem, 12vw, 3.75rem);
  line-height: 1;
  color: var(--rose-300);
  filter: drop-shadow(0 0 26px color-mix(in oklab, var(--rose-500) 75%, transparent));
  animation: loader-beat 2.6s var(--ease-out-quint) infinite;
}

.loader__title {
  color: var(--text-strong);
  font-size: var(--fs-lead);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-wrap: balance;
}

.loader__bar {
  position: relative;
  width: 100%;
  height: 6px;
  border-radius: var(--r-pill);
  overflow: hidden;
  background: color-mix(in oklab, var(--ink-800) 72%, transparent);
  border: 1px solid var(--glass-line);
}

.loader__fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--violet-400), var(--rose-400), var(--gold-400));
  box-shadow: 0 0 20px -2px color-mix(in oklab, var(--rose-400) 75%, transparent);
  transition: width 320ms var(--ease-out-quint);
}

.loader__status {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--s-2);
  color: var(--text-soft);
  font-size: var(--fs-caption);
  letter-spacing: 0.02em;
}

.loader__percent {
  color: var(--text-faint);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
}

.loader__start {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  margin-top: var(--s-2);
  padding: var(--s-3) clamp(var(--s-6), 6vw, var(--s-12));
  border-radius: var(--r-pill);
  color: var(--text-strong);
  font-size: var(--fs-body);
  font-weight: 700;
  letter-spacing: 0.03em;
  background: linear-gradient(120deg, color-mix(in oklab, var(--rose-500) 92%, transparent), color-mix(in oklab, var(--violet-500) 92%, transparent));
  border: 1px solid var(--glass-line-hi);
  animation: loader-pulse 2.8s var(--ease-out-quint) infinite;
  transition:
    opacity var(--dur-4) var(--ease-out-expo),
    translate var(--dur-4) var(--ease-out-expo),
    transform var(--dur-2) var(--ease-spring);
}

@starting-style {
  .loader__start:not([hidden]) {
    opacity: 0;
    translate: 0 10px;
  }
}

.loader__start:not([hidden]) {
  opacity: 1;
  translate: 0 0;
}

/* Author `display` outranks the UA rule for `[hidden]`, so the hidden state has
   to be restated here or the button would show before the gate is ready. */
.loader__start[hidden] {
  display: none;
}

.loader__start svg {
  width: 20px;
  height: 20px;
}

.loader__start[disabled] {
  opacity: 0.65;
  pointer-events: none;
}

@media (hover: hover) {
  .loader__start:not([disabled]):hover {
    transform: translateY(-2px) scale(1.02);
  }
}
