/* ---------------------------------------------------------------------------
   Bloomcast — production stylesheet.
   Color tokens + typography scale + layout + glass-morphism for the panel.
   --------------------------------------------------------------------------- */

:root {
  /* color tokens — derived from the canvas palette so UI harmonizes */
  --bg-deep:        #1a1130;
  --bg-deeper:      #0c081e;
  --accent-1:       #c4b5fd;       /* lavender */
  --accent-2:       #f9c5d1;       /* baby pink */
  --accent-3:       #b6e5d8;       /* mint */
  --text-bright:    rgba(232, 228, 255, 0.92);
  --text-soft:      rgba(232, 228, 255, 0.62);
  --text-mute:      rgba(232, 228, 255, 0.36);
  --text-faint:     rgba(232, 228, 255, 0.22);
  --rule:           rgba(196, 181, 253, 0.18);

  /* typography */
  --ff-display: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;
  --ff-serif:   'Cormorant Garamond', 'Tiempos Text', Georgia, serif;
  --ff-mono:    'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;

  /* layout */
  --pad-edge:   24px;
  --pad-edge-sm: 14px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: var(--bg-deep);
  color: var(--text-bright);
  overflow: hidden;
  font-family: var(--ff-display);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  cursor: crosshair;
}

#bloomcast {
  position: fixed;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  /* Disable native gestures (browser pinch-zoom, double-tap zoom, scroll) so
     our touch handlers in interactions.js can own pinch + drag + tap. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}

/* ============================================================================
   CINEMATIC INTRO TITLE CARD
   Sequence (10s total): 0.0s page loads with title visible over solid void;
   0-0.45s title fades up from y=8px; 0.45-1.05s title held; 1.05-1.5s title
   fades out; 1.5-2.0s the entire title-card background fades to transparent
   revealing the distant pinprick of light (camera at 200m). Then JS takes
   over for the camera approach, heartbeat shockwave, and hints reveal.
   pointer-events:none always so the card never blocks interaction once it
   becomes transparent. animation-fill-mode:forwards holds the end state. */
#intro-titlecard {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  /* Solid deep-void background — same as scene clear color so the canvas
     "appears" continuously, not as a swap. */
  background: #1a1130;
  z-index: 9999;
  pointer-events: none;
  animation: introBackdropFade 2s ease-out forwards;
}
.intro-title {
  margin: 0;
  font-family: var(--ff-display);
  font-weight: 600;
  font-size: clamp(28px, 6vw, 56px);
  letter-spacing: 0.42em;
  color: rgba(252, 231, 243, 0.95);
  /* Subtle radiant glow so the title doesn't read as flat text. */
  text-shadow:
    0 0 16px rgba(196, 181, 253, 0.40),
    0 0 38px rgba(249, 197, 209, 0.22);
  opacity: 0;
  transform: translateY(8px);
  animation: introTextSequence 1.5s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
.intro-subtitle {
  margin: 0;
  font-family: var(--ff-mono);
  font-size: clamp(10px, 1.2vw, 12px);
  letter-spacing: 0.32em;
  color: rgba(196, 181, 253, 0.65);
  opacity: 0;
  transform: translateY(6px);
  /* Slightly delayed so seed appears just after the title. */
  animation: introTextSequence 1.5s cubic-bezier(0.2, 0.8, 0.2, 1) 120ms forwards;
}
.intro-seed-prefix { opacity: 0.7; }

@keyframes introBackdropFade {
  /* Holds solid void for the first 75% (1.5s of 2.0s), then fades to
     transparent over the final 0.5s revealing the pinprick. */
  0%, 75% { opacity: 1; }
  100%    { opacity: 0; visibility: hidden; }
}
@keyframes introTextSequence {
  0%   { opacity: 0; transform: translateY(8px); }
  30%  { opacity: 1; transform: translateY(0); }
  68%  { opacity: 1; transform: translateY(0); }
  100% { opacity: 0; transform: translateY(-4px); }
}

/* Ignition flash — full-screen white burst at the moment the halo spawns.
   JS toggles .active to fire the keyframe. Peaks at 85% white opacity
   ~80ms in, then decays over the rest of the 0.65s animation, becoming
   visibility:hidden so it never blocks input afterward. */
#intro-flash {
  position: fixed;
  inset: 0;
  background: #ffffff;
  opacity: 0;
  z-index: 9998;
  pointer-events: none;
  visibility: hidden;
}
#intro-flash.active {
  visibility: visible;
  animation: introFlashBurst 0.85s cubic-bezier(0.1, 0.7, 0.3, 1) forwards;
}
@keyframes introFlashBurst {
  0%   { opacity: 0;    background: #ffffff; }
   8%  { opacity: 1.00; background: #ffffff; } /* full white retina hit */
  18%  { opacity: 0.92; background: #fff0f9; } /* pink shift */
  40%  { opacity: 0.50; background: #ddc8ff; } /* lavender afterglow */
  100% { opacity: 0;    visibility: hidden; }
}

/* Slingshot mini-flash — softer lavender pulse at closest pass. */
#intro-flash-secondary {
  position: fixed;
  inset: 0;
  background: rgba(196, 181, 253, 0.55);
  opacity: 0;
  z-index: 9997;
  pointer-events: none;
  visibility: hidden;
}
#intro-flash-secondary.active {
  visibility: visible;
  animation: introSlingshotPulse 0.55s ease-out forwards;
}
@keyframes introSlingshotPulse {
  0%   { opacity: 0;    background: rgba(196, 181, 253, 0.55); }
  20%  { opacity: 0.55; background: rgba(196, 181, 253, 0.55); }
  60%  { opacity: 0.25; background: rgba(249, 197, 209, 0.45); }
  100% { opacity: 0;    visibility: hidden; }
}

/* On reduced-motion devices, skip the intro animation entirely. */
@media (prefers-reduced-motion: reduce) {
  #intro-titlecard {
    animation: none;
    opacity: 0;
    visibility: hidden;
  }
  .intro-title, .intro-subtitle { animation: none; opacity: 0; }
  #intro-flash, #intro-flash.active,
  #intro-flash-secondary, #intro-flash-secondary.active {
    animation: none; opacity: 0; visibility: hidden;
  }
}

/* --- BRAND (top-left) ------------------------------------------------------ */

#brand {
  position: fixed;
  top: max(var(--pad-edge), env(safe-area-inset-top));
  left: max(var(--pad-edge), env(safe-area-inset-left));
  pointer-events: none;
  user-select: none;
  z-index: 10;
}

.brand__title {
  font-family: var(--ff-display);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.32em;
  margin: 0;
  color: var(--text-bright);
  text-transform: uppercase;
  mix-blend-mode: screen;
  display: inline-flex;
}

/* Letter-by-letter staggered reveal — JS in main.js wraps each character
   in a span and sets --i to its index. */
.brand__title span {
  display: inline-block;
  opacity: 0;
  transform: translateY(10px);
  filter: blur(4px);
  animation: brand-letter 720ms cubic-bezier(0.2, 0.8, 0.2, 1) calc(var(--i) * 80ms + 200ms) forwards;
}

@keyframes brand-letter {
  to {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
}

.brand__tagline {
  opacity: 0;
  animation: tagline-fade 1.2s cubic-bezier(0.2, 0.8, 0.2, 1) 1.4s forwards;
}

@keyframes tagline-fade {
  to { opacity: 1; }
}

.brand__title,
.brand__tagline {
  /* override animation to immediate-on if user prefers reduced motion */
}

.brand__tagline {
  font-family: var(--ff-serif);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.01em;
  color: var(--text-soft);
  margin: 6px 0 0;
  max-width: 22ch;
  line-height: 1.45;
  mix-blend-mode: screen;
  font-style: italic;
}

.brand__tagline em {
  color: var(--accent-1);
  font-style: normal;
  letter-spacing: 0.04em;
}

/* --- LINKS (top-right) ----------------------------------------------------- */

#links {
  position: fixed;
  top: max(var(--pad-edge), env(safe-area-inset-top));
  right: max(var(--pad-edge), env(safe-area-inset-right));
  display: flex;
  gap: 10px;
  align-items: center;
  /* Above the chapter (90) and about (100) panels so the nav stays visible
     and interactive even with a panel open — user can switch panels or
     toggle audio/react without first closing what's in front of them. */
  z-index: 200;
}

/* Glass pill — unified treatment for every top-nav button. Subtle dark glass
   with a top-edge inner highlight + soft shadow, so the chips read like
   frosted glass floating over the scene. Hover lifts + brightens. */
.link {
  position: relative;
  font-family: var(--ff-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(232, 228, 255, 0.88);
  text-decoration: none;
  background:
    linear-gradient(180deg, rgba(48, 32, 84, 0.42), rgba(20, 14, 36, 0.62));
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid rgba(196, 181, 253, 0.22);
  border-radius: 999px;
  padding: 8px 14px;
  cursor: pointer;
  transition:
    color 220ms ease,
    background 220ms ease,
    border-color 220ms ease,
    box-shadow 260ms ease,
    transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18),
    0 6px 18px -10px rgba(0, 0, 0, 0.7);
}

.link:hover,
.link:focus-visible {
  color: #fff;
  background:
    linear-gradient(180deg, rgba(64, 44, 112, 0.55), rgba(28, 20, 50, 0.72));
  border-color: rgba(249, 197, 209, 0.50);
  outline: none;
  transform: translateY(-1px);
  text-shadow: 0 0 8px rgba(196, 181, 253, 0.45);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    inset 0 -1px 0 rgba(0, 0, 0, 0.18),
    0 10px 26px -10px rgba(196, 181, 253, 0.55);
}

.link--icon {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Credits-pill text swap defaults: show full text on desktop. */
.credit-full { display: inline; }
.credit-short { display: none; }

.audio-icon {
  font-size: 12px;
  color: var(--text-mute);
  transition: color 220ms ease;
}

#audio-toggle[aria-pressed="true"] .audio-icon { color: var(--accent-1); }
.audio-state[data-state="on"] { color: var(--accent-1); }

/* Hue shuffle button — one-tap randomize the global color-wheel rotation. */
.hue-icon {
  font-size: 13px;
  display: inline-block;
  transition: transform 360ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
#hue-shuffle:active .hue-icon { transform: rotate(180deg); }

/* Fullscreen toggle — glass pill matching the others. The Unicode glyph
   renders smaller than the emoji icons, so bump its font-size + opacity. */
.fullscreen-icon {
  font-size: 14px;
  font-weight: 700;
  color: rgba(232, 228, 255, 0.85);
  transition: color 220ms ease;
  display: inline-block;
}
#fullscreen-toggle[aria-pressed="true"] .fullscreen-icon {
  color: var(--accent-2);
  text-shadow: 0 0 10px rgba(249, 197, 209, 0.55);
}
.fullscreen-label { transition: color 220ms ease; }

/* React button — listens to ambient music via microphone, drives shockwaves
   on bass kicks. When active, the icon pulses to match the beat. */
.react-icon {
  font-size: 13px;
  color: var(--text-mute);
  transition: color 220ms ease, transform 120ms ease, text-shadow 200ms ease;
  display: inline-block;
}
#react-toggle[aria-pressed="true"] .react-icon {
  color: var(--accent-2);
  text-shadow: 0 0 10px rgba(249, 197, 209, 0.65);
}
.react-state { transition: color 220ms ease; }
.react-state[data-state="on"] { color: var(--accent-2); }
.react-state[data-state="denied"] { color: rgba(255, 145, 145, 0.85); }
/* Beat pulse — main.js bumps a CSS var on each kick; the icon scales briefly. */
#react-toggle[aria-pressed="true"] .react-icon {
  transform: scale(calc(1 + var(--beat, 0) * 0.45));
}

/* --- HINTS (bottom-left) --------------------------------------------------- */

.hints {
  position: fixed;
  bottom: calc(var(--pad-edge) + 4px);
  left: var(--pad-edge);
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
  align-items: center;
  pointer-events: none;
  user-select: none;
  z-index: 10;
  transition: opacity 1.4s ease;
}

.hints[data-state="hidden"] { opacity: 0; }

.hint {
  font-family: var(--ff-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-mute);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  mix-blend-mode: screen;
}

.hint kbd {
  font-family: inherit;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  color: var(--accent-1);
  border: 1px solid var(--rule);
  padding: 2px 6px;
  border-radius: 3px;
  background: rgba(196, 181, 253, 0.04);
  position: relative;
}

/* First-interaction pulse — draws the eye to "click" until the user has
   actually clicked once. Removed via JS once the first click fires. */
.hint--pulse kbd {
  animation: hintPulse 2.6s ease-in-out infinite;
}

@keyframes hintPulse {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(196, 181, 253, 0.55);
    background: rgba(196, 181, 253, 0.04);
  }
  50% {
    box-shadow: 0 0 0 8px rgba(196, 181, 253, 0);
    background: rgba(196, 181, 253, 0.18);
  }
}

.hints[data-state="interacted"] .hint--pulse kbd { animation: none; }

/* Hint pills stagger in on load. */
.hint {
  opacity: 0;
  transform: translateY(6px);
  animation: hintIn 700ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
/* Delayed so hints appear AT the moment the intro camera arrives + the
   heartbeat shockwave fires (~8.4s after the dive was slowed for smoother
   per-frame motion). Staggered 180ms each so they cascade in rather than
   pop together. The user just watched a cinematic intro, so the
   affordances arrive as the "now it's yours" handshake. */
.hint:nth-child(1) { animation-delay: 8500ms; }
.hint:nth-child(2) { animation-delay: 8680ms; }
.hint:nth-child(3) { animation-delay: 8860ms; }

@keyframes hintIn {
  to { opacity: 1; transform: translateY(0); }
}

/* --- SIGNATURE (bottom-right) --------------------------------------------- */

#signature {
  position: fixed;
  bottom: var(--pad-edge);
  right: var(--pad-edge);
  font-family: var(--ff-mono);
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.18em;
  color: var(--text-mute);
  user-select: none;
  pointer-events: none;
  text-transform: lowercase;
  mix-blend-mode: screen;
  z-index: 10;
}

/* --- TOUR (left-side chapter nav) ----------------------------------------- */

#tour {
  position: fixed;
  left: var(--pad-edge);
  top: 50%;
  transform: translateY(-50%);
  z-index: 12;
  pointer-events: auto;
}

.tour__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tour__btn {
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  background: rgba(20, 14, 36, 0.55);
  backdrop-filter: blur(10px) saturate(140%);
  -webkit-backdrop-filter: blur(10px) saturate(140%);
  border: 1px solid rgba(196, 181, 253, 0.22);
  border-radius: 10px;
  padding: 12px 18px 12px 16px;
  cursor: pointer;
  font-family: var(--ff-mono);
  color: rgba(244, 240, 255, 0.92);
  letter-spacing: 0.06em;
  transition:
    color 220ms ease,
    background 220ms ease,
    border-color 220ms ease,
    transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1),
    box-shadow 320ms ease;
  text-align: left;
  box-shadow: 0 8px 22px -16px rgba(0, 0, 0, 0.7);
}

/* Accent dot — left-edge indicator that fills with color on hover/active. */
.tour__btn::before {
  content: '';
  position: absolute;
  left: -2px;
  top: 50%;
  width: 4px;
  height: 18px;
  border-radius: 2px;
  background: linear-gradient(180deg, var(--accent-1), var(--accent-2));
  transform: translateY(-50%) scaleY(0.25);
  transform-origin: center;
  opacity: 0.4;
  transition: transform 320ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 320ms ease;
}

.tour__btn:hover,
.tour__btn:focus-visible,
.tour__btn[data-active="true"] {
  color: #fff;
  background: rgba(40, 28, 70, 0.72);
  border-color: rgba(249, 197, 209, 0.55);
  outline: none;
  transform: translateX(6px);
  box-shadow: 0 14px 30px -18px rgba(196, 181, 253, 0.55);
}

.tour__btn:hover::before,
.tour__btn:focus-visible::before,
.tour__btn[data-active="true"]::before {
  transform: translateY(-50%) scaleY(1);
  opacity: 1;
}

.tour__num {
  font-size: 11px;
  font-weight: 600;
  color: var(--accent-1);
  letter-spacing: 0.22em;
  font-feature-settings: 'tnum' 1;
  opacity: 0.85;
  transition: color 220ms ease, opacity 220ms ease;
}

.tour__btn:hover .tour__num,
.tour__btn[data-active="true"] .tour__num {
  color: var(--accent-2);
  opacity: 1;
}

.tour__name {
  font-size: 14px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  font-family: var(--ff-display);
  color: inherit;
}

/* Tour staggered reveal on load */
.tour__list li {
  opacity: 0;
  transform: translateX(-8px);
  animation: tourIn 700ms cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
}
.tour__list li:nth-child(1) { animation-delay: 2400ms; }
.tour__list li:nth-child(2) { animation-delay: 2520ms; }
.tour__list li:nth-child(3) { animation-delay: 2640ms; }
.tour__list li:nth-child(4) { animation-delay: 2760ms; }

@keyframes tourIn {
  to { opacity: 1; transform: translateX(0); }
}

/* --- CHAPTER panel (slide-in from right) --------------------------------- */

.chapter {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(440px, 100vw);
  background: rgba(20, 14, 36, 0.78);
  backdrop-filter: blur(16px) saturate(140%);
  -webkit-backdrop-filter: blur(16px) saturate(140%);
  border-left: 1px solid var(--rule);
  z-index: 90;
  overflow-y: auto;
  transform: translateX(0);
  transition: transform 420ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 280ms ease;
  cursor: default;
}

.chapter[hidden] {
  display: block !important;
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
}

.chapter__close {
  position: absolute;
  /* Sits below the top-right nav so the two don't collide visually. */
  top: 70px;
  right: 16px;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(180deg, rgba(48, 32, 84, 0.42), rgba(20, 14, 36, 0.62));
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid rgba(196, 181, 253, 0.22);
  border-radius: 50%;
  color: rgba(232, 228, 255, 0.88);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 6px 18px -10px rgba(0, 0, 0, 0.7);
  transition: color 200ms ease;
  z-index: 2;
}

.chapter__close {
  transition:
    color 220ms ease,
    background 220ms ease,
    border-color 220ms ease,
    box-shadow 260ms ease,
    transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.chapter__close:hover,
.chapter__close:focus-visible {
  color: #fff;
  background:
    linear-gradient(180deg, rgba(64, 44, 112, 0.55), rgba(28, 20, 50, 0.72));
  border-color: rgba(249, 197, 209, 0.50);
  outline: none;
  transform: translateY(-1px) rotate(90deg);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 10px 26px -10px rgba(196, 181, 253, 0.55);
}

.chapter__inner {
  /* Extra top padding (~120px) so the chapter title clears the close-X
     button which now sits below the top-right nav at y≈70-104. */
  padding: 124px calc(var(--pad-edge) + 4px) calc(var(--pad-edge) + 16px);
  position: relative;
}

.chapter__article {
  display: none;
}

.chapter__article[data-active="true"] {
  display: block;
  animation: chapterFadeIn 480ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

@keyframes chapterFadeIn {
  from { opacity: 0; transform: translateX(8px); }
  to   { opacity: 1; transform: translateX(0); }
}

.chapter__header {
  margin-bottom: 22px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--rule);
}

.chapter__num {
  display: block;
  font-family: var(--ff-mono);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.28em;
  color: var(--accent-2);
  text-transform: uppercase;
  margin-bottom: 8px;
  font-feature-settings: 'tnum' 1;
}

.chapter__title {
  font-family: var(--ff-display);
  font-size: 28px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--text-bright);
  margin: 0;
  line-height: 1.05;
}

.chapter__lede {
  font-family: var(--ff-serif);
  font-size: 18px;
  line-height: 1.45;
  font-style: italic;
  color: var(--accent-1);
  margin: 0 0 22px;
}

.chapter__body {
  font-family: var(--ff-serif);
  font-size: 15px;
  font-weight: 400;
  line-height: 1.62;
  color: var(--text-bright);
  margin: 0 0 16px;
}

.chapter__body em {
  font-style: italic;
  color: var(--accent-2);
}

.chapter__body code {
  font-family: var(--ff-mono);
  font-size: 12px;
  letter-spacing: 0.02em;
  color: var(--accent-1);
  background: rgba(196, 181, 253, 0.06);
  padding: 1px 6px;
  border-radius: 3px;
  border: 1px solid var(--rule);
}

.chapter__specs {
  margin: 28px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 8px 24px;
  font-family: var(--ff-mono);
  font-size: 11px;
  letter-spacing: 0.06em;
}

.chapter__specs dt {
  color: var(--text-mute);
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-size: 9px;
  align-self: center;
}

.chapter__specs dd {
  color: var(--accent-1);
  margin: 0;
  align-self: center;
  font-feature-settings: 'tnum' 1;
}

/* --- ABOUT panel (slide-in from right) ------------------------------------ */

.about {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(420px, 100vw);
  background: rgba(10, 14, 28, 0.78);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-left: 1px solid var(--rule);
  /* Extra top padding so the heading clears the close-X (now at y≈70-104). */
  padding: 124px calc(var(--pad-edge) + 4px) var(--pad-edge);
  z-index: 100;
  overflow-y: auto;
  transform: translateX(0);
  transition: transform 360ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 280ms ease;
  cursor: default;
}

.about[hidden] {
  display: block !important;
  transform: translateX(100%);
  opacity: 0;
  pointer-events: none;
}

.about__close {
  position: absolute;
  /* Sits below the top-right nav so the two don't collide visually. */
  top: 70px;
  right: 16px;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(180deg, rgba(48, 32, 84, 0.42), rgba(20, 14, 36, 0.62));
  backdrop-filter: blur(14px) saturate(160%);
  -webkit-backdrop-filter: blur(14px) saturate(160%);
  border: 1px solid rgba(196, 181, 253, 0.22);
  border-radius: 50%;
  color: rgba(232, 228, 255, 0.88);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.07),
    0 6px 18px -10px rgba(0, 0, 0, 0.7);
  transition:
    color 220ms ease,
    background 220ms ease,
    border-color 220ms ease,
    box-shadow 260ms ease,
    transform 240ms cubic-bezier(0.2, 0.8, 0.2, 1);
  transition: color 200ms ease;
}

.about__close:hover,
.about__close:focus-visible {
  color: #fff;
  background:
    linear-gradient(180deg, rgba(64, 44, 112, 0.55), rgba(28, 20, 50, 0.72));
  border-color: rgba(249, 197, 209, 0.50);
  outline: none;
  transform: translateY(-1px) rotate(90deg);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.10),
    0 10px 26px -10px rgba(196, 181, 253, 0.55);
}

.about__heading {
  font-family: var(--ff-mono);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--accent-1);
  margin: 0 0 18px;
}

.about__body {
  font-family: var(--ff-serif);
  font-size: 17px;
  font-weight: 400;
  line-height: 1.55;
  color: var(--text-bright);
  margin: 0 0 16px;
}

.about__body em {
  font-style: italic;
  color: var(--accent-2);
}

.about__body strong {
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--accent-1);
}

.about__body--mute {
  color: var(--text-soft);
  font-style: italic;
}

.about__controls {
  margin: 24px 0 0;
  padding: 18px 0 0;
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 10px 18px;
  font-family: var(--ff-mono);
  font-size: 11px;
  letter-spacing: 0.05em;
  color: var(--text-soft);
}

.about__controls dt kbd {
  font-family: inherit;
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-1);
  border: 1px solid var(--rule);
  padding: 2px 7px;
  border-radius: 3px;
  background: rgba(196, 181, 253, 0.04);
  white-space: nowrap;
}

.about__controls dd {
  margin: 0;
  align-self: center;
  line-height: 1.45;
}

.about__credit {
  margin: 28px 0 0;
  padding: 16px 0 0;
  border-top: 1px solid var(--rule);
  font-family: var(--ff-mono);
  font-size: 9px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-faint);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.about__credit-line { display: block; }

/* --- FALLBACK -------------------------------------------------------------- */

#fallback {
  position: fixed;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: var(--bg-deep);
  color: var(--accent-1);
  font-family: var(--ff-serif);
  text-align: center;
  padding: 20px;
  z-index: 1000;
}

#fallback[hidden] { display: none; }

#fallback h1 {
  font-size: 22px;
  font-weight: 400;
  font-style: italic;
  margin: 0 0 8px;
  opacity: 0.85;
}

#fallback p {
  font-size: 14px;
  opacity: 0.6;
  margin: 4px 0;
}

/* --- responsive ------------------------------------------------------------ */

@media (max-width: 720px) {
  /* Mobile tour — repositioned from desktop's left-aligned vertical column
     to a docked horizontal strip at the bottom-center. Chips are
     number-only ("01" "02" "03" "04") to fit compactly without sacrificing
     the chapter navigation on phones. z-index above the chapter panel
     so they remain tappable when a chapter is open (sit at the bottom
     edge of the bottom-sheet, reading as chapter tabs). */
  #tour {
    top: auto;
    bottom: calc(max(var(--pad-edge), env(safe-area-inset-bottom)) + 12px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 250;
  }
  .tour__list {
    flex-direction: row;
    gap: 6px;
  }
  .tour__btn {
    padding: 7px 12px;
    gap: 4px;
    /* Solid-fill glass — Safari's backdrop-filter can no-op so make pills
       readable against any underlying content (canvas OR chapter panel). */
    background:
      linear-gradient(180deg, rgba(54, 38, 96, 0.82), rgba(24, 16, 44, 0.90));
    border-color: rgba(196, 181, 253, 0.50);
  }
  .tour__btn::before { display: none; }     /* hide left-edge accent dot */
  .tour__name { display: none; }            /* number-only chips */
  .tour__num {
    font-size: 11px;
    color: rgba(244, 240, 255, 0.92);
    letter-spacing: 0.20em;
  }
  .tour__btn:hover, .tour__btn:focus-visible {
    transform: none;                        /* no translate-X on touch */
  }
  .tour__btn[data-active="true"] {
    border-color: rgba(249, 197, 209, 0.70);
    background: linear-gradient(180deg, rgba(74, 50, 130, 0.88), rgba(38, 24, 70, 0.92));
  }
  .tour__btn[data-active="true"] .tour__num {
    color: var(--accent-2);
  }
}

@media (max-width: 520px) {
  :root {
    --pad-edge: 14px;
  }
  .brand__title  { font-size: 11px; letter-spacing: 0.28em; }
  /* Hide the tagline on phones — the nav row needs the horizontal space and
     the about panel already explains what the app is. */
  .brand__tagline { display: none; }

  .hints { gap: 12px; }
  .hint  { font-size: 9px; letter-spacing: 0.12em; }
  .hint kbd { font-size: 8px; padding: 2px 5px; }
  /* Top padding clears the close-X (now at y≈70-104) so the About panel
     content doesn't slide behind it on phones. The About panel stays as
     a right-side slide-in (it's a long read; works as a panel). */
  .about { width: 100vw; padding: 124px 18px 18px; }
  .about__body { font-size: 15px; }

  /* CHAPTER PANEL on phones is a BOTTOM SHEET, not a right slide-in.
     Crucial UX: when a user taps a chapter, they should still see the
     orbs framed for that chapter in the top half of the screen. The
     desktop version (right-aligned panel + orbs filling the left) doesn't
     translate to portrait — letterboxing the orbs into the top 42% gives
     them air, and the chapter content lives in the bottom 58%. */
  .chapter {
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    width: 100vw;
    height: 58vh;
    border-left: none;
    border-top: 1px solid var(--rule);
    /* Visible state — translateY 0. Hidden state below slides down. */
    transform: translateY(0);
  }
  .chapter[hidden] {
    transform: translateY(100%);
  }
  /* Close X sits at top-right of the SHEET (was below the top nav on
     a tall right-side panel). */
  .chapter__close {
    top: 14px;
    right: 14px;
  }
  /* Inner padding: top clears the close X, bottom clears the tour strip
     that overlays the sheet's lower edge. */
  .chapter__inner {
    padding: 56px 18px 76px;
  }
  .chapter__title { font-size: 22px; }
  .chapter__body { font-size: 14px; }

  /* ---- Top-nav simplification on phones --------------------------------
     All 4 affordances stay visible. We make room by:
       1. dropping the on/off labels on ♪ and ♬ (icon-only)
       2. abbreviating the credits text
       3. tightening pill padding + font
     We ALSO bump the pill background and border opacity on mobile because
     Safari often skips backdrop-filter, leaving the desktop "frosted glass"
     look as a near-transparent pill that vanishes against the dark sky. */
  #links { gap: 6px; }
  .link {
    font-size: 9px;
    padding: 7px 11px;
    letter-spacing: 0.12em;
    /* Heavier solid-fill so pills stay legible without backdrop-filter. */
    background:
      linear-gradient(180deg, rgba(54, 38, 96, 0.78), rgba(24, 16, 44, 0.86));
    border-color: rgba(196, 181, 253, 0.45);
    color: rgba(244, 240, 255, 0.96);
  }
  #audio-toggle .audio-state,
  #react-toggle .react-state,
  #hue-shuffle .hue-label { display: none; }
  #audio-toggle, #react-toggle, #hue-shuffle { padding: 7px 11px; }
  .audio-icon, .react-icon, .hue-icon {
    font-size: 13px;
    /* Even when off, the icon should be clearly visible on a phone. */
    color: rgba(232, 228, 255, 0.85);
  }
  /* Credits pill + fullscreen pill hidden across all mobile widths. With
     ♪ ♬ 🎨 about pills plus the BLOOMCAST title on the left, there isn't
     room for more buttons until tablet sizes (>520px). Both still reachable
     elsewhere — credits in the About panel, fullscreen via the device's
     own browser-chrome controls. */
  .link[data-credits] { display: none; }
  #fullscreen-toggle { display: none !important; }
  .credit-full { display: none; }
  .credit-short { display: inline; }
}

/* Tightest case — sub-380px (iPhone SE-ish portrait). Tighten typography
   one extra notch beyond the standard mobile rule. */
@media (max-width: 380px) {
  #links { gap: 4px; }
  .link { font-size: 8px; padding: 6px 9px; letter-spacing: 0.10em; }
  #audio-toggle, #react-toggle, #hue-shuffle { padding: 6px 9px; }
  .audio-icon, .react-icon, .hue-icon { font-size: 12px; }
}

/* Cursor glow — soft luminous halo following the pointer. Mix-blend-mode
   screen so it brightens whatever it passes over (canvas + text). Hidden
   on touch devices via the no-hover query below. */
#cursor-glow {
  position: fixed;
  top: 0;
  left: 0;
  width: 60px;
  height: 60px;
  margin: -30px 0 0 -30px;
  border-radius: 50%;
  /* Softer, smaller halo — was too bright competing with the orbs. */
  background: radial-gradient(
    circle,
    rgba(196, 181, 253, 0.10) 0%,
    rgba(249, 197, 209, 0.04) 35%,
    transparent 65%
  );
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 50;
  opacity: 0;
  transition: opacity 0.5s ease;
  will-change: transform, opacity;
}

#cursor-glow.visible { opacity: 1; }

/* Hint copy swap — show "click/scroll/right-drag" on hover devices and
   "tap/pinch/drag" on touch devices. Always exactly one is visible. */
.hint-touch { display: none; }
@media (hover: none) {
  #cursor-glow { display: none; }
  .hint-mouse { display: none; }
  .hint-touch { display: inline; }
}

/* Chapter / About copy that depends on perf tier — mobile users see the
   slimmed-build number, desktop users see the full build number. CSS
   handles the swap via (hover: none). */
.tier-mobile { display: none; }
@media (hover: none) {
  .tier-desktop { display: none; }
  .tier-mobile { display: inline; }
}

/* Reduce motion preference */
@media (prefers-reduced-motion: reduce) {
  .about,
  .hints,
  .link,
  .about__close { transition: none; }
  .brand__title span,
  .brand__tagline,
  .hint {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    animation: none !important;
  }
  #cursor-glow { display: none; }
}
