/* =========================================================
   MuSocí — v1 Design System
   Deep premium dark, blue → purple → magenta gradient family,
   pulled from the MuSocí logo. Editorial, alive, anti-algorithm.
   ========================================================= */

:root {
  /* --- Core brand colors (sampled from logo) --- */
  --color-blue:      #3aa0c8;   /* wing / circle blue */
  --color-blue-deep: #2c5d9e;   /* wordmark blue */
  --color-purple:    #5b2f86;   /* wordmark purple / note top */
  --color-magenta:   #b8326a;   /* note bottom */
  --color-pink:      #fe66c4;   /* tagline accent pink */

  /* --- Base surface (deep premium dark, default = night-leaning) --- */
  --bg-0: #0c0a18;     /* page background */
  --bg-1: #14112a;     /* section alt background */
  --bg-2: #1c1838;     /* card / panel background */
  --bg-glow: radial-gradient(circle at 50% 0%, rgba(91, 47, 134, 0.35), transparent 60%);

  /* --- Text --- */
  --text-0: #f6f4fb;   /* primary text */
  --text-1: #c9c2dd;   /* secondary text */
  --text-muted: #8d84ab;

  /* --- Gradients --- */
  --grad-brand: linear-gradient(135deg, var(--color-blue) 0%, var(--color-purple) 55%, var(--color-magenta) 100%);
  --grad-text: linear-gradient(120deg, var(--color-blue) 0%, var(--color-pink) 100%);

  /* --- Glow shadows (blue -> magenta), used for the cinematic look --- */
  --glow-blue: rgba(58, 160, 200, 0.45);
  --glow-magenta: rgba(184, 50, 106, 0.45);
  --glow-pink: rgba(254, 102, 196, 0.45);
  --glow-pink-bright: rgba(254, 102, 196, 0.85);

  /* --- Layout --- */
  --container-w: 1100px;
  --container-narrow: 760px;
  --radius: 14px;
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2.5rem;
  --space-5: 4rem;
  --space-6: 6rem;

  /* --- Fonts --- */
  --font-display: "Fraunces", Georgia, serif;
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  /* --- Transition --- */
  --transition-slow: 900ms ease;
}

/* =========================================================
   CIRCADIAN TOUCH — Day vs Night
   Default (no attribute, or data-daypart="night") = deep,
   moody, magenta-leaning. Day = brighter, bluer, more open.
   Applied to <body data-daypart="day|night"> by js/main.js
   ========================================================= */

body[data-daypart="day"] {
  --bg-0: #14182e;
  --bg-1: #1b2140;
  --bg-2: #232a50;
  --bg-glow: radial-gradient(circle at 50% 0%, rgba(58, 160, 200, 0.30), transparent 60%);
  --text-1: #d6dcf2;
}

body[data-daypart="night"] {
  --bg-0: #0a0814;
  --bg-1: #120f24;
  --bg-2: #1a1530;
  --bg-glow: radial-gradient(circle at 50% 0%, rgba(184, 50, 106, 0.30), transparent 60%);
}

/* =========================================================
   RESET / BASE
   ========================================================= */

* { box-sizing: border-box; }

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg-0);
  color: var(--text-0);
  font-family: var(--font-body);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  transition: background var(--transition-slow);
}

img { max-width: 100%; display: block; }

a { color: var(--color-blue); text-decoration: none; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
}

.container {
  max-width: var(--container-w);
  margin: 0 auto;
  padding: 0 var(--space-3);
}

.container.narrow {
  max-width: var(--container-narrow);
}

.grad-text {
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter:
    drop-shadow(0 0 18px var(--glow-blue))
    drop-shadow(0 0 34px var(--glow-pink));
}

/* "Audio TV" in the hero — the most important phrase, so it gets a
   much darker, high-contrast gradient against the bright blue
   background, with a soft light halo to keep it feeling like a glow
   rather than flat text. */
.hero .grad-text {
  background: linear-gradient(120deg, #0a1638 0%, #3a1240 55%, #5c1638 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  filter:
    drop-shadow(0 0 14px rgba(255, 255, 255, 0.35))
    drop-shadow(0 0 26px rgba(180, 210, 255, 0.3));
}

/* =========================================================
   BUTTONS
   ========================================================= */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.85rem 1.8rem;
  border-radius: 999px;
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.2s ease, opacity 0.2s ease;
}

.btn-primary {
  background: var(--grad-brand);
  color: #fff;
  box-shadow:
    0 8px 24px -8px rgba(184, 50, 106, 0.6),
    0 0 0 1px rgba(255, 255, 255, 0.04) inset,
    0 0 22px -4px var(--glow-blue);
  animation: btn-pulse 4.5s ease-in-out infinite;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow:
    0 12px 28px -8px rgba(184, 50, 106, 0.75),
    0 0 0 1px rgba(255, 255, 255, 0.06) inset,
    0 0 32px -2px var(--glow-pink);
  animation-play-state: paused;
}

@keyframes btn-pulse {
  0%, 100% {
    box-shadow:
      0 8px 24px -8px rgba(184, 50, 106, 0.6),
      0 0 0 1px rgba(255, 255, 255, 0.04) inset,
      0 0 18px -4px var(--glow-blue);
  }
  50% {
    box-shadow:
      0 8px 28px -6px rgba(184, 50, 106, 0.75),
      0 0 0 1px rgba(255, 255, 255, 0.04) inset,
      0 0 28px -2px var(--glow-pink);
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn-primary { animation: none; }
}

.btn-ghost {
  background: transparent;
  color: var(--text-0);
  border-color: rgba(246, 244, 251, 0.25);
}

.btn-ghost:hover {
  border-color: rgba(246, 244, 251, 0.6);
}

.btn-lg {
  padding: 1.05rem 2.4rem;
  font-size: 1.05rem;
}

.btn-nav {
  padding: 0.6rem 1.4rem;
  font-size: 0.85rem;
}

/* =========================================================
   HEADER / NAV
   ========================================================= */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: rgba(12, 10, 24, 0.7);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
}

.brand-logo {
  height: 38px;
  width: auto;
  filter: drop-shadow(0 0 10px var(--glow-magenta));
}

.site-nav {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.site-nav a {
  color: var(--text-1);
  font-size: 0.9rem;
  font-weight: 500;
  transition: color 0.2s ease;
}

.site-nav a:hover { color: var(--text-0); }

@media (max-width: 720px) {
  .site-nav { display: none; }
}

/* =========================================================
   HERO
   ========================================================= */

.hero {
  position: relative;
  padding: var(--space-3) 0 var(--space-5);
  overflow: hidden;
  /* Fallback gradient shown before/if video loads */
  background:
    radial-gradient(ellipse at 50% 100%, rgba(160, 205, 255, 0.45), transparent 55%),
    radial-gradient(circle at 85% 20%, rgba(110, 90, 255, 0.25), transparent 45%),
    linear-gradient(180deg, #15349c 0%, #2456c8 55%, #6e9bf2 100%);
}

/* Hero background video — sits below all overlays and content */
.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  z-index: 0;
  pointer-events: none;
}

.hero-glow {
  position: absolute;
  inset: 0;
  background: var(--bg-glow);
  z-index: 0;
  pointer-events: none;
  transition: background var(--transition-slow);
}

/* vignette for depth — softly deepens the edges so the center glows,
   tuned for the deeper, richer blue hero background */
.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  /* Uniform dark coat — evens out the video's naturally bright center
     rather than a vignette that amplifies it. Matches the original
     musoci.com approach (they used 0.7; 0.45 keeps more video visible). */
  background: rgba(5, 12, 45, 0.45);
}

.hero-inner {
  position: relative;
  z-index: 3;
  text-align: center;
  max-width: 760px;
  /* Soft radial dark scrim anchored to the text block — no hard edges */
  background: radial-gradient(ellipse at 50% 42%, rgba(0,0,0,0.32) 0%, transparent 72%);
  padding: var(--space-2) var(--space-3);
  border-radius: 1.5rem;
}

/* =========================================================
   FLOATING MUSIC NOTES — gentle animated background
   ========================================================= */

.notes-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 1;
  pointer-events: none;
}

.note {
  position: absolute;
  font-family: var(--font-display);
  line-height: 1;
  /* Near-black navy notes on the deep blue field — matches the
     original site's dense, dark, converging note pattern. */
  color: rgba(255, 255, 255, 0.18);
  filter: drop-shadow(0 3px 10px rgba(0, 0, 0, 0.30));
  animation: float-note ease-in-out infinite;
  will-change: transform, opacity;
}

/* Notes start large and near the viewer, then recede inward toward the
   center vanishing point — shrinking and rotating as they travel, like
   they're flying backward into the screen and drawing the eye inward. */
@keyframes float-note {
  0%   { transform: translate(0, 0) rotate(0deg) scale(1.15); opacity: 0; }
  12%  { opacity: 0.75; }
  75%  { opacity: 0.45; }
  100% { transform: translate(var(--tx, 0), var(--ty, 0)) rotate(var(--spin, 12deg)) scale(0.18); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .note { animation: none; opacity: 0.15; }
}

/* Warm time-of-day greeting above the hero headline — noticeable,
   but smaller than and subordinate to the main headline below it */
.hero-greeting {
  margin: 0 0 0.35rem;
  font-size: clamp(1rem, 2.6vw, 1.2rem);
  font-weight: 500;
  /* Soft champagne/cream-gold — warmer and more "hello" than the
     near-white body text, but still quiet and premium */
  color: #e8dcc0;
  letter-spacing: 0.02em;
  opacity: 0.95;
  min-height: 1.3em; /* avoid layout shift before JS sets the text */
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.70);
}

@media (max-width: 600px) {
  .hero-greeting {
    font-size: 0.95rem;
  }
}

/* Letter-by-letter type-in (driven by JS, which wraps each character
   in a span) — each letter fades/steps in quickly in sequence */
.hero-greeting__char {
  display: inline-block;
  opacity: 0;
  animation: hero-greeting-char-in 0.25s ease-out forwards;
}

@keyframes hero-greeting-char-in {
  from { opacity: 0; transform: translateY(0.15em); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Once typing finishes, JS adds this class for a slow, gentle
   opacity/glow breath — visible, but still a slow soft cycle,
   not a strobe. Opacity dips noticeably via compositing (which
   affects the whole element incl. the per-letter spans), and
   the glow at the peak inherits down to the letters. */
.hero-greeting--pulse {
  animation: hero-greeting-pulse 4s ease-in-out infinite;
}

@keyframes hero-greeting-pulse {
  0%, 100% {
    opacity: 0.7;
    text-shadow: 0 0 0 transparent;
  }
  50% {
    opacity: 1;
    text-shadow: 0 0 18px var(--glow-blue), 0 0 34px var(--glow-pink);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero-greeting__char {
    opacity: 1;
    animation: none;
  }
  .hero-greeting--pulse {
    animation: none;
  }
}

.hero-logo-wrap {
  display: flex;
  justify-content: center;
  margin: 0 auto var(--space-3);
}

/* Real logo graphic — centerpiece of the hero (clean gradient mark) */
.hero-logo {
  display: block;
  width: min(190px, 42vw);
  height: auto;
  /* Simple grounding shadow only — the vibrancy now comes from the
     radial bloom behind it, not an edge halo on the artwork. */
  filter: drop-shadow(0 8px 24px rgba(5, 12, 45, 0.45));
}

/* "MuSocí" wordmark image, used inline in the hero headline so the
   lettering matches the brand exactly (a font can't replicate it). */
.hero-wordmark {
  display: inline-block;
  height: 1.15em;
  width: auto;
  vertical-align: -0.08em;
  margin-right: 0.1em;
  /* Invert the black-on-transparent PNG to white-on-transparent */
  filter: invert(1);
}

.hero-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2.4rem, 6vw, 4rem);
  line-height: 1.1;
  margin: 0 0 var(--space-2);
  color: #ffffff;
  text-shadow:
    0 1px 3px rgba(0, 0, 0, 0.85),
    0 3px 12px rgba(0, 0, 0, 0.55);
}

.hero-sub {
  font-size: 1.2rem;
  color: #ffffff;
  font-style: italic;
  margin: 0 0 var(--space-3);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.80);
}

.hero-definition {
  font-size: 1.05rem;
  color: #ffffff;
  max-width: 620px;
  margin: 0 auto var(--space-4);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.75);
}

.hero-cta {
  display: flex;
  justify-content: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

/* Hero-only button contrast fixes — the default brand gradient and ghost
   border were too close to the blue hero background and faded in. */
.hero-cta .btn-primary {
  background: linear-gradient(135deg, var(--color-magenta) 0%, var(--color-pink) 100%);
  box-shadow:
    0 8px 24px -6px rgba(0, 0, 0, 0.45),
    0 0 0 1px rgba(255, 255, 255, 0.18) inset;
}

.hero-cta .btn-ghost {
  /* Match btn-lg sizing so both hero buttons are the same height + baseline */
  padding: 1.05rem 2.4rem;
  font-size: 1.05rem;
  background: rgba(10, 22, 56, 0.35);
  color: #ffffff;
  border-color: rgba(255, 255, 255, 0.6);
}

.hero-cta .btn-ghost:hover {
  background: rgba(10, 22, 56, 0.5);
  border-color: rgba(255, 255, 255, 0.9);
}

/* --- Mood Index --- */

.mood-index {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 1.4rem;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid rgba(255, 255, 255, 0.1);
  font-size: 0.95rem;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.03) inset,
    0 0 24px -6px var(--glow-magenta),
    0 0 40px -12px var(--glow-blue);
}

.mood-index__label {
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-size: 0.7rem;
  font-weight: 600;
}

.mood-index__mood {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.05rem;
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  transition: opacity 0.4s ease;
}

/* =========================================================
   MARQUEE — reusable auto-scrolling row
   Use for any tile/card/pill row that should drift sideways
   on its own, filmstrip-style. Markup pattern:
     <div class="marquee">
       <div class="marquee__track">
         ...items... (duplicated once in the DOM for a seamless loop)
       </div>
     </div>
   The track is twice as wide as one set of items and animates
   from translateX(0) to translateX(-50%), so the duplicate set
   lines up exactly as the first set scrolls off.
   ========================================================= */
.marquee {
  margin-top: var(--space-3);
  overflow: hidden;
  -webkit-mask-image: linear-gradient(
    to right,
    transparent,
    #000 8%,
    #000 92%,
    transparent
  );
  mask-image: linear-gradient(
    to right,
    transparent,
    #000 8%,
    #000 92%,
    transparent
  );
}

.marquee__track {
  display: flex;
  gap: 0.75rem;
  width: max-content;
  animation: marquee-scroll 36s linear infinite;
}

.marquee:hover .marquee__track,
.marquee:focus-within .marquee__track {
  animation-play-state: paused;
}

.marquee__item {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 1.1rem;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid rgba(255, 255, 255, 0.08);
  font-size: 0.85rem;
  color: var(--text-2);
  white-space: nowrap;
  box-shadow: 0 0 18px -8px var(--glow-blue);
}

@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .marquee__track {
    animation: none;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* Show Blocks — square show-art tiles in the marquee */
.marquee--shows {
  margin-top: var(--space-4);
}

.marquee--shows .marquee__track {
  /* No flex `gap` here on purpose: gap adds one extra gap-width
     between the two duplicated sets, which throws off the
     translateX(-50%) loop point by half a gap and causes a visible
     jump/stutter on every cycle. Using a margin on each tile instead
     keeps every tile's "slot" (tile + spacing) an identical width,
     so the duplicated track is exactly 2x one set and -50% lines up
     pixel-perfect. */
  gap: 0;
  animation-duration: 75s;
}

/* Desktop hover: pause the marquee so tiles are easy to click */
.marquee--shows:hover .marquee__track {
  animation-play-state: paused;
}

.show-tile {
  flex: 0 0 auto;
  width: clamp(140px, 22vw, 220px);
  aspect-ratio: 1 / 1;
  margin-right: var(--space-3);
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 0 24px -10px var(--glow-blue);
}

.show-tile__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Show tiles that link to a world — clickable */
.show-tile[data-world] { cursor: pointer; }

.show-tile.is-selected {
  outline: 2px solid rgba(255, 188, 32, 0.85);
  outline-offset: -2px;
  border-radius: var(--radius);
}

/* Featured Artists — "Here's Who Earned It" marquee, same seamless-loop
   pattern as .marquee--shows but pink-tinted to read as "artists" */
.marquee--artists {
  margin-top: var(--space-4);
}

.marquee--artists .marquee__track {
  /* See note on .marquee--shows above — gap:0 + per-tile margin keeps
     the duplicated track exactly 2x one set for a seamless -50% loop. */
  gap: 0;
  animation-duration: 45s;
}

.artist-tile {
  position: relative;
  flex: 0 0 auto;
  width: clamp(140px, 22vw, 220px);
  aspect-ratio: 1 / 1;
  margin-right: var(--space-3);
  border-radius: var(--radius);
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: 0 0 24px -10px var(--glow-pink);
}

.artist-tile__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  display: block;
}

/* Top-anchored crop for tall/vertical source photos — keeps the
   face/head in frame instead of center-cropping it out */
.artist-tile__img--top {
  object-position: top center;
}

/* Nikki Voice's photo — nudged down slightly from a pure top crop so
   more of the "N" logo at the bottom of the shot is visible, while
   keeping her head/face safely in frame */
.artist-tile__img--nikki {
  object-position: center 20%;
}

/* DreadHead4Life's photo — full-body vertical shot, seated cross-legged.
   Anchor further down to trim the empty white space above his head and
   reveal more of his seated pose (jeans, crossed legs, sneakers), while
   keeping his face, hat, and shades safely in frame. */
.artist-tile__img--dreadhead {
  object-position: center 40%;
}

.artist-tile__name {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  padding: 0.6rem 0.75rem 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-0);
  background: linear-gradient(to top, rgba(0, 0, 0, 0.8), transparent);
}

/* Open-slot tile — a real link into submission, not a dead placeholder.
   Reads as "earned, not filled yet" rather than "coming soon". */
.artist-tile--placeholder {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  background: linear-gradient(160deg, var(--bg-2), var(--bg-1));
  text-decoration: none;
  border: 1px solid rgba(255, 255, 255, 0.08);
  transition: border-color 0.2s ease, background 0.2s ease, transform 0.2s ease;
}

.artist-tile--placeholder:hover,
.artist-tile--placeholder:focus-visible {
  border-color: rgba(254, 102, 196, 0.45);
  background: linear-gradient(160deg, var(--bg-2), var(--bg-1) 70%, rgba(254, 102, 196, 0.08));
}

.artist-tile--placeholder__plus {
  position: relative;
  width: 26%;
  aspect-ratio: 1 / 1;
}

.artist-tile--placeholder__plus::before,
.artist-tile--placeholder__plus::after {
  content: "";
  position: absolute;
  background: rgba(255, 255, 255, 0.4);
  border-radius: 2px;
  transition: background 0.2s ease;
}

.artist-tile--placeholder__plus::before {
  left: 50%;
  top: 0;
  bottom: 0;
  width: 13%;
  transform: translateX(-50%);
}

.artist-tile--placeholder__plus::after {
  top: 50%;
  left: 0;
  right: 0;
  height: 13%;
  transform: translateY(-50%);
}

.artist-tile--placeholder:hover .artist-tile--placeholder__plus::before,
.artist-tile--placeholder:hover .artist-tile--placeholder__plus::after,
.artist-tile--placeholder:focus-visible .artist-tile--placeholder__plus::before,
.artist-tile--placeholder:focus-visible .artist-tile--placeholder__plus::after {
  background: var(--color-pink);
}

.artist-tile--placeholder__tag {
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--color-pink);
}

.artist-tile--placeholder__label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-1);
  text-align: center;
  padding: 0 0.5rem;
}

.section-kicker--center {
  text-align: center;
}

/* =========================================================
   GENERIC SECTIONS
   ========================================================= */

.section {
  padding: var(--space-5) 0;
}

.section--listeners { background: var(--bg-1); }
.section--artists { background: var(--bg-0); }
.section--journey { background: var(--bg-1); }
.section--signup { background: var(--bg-0); }
.section--shows { background: var(--bg-0); }
.section--featured-artists { background: var(--bg-1); }

.section-title--center {
  text-align: center;
}

.section-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.6rem, 4vw, 2.2rem);
  margin: 0 0 var(--space-3);
  line-height: 1.25;
}

.section-kicker {
  color: var(--text-1);
  margin-top: -0.5rem;
  margin-bottom: var(--space-3);
}

.section p { color: var(--text-1); }

.welcome-line {
  font-family: var(--font-display);
  font-size: 1.4rem;
  color: var(--text-0);
  font-weight: 600;
  margin-top: var(--space-3);
}

.emphasis-line {
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: var(--text-0);
  font-weight: 500;
  font-style: italic;
}

.pull-quote {
  margin: var(--space-3) 0;
  padding: var(--space-2) var(--space-3);
  border-left: 3px solid var(--color-pink);
  font-family: var(--font-display);
  font-style: italic;
  font-size: 1.2rem;
  color: var(--text-0);
}

/* =========================================================
   PLAYER (Live365) + JOTFORM PLACEHOLDERS
   ========================================================= */

.player-section {
  padding: var(--space-5) 0;
  background: var(--bg-1);
}

.player-frame {
  margin-top: var(--space-3);
}

/* Two-column "Now Airing" layout — content left, vertical player right.
   Stacks (content above player) on narrow screens. */
.player-layout {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.player-layout__content {
  flex: 1 1 320px;
}

.player-layout__content .section-title,
.player-layout__content .section-kicker {
  margin: 0;
}

.player-layout__content .section-kicker {
  margin-top: var(--space-2);
}

/* "Now Airing" live show + mood, auto-filled by js/schedule.js */
.now-airing__badge {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: var(--space-2);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-pink);
}

.now-airing__badge::before {
  content: "";
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: var(--color-pink);
  box-shadow: 0 0 8px var(--glow-pink);
}

.now-airing__show {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.3rem;
  color: var(--text-0);
  margin: var(--space-1) 0 0;
}

.now-airing__mood {
  font-size: 1rem;
  font-style: italic;
  color: var(--text-muted);
  margin: 0.25rem 0 0;
  min-height: 1.5em;
  transition: opacity 1s ease;
}

.player-layout__player {
  flex: 0 0 auto;
  width: 100%;
  max-width: 300px;
  margin: 0 auto;
}

@media (max-width: 760px) {
  .player-layout {
    flex-direction: column;
  }
  .player-layout__content {
    text-align: center;
  }
}

/* Live365 embeds — main player + "Previously on MuSocí" history widget,
   laid out side-by-side on wide screens and stacked on mobile. */
.live365-players {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: var(--space-4);
}

.live365-player {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
}

.live365-player__label {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
  color: var(--text-0);
  margin: 0 0 var(--space-2);
  text-align: center;
}

.responsive-embed {
  width: 100%;
  margin: 0 auto;
  border-radius: var(--radius);
  overflow: hidden;
}

.responsive-embed iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.responsive-embed--player {
  max-width: 300px;
  aspect-ratio: 300 / 614;
}

.responsive-embed--played {
  max-width: 450px;
  aspect-ratio: 450 / 511;
}

.player-placeholder,
.jotform-placeholder {
  border: 1px dashed rgba(255, 255, 255, 0.18);
  border-radius: var(--radius);
  padding: var(--space-4);
  background: var(--bg-2);
}

.player-placeholder__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
  margin: 0 0 0.5rem;
}

.player-placeholder__text {
  color: var(--text-1);
  font-size: 0.95rem;
  max-width: 560px;
}

.player-placeholder__text code {
  background: rgba(255,255,255,0.08);
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  font-size: 0.85em;
}

.player-placeholder__slot {
  margin-top: var(--space-2);
  padding: var(--space-4);
  border-radius: var(--radius);
  background: var(--bg-0);
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 90px;
}

.placeholder-tag {
  color: var(--text-muted);
  font-size: 0.85rem;
  letter-spacing: 0.05em;
}

/* =========================================================
   JOURNEY STEPS
   ========================================================= */

.journey-steps {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.journey-steps li {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
}

.journey-steps__num {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.4rem;
  height: 2.4rem;
  border-radius: 50%;
  background: var(--grad-brand);
  color: #fff;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.1rem;
}

.journey-steps h3 {
  margin: 0 0 0.25rem;
  font-family: var(--font-display);
  font-weight: 600;
}

.journey-steps p { margin: 0; }

/* =========================================================
   SUBMISSION BLOCK
   ========================================================= */

.submission-block {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid rgba(255, 255, 255, 0.08);
}

.submission-block__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.6rem;
  margin: 0 0 var(--space-1);
}

.submission-block__note {
  color: var(--text-1);
  margin-bottom: var(--space-3);
}

/* =========================================================
   THE SOUL THEORY — CINEMATIC SHOW WORLD
   ========================================================= */

/* Section: full-width cinematic band.
   aspect-ratio matches the wide image (1659×948 = 7:4) exactly so object-fit:cover
   shows the complete image with zero crop at any viewport width. */
.world--soul-theory {
  position: relative;
  width: 100%;
  aspect-ratio: 1659 / 948;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate; /* contain mix-blend-mode: screen on smoke to this section */
}

/* ---- Background art ---- */
.soul-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  z-index: 0;
}
/* ---- Single drift frame: ONE element carries the Ken Burns animation.
   The background image, lamp overlays, and city windows are all children
   of this div — they share one CSS transform and physically cannot drift
   apart. Smoke and embers live outside this frame. ---- */
.soul-drift-frame {
  position: absolute;
  inset: 0;
  transform-origin: center center;
  will-change: transform;
  animation: soul-bg-drift 45s ease-in-out infinite alternate;
}
/* Very subtle Ken Burns — any scale > 1.00 trims edges slightly */
@keyframes soul-bg-drift {
  from { transform: scale(1.00) translate(0,    0);    }
  to   { transform: scale(1.03) translate(-0.8%, 0.3%); }
}

.soul-bg__img {
  width: 100%;
  height: 100%;
  /* Container aspect-ratio matches image: cover = no crop at all at 1× scale */
  object-fit: cover;
  object-position: center center;
  display: block;
  /* No animation here — parent .soul-drift-frame carries the transform */
}

/* ---- Scrim: heavy bottom darkening for tagline readability,
          subtle left shadow behind the singer ---- */
.soul-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to top,
      rgba(5,2,12,0.92) 0%,
      rgba(5,2,12,0.72) 16%,
      rgba(5,2,12,0.38) 36%,
      rgba(5,2,12,0.08) 58%,
      transparent       78%
    ),
    linear-gradient(to right,
      rgba(5,2,12,0.22) 0%,
      transparent       18%
    );
}

/* ---- Smoke: canvas particle simulation (js/smoke.js)
   Canvas is centered horizontally on the trumpet bell (left:63%) and spans the
   full section height so particles can rise as far as they need to.
   mix-blend-mode:screen makes the canvas black background invisible —
   only bright smoke particles composite against the artwork.
   isolation:isolate on the section (already set) confines the blend mode. ---- */
.soul-smoke-canvas {
  position: absolute;
  left: 63%;
  top: 0;
  transform: translateX(-50%);
  width: 28%;
  height: 100%;
  z-index: 2;
  pointer-events: none;
  mix-blend-mode: screen;
}

/* ---- Gold dust particles: fine flecks rising from the piano (bottom of frame).
   Piano hands are in the lower-right area of the wide image (~left 60-82%, bottom 2-10%).
   Sizes small (2-4px), glow tight, drifting slowly upward from the performance. ---- */
.soul-embers {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}
.soul-ember {
  position: absolute;
  border-radius: 50%;
  background: rgba(255,225,80,0.90);
  box-shadow:
    0 0 4px 2px rgba(255,205,55,0.72),
    0 0 9px 4px rgba(255,170,20,0.36);
  will-change: transform, opacity;
}
/* Anchored at bottom 2-10% = piano zone; rise slowly through the scene */
.soul-ember--1 { width: 3px; height: 3px; left: 67%; bottom: 6%;  animation: ember-rise  9s ease-in infinite; animation-delay:  0s; }
.soul-ember--2 { width: 2px; height: 2px; left: 72%; bottom: 4%;  animation: ember-rise 12s ease-in infinite; animation-delay: -4s; }
.soul-ember--3 { width: 4px; height: 4px; left: 63%; bottom: 8%;  animation: ember-rise 10s ease-in infinite; animation-delay: -7s; }
.soul-ember--4 { width: 2px; height: 2px; left: 78%; bottom: 3%;  animation: ember-rise 14s ease-in infinite; animation-delay: -2s; }
.soul-ember--5 { width: 3px; height: 3px; left: 69%; bottom: 10%; animation: ember-rise 11s ease-in infinite; animation-delay: -9s; }
.soul-ember--6 { width: 2px; height: 2px; left: 75%; bottom: 5%;  animation: ember-rise  8s ease-in infinite; animation-delay: -5s; }
.soul-ember--7 { width: 3px; height: 3px; left: 61%; bottom: 7%;  animation: ember-rise 13s ease-in infinite; animation-delay: -1s; }
.soul-ember--8 { width: 2px; height: 2px; left: 82%; bottom: 2%;  animation: ember-rise 10s ease-in infinite; animation-delay: -6s; }
@keyframes ember-rise {
  0%   { transform: translateY(0)      translateX(0);    opacity: 0;    }
  8%   { opacity: 0.92; }
  50%  { transform: translateY(-160px) translateX(8px);  opacity: 0.68; }
  82%  { opacity: 0.14; }
  100% { transform: translateY(-340px) translateX(-5px); opacity: 0;    }
}


  12%  { opacity: 0;    }
  22%  { opacity: 0.60; }
  34%  { opacity: 0.26; }
  44%  { opacity: 0.54; }
  56%  { opacity: 0;    }
  100% { opacity: 0;    }
}

/* ---- Lamp-sync wrapper: fills the world section identically to the
   background image and carries the same soul-bg-drift transform animation.
   Because the wrapper's bounding box matches the container exactly, its
   transform-origin:center is the same physical pivot as the image's — so
   every child lamp glow tracks the Ken Burns drift frame-for-frame.
   Opacity and child z-index are unaffected; only position is synced. ---- */
/* ---- Lamp wrapper: no animation — inherits drift from .soul-drift-frame parent. ---- */
.soul-lamp-sync {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 3;
}

/* ---- City-window wrapper: no animation — inherits drift from .soul-drift-frame parent.
   Sits below the lamp wrapper (z-index:2 vs 3). ---- */
.soul-city-sync {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 2;
}

/* ---- Place mode: freeze the single drift frame so visual pos = layout pos.
   Added by placer.js via document.body.classList.add("is-placing") when ?place=1. ---- */
.is-placing .soul-drift-frame {
  animation-play-state: paused !important;
}

/* ---- City window shared styles.
   Small warm point of light. JS (soul-city.js) controls opacity (0=off, onOpacity=lit).
   In ?place=1 mode soul-city.js sets all windows to opacity:1 so they're visible
   while being dragged to real building positions.
   mix-blend-mode:screen composites warmly against dark building silhouettes. ---- */
.soul-win {
  position: absolute;
  width: 0.70%;
  height: 0.90%;
  border-radius: 1px;
  mix-blend-mode: screen;
  opacity: 0;
  pointer-events: none;
  transition: opacity 5s ease-in-out; /* JS overrides per-window for varied feel */
  background: radial-gradient(ellipse,
    rgba(255, 252, 210, 0.80)  0%,
    rgba(255, 240, 140, 0.55) 25%,
    rgba(255, 205,  55, 0.28) 55%,
    rgba(255, 160,  15, 0.08) 78%,
    transparent               100%
  );
  /* no box-shadow — clean point of light, no halo */
}

/* soul-win--twinkle / is-twinkling — flicker behaviour handled in JS (soul-city.js).
   CSS animation removed: JS sets inline opacity directly for flicker,
   which avoids the CSS animation override conflict and gives real random flicker. */

/* Finalized positions — manually placed on real building windows 2026-06-20. */
.soul-win--1 { left: 21.59%; top: 63.62%; }
.soul-win--2 { left: 10.72%; top: 62.84%; }
.soul-win--3 { left: 16.68%; top: 55.28%; }
.soul-win--4 { left:  4.39%; top: 59.71%; }
.soul-win--5 { left:  1.64%; top: 67.01%; }
.soul-win--6 { left: 15.93%; top: 65.45%; }
.soul-win--7 { left: 13.25%; top: 60.10%; }
.soul-win--8 { left:  6.18%; top: 64.67%; }

/* ================================================================
   LAMPS — FINALIZED. DO NOT EDIT POSITIONS OR SIZE.
   .soul-streetlight → left:12.0%  top:72.34%  width:1%  height:1.75%
   .soul-sign-glow   → left:23.9%  top:76.2%   width:1%  height:1.75%
   These are pixel-locked to artwork bulbs via Ken Burns drift-sync.
   Placer.js is barred from touching these elements (soul-win class guard).
   Any future CSS edit near this section: scroll past without changing.
   ================================================================ */

/* ---- Streetlight — LEFT lamp above the "good music sold here" sign.
   JS drives opacity: steady 0.82 for ~5–6 s, then electrical flicker.
   Pixel-measured bulb: (218,694) in the 1659×948 image = 13.14%,73.21%.
   CSS left/top offset by half element size (1%×1.75%) to center on bulb. ---- */
.soul-streetlight {
  position: absolute;
  left: 12.0%;
  top: 72.34%;
  width: 1%;
  height: 1.75%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 248, 200, 0.95) 0%,
    rgba(255, 215,  90, 0.70) 35%,
    rgba(255, 165,  30, 0.30) 65%,
    transparent               85%
  );
  box-shadow:
    0 0 8px  4px rgba(255, 215,  80, 0.55),
    0 0 20px 10px rgba(255, 160,  20, 0.22);
  mix-blend-mode: screen;
  opacity: 0;
  z-index: 3;
  pointer-events: none;
}

/* ---- Sign glow — RIGHT lamp above the sign: calm, mostly-steady warm wash
   that illuminates the sign text. No flicker. Very gentle breathing.
   Pixel-measured bulb: (395,738) in the 1659×948 image = 23.81%,77.85%.
   CSS left/top offset by half element size (1%×1.75%) to center on bulb. ---- */
.soul-sign-glow {
  position: absolute;
  left: 23.9%;
  top: 76.2%;
  width: 1%;
  height: 1.75%;
  border-radius: 50%;
  background: radial-gradient(circle,
    rgba(255, 248, 200, 0.90) 0%,
    rgba(255, 215,  90, 0.65) 35%,
    rgba(255, 165,  30, 0.28) 65%,
    transparent               85%
  );
  box-shadow:
    0 0 12px  6px rgba(255, 210,  75, 0.50),
    0 0 30px 15px rgba(255, 155,  20, 0.18);
  mix-blend-mode: screen;
  opacity: 0.76;
  z-index: 3;
  pointer-events: none;
  animation: sign-glow-breathe 11s ease-in-out infinite;
}
@keyframes sign-glow-breathe {
  0%, 100% { opacity: 0.70; }
  50%       { opacity: 0.82; }
}

/* ---- EQ bar overlay: 9 animated audio-meter bars inside the
   "O" counter in SOUL. Must stay ENTIRELY within the O's inner
   circle. Container is small and high so even the tallest bar
   at peak scaleY never touches the O's bottom ring or "THEORY".
   Container bottom = top + height = 40 + 6 = 46% — well inside O. ---- */
.soul-eq-overlay {
  position: absolute;
  left: 44%;
  top: 40%;
  width: 5%;
  height: 6%;
  z-index: 3;
  pointer-events: none;
  display: flex;
  align-items: flex-end;
  justify-content: space-evenly;
  mix-blend-mode: screen;
  overflow: hidden;
  opacity: 0.72;
}
.soul-eq-bar {
  display: block;
  width: 7%;
  height: 100%;
  background: linear-gradient(to top,
    rgba(255, 188, 32, 0.92)  0%,
    rgba(255, 158, 18, 0.72) 55%,
    rgba(255, 218, 92, 0.58) 100%
  );
  border-radius: 1px 1px 0 0;
  transform-origin: bottom center;
  transform: scaleY(0.15);
}
@keyframes eq-p1 { 0%,100%{transform:scaleY(0.12)} 50%{transform:scaleY(0.74)} }
@keyframes eq-p2 { 0%,100%{transform:scaleY(0.38)} 50%{transform:scaleY(0.90)} }
@keyframes eq-p3 { 0%,100%{transform:scaleY(0.58)} 50%{transform:scaleY(0.28)} }
@keyframes eq-p4 { 0%,100%{transform:scaleY(0.18)} 50%{transform:scaleY(0.96)} }
@keyframes eq-p5 { 0%,100%{transform:scaleY(0.45)} 50%{transform:scaleY(0.62)} }
@keyframes eq-p6 { 0%,100%{transform:scaleY(0.28)} 50%{transform:scaleY(0.82)} }
@keyframes eq-p7 { 0%,100%{transform:scaleY(0.62)} 50%{transform:scaleY(0.22)} }
@keyframes eq-p8 { 0%,100%{transform:scaleY(0.35)} 50%{transform:scaleY(0.72)} }
@keyframes eq-p9 { 0%,100%{transform:scaleY(0.50)} 50%{transform:scaleY(0.18)} }
.soul-eq-bar--1 { animation: eq-p1 3.2s ease-in-out infinite; animation-delay:  0.0s; }
.soul-eq-bar--2 { animation: eq-p2 2.8s ease-in-out infinite; animation-delay: -0.8s; }
.soul-eq-bar--3 { animation: eq-p3 3.7s ease-in-out infinite; animation-delay: -1.4s; }
.soul-eq-bar--4 { animation: eq-p4 2.5s ease-in-out infinite; animation-delay: -0.3s; }
.soul-eq-bar--5 { animation: eq-p5 3.4s ease-in-out infinite; animation-delay: -2.1s; }
.soul-eq-bar--6 { animation: eq-p6 2.9s ease-in-out infinite; animation-delay: -1.7s; }
.soul-eq-bar--7 { animation: eq-p7 3.1s ease-in-out infinite; animation-delay: -0.6s; }
.soul-eq-bar--8 { animation: eq-p8 3.5s ease-in-out infinite; animation-delay: -2.8s; }
.soul-eq-bar--9 { animation: eq-p9 2.7s ease-in-out infinite; animation-delay: -1.2s; }

/* ---- Tagline: upper-center, in the darker atmospheric zone ABOVE the gold title ----
   Gold title sits at ~y=28-58% of the wide image; tagline at top: 20%
   materialises in the moody upper space well above the title. ---- */
.soul-content {
  position: absolute;
  top: 20%;
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  width: 60%;
  text-align: center;
  pointer-events: none;
}
.soul-show-link {
  display: inline-block;
  margin-top: 1rem;
  font-family: 'Exo 2', sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gold);
  text-decoration: none;
  border-bottom: 1px solid rgba(212,172,60,0.35);
  padding-bottom: 2px;
  transition: border-color 0.2s;
}
.soul-show-link:hover,
.soul-show-link:focus {
  border-bottom-color: var(--gold);
  outline: none;
}


/* Slow hypnotic loop — PURE OPACITY only, no position or letter-spacing movement.
   fade in → breathe with warm glow → fade out completely → brief pause → repeat (22s) */
.soul-meta {
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 300;
  font-size: clamp(1.05rem, 2.8vw, 1.55rem);
  color: rgba(255,235,175,0.95);
  letter-spacing: 0.04em;
  margin: 0;
  animation: soul-tagline-cycle 22s linear infinite;
}
@keyframes soul-tagline-cycle {
  /* FADE IN: 0-22% (0-4.8s) — slow opacity climb */
  0%   { opacity: 0;    text-shadow: none; }
  22%  { opacity: 1;    text-shadow: none; }

  /* BREATHE: 22-70% (4.8-15.4s) — two slow swells with warm gold glow */
  30%  { opacity: 0.30; text-shadow: none; }
  42%  { opacity: 1;    text-shadow: 0 0 14px rgba(255,220,100,0.88), 0 0 28px rgba(255,180,50,0.52), 0 0 50px rgba(255,150,20,0.24); }
  52%  { opacity: 0.33; text-shadow: none; }
  63%  { opacity: 1;    text-shadow: 0 0 16px rgba(255,220,100,0.82), 0 0 30px rgba(255,180,50,0.46); }

  /* DISSIPATE: 70-88% (15.4-19.4s) — fade decisively to ZERO */
  70%  { opacity: 1;    text-shadow: none; }
  88%  { opacity: 0;    text-shadow: none; } /* fully gone — linear guarantees true 0 */

  /* PAUSE: 88-100% (19.4-22s) — hold invisible, clean gap before loop */
  100% { opacity: 0;    text-shadow: none; }
}

/* ---- Reduced motion: static image + tagline visible, no animation ---- */
@media (prefers-reduced-motion: reduce) {
  .soul-drift-frame     { animation: none; } /* stops Ken Burns; children freeze with it */
  .soul-win             { transition: none; }
  .soul-smoke-canvas    { display: none; }
  .soul-embers          { display: none; }
  .soul-streetlight     { opacity: 0.82; } /* JS sets steady-on for reduced-motion */
  .soul-sign-glow       { animation: none; opacity: 0.76; }
  .velvet-candle-glow   { animation: none; opacity: 0.80; }
  .velvet-candle-flame  { animation: none; opacity: 0.88; }
  .soul-eq-bar          { animation: none; transform: scaleY(0.55); }
  .soul-meta            { animation: none; opacity: 1; }
}

/* =========================================================
   VELVET HOURS WORLD — static background, structured for
   future layered effects (same pattern as Soul Theory).
   Image: velvet hours chaise.png — 1536×1024 (3:2 ratio).
   "VELVET Hours" title is baked in at lower-left of artwork;
   object-position: left bottom protects it from any cropping
   on narrow screens where min-height overrides the aspect-ratio.
   isolation: isolate pre-wires mix-blend-mode containment for
   any future overlay layers.
   ========================================================= */
.world--velvet-hours {
  position: relative;
  width: 100%;
  aspect-ratio: 1536 / 1024;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.velvet-bg {
  position: absolute;
  inset: 0;
}
.velvet-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Anchor lower-left so "VELVET Hours" title stays visible
     if the image is cropped on very narrow viewports */
  object-position: left bottom;
  display: block;
}

/* ---- Velvet Hours: candle flicker — main votive candle, bottom-right.
   Two layers positioned over the candle at ~left:87%, top:82%:

   1. velvet-candle-glow — the ambient light pool the flame throws.
      Larger oval, very slow and irregular breathing (17 s cycle).
      Radiates down toward the surface as well as up around the flame.

   2. velvet-candle-flame — the flame body itself.
      Smaller, pivots from transform-origin:bottom center (the wick).
      Irregular 7.3 s cycle with asymmetric keyframe stops so it never
      settles into an obvious repeating pulse. Small lateral sways (±2–3 px)
      and scaleX/Y variations mimic a calm real flame in still air.

   Both use mix-blend-mode:screen — blends warmth into the artwork
   without sitting on top of it as an obvious overlay.
   Left values are pre-adjusted so the center of each element lands
   at 87% — avoids stacking translateX(-50%) with animated transforms.
   ---- */

.velvet-candle-glow {
  position: absolute;
  left: 86.25%;     /* 87.5% center − 1.25% (half of 2.5% width) */
  top: 51%;         /* tight halo around the tall slim flame */
  width: 2.5%;
  height: 4.5%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 55%,
    rgba(255, 190, 55, 0.38)  0%,
    rgba(255, 135, 18, 0.16) 45%,
    rgba(180,  62,  0, 0.05) 68%,
    transparent               85%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  animation: velvet-glow-breathe 17s ease-in-out infinite;
}

@keyframes velvet-glow-breathe {
  /* Long irregular breathing — the gap between peaks is never the same */
  0%   { opacity: 0.80; }
  7%   { opacity: 0.90; }
  13%  { opacity: 0.74; }
  21%  { opacity: 0.86; }
  28%  { opacity: 0.92; }
  36%  { opacity: 0.70; }
  42%  { opacity: 0.84; }
  49%  { opacity: 0.90; }
  55%  { opacity: 0.76; }
  62%  { opacity: 0.88; }
  70%  { opacity: 0.94; }
  76%  { opacity: 0.68; }
  83%  { opacity: 0.82; }
  89%  { opacity: 0.78; }
  95%  { opacity: 0.92; }
  100% { opacity: 0.80; }
}

.velvet-candle-flame {
  position: absolute;
  left: 87.05%;     /* 87.5% center − 0.45% (half of 0.9% width) */
  top: 52%;         /* bottom at ~56.5%, flame sits on the wick */
  width: 0.9%;
  height: 4.5%;
  border-radius: 50% 50% 38% 38%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(255, 248, 185, 0.94)  0%,
    rgba(255, 205,  65, 0.66) 36%,
    rgba(255, 130,  12, 0.28) 68%,
    transparent                88%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  transform-origin: bottom center;
  animation: velvet-flame-flicker 7.3s linear infinite;
}

@keyframes velvet-flame-flicker {
  /* Asymmetric stops + values — no two "quiet" stretches are equal length.
     Lateral sways keep the flame feeling alive without being dramatic.
     scaleX widens on a lean, scaleY drops slightly (flame compresses in wind). */
  0%   { opacity: 0.88; transform: scaleX(1.00) scaleY(1.00) translateX(0px);  }
  5%   { opacity: 0.78; transform: scaleX(1.06) scaleY(0.96) translateX(1px);  }
  9%   { opacity: 0.91; transform: scaleX(0.97) scaleY(1.04) translateX(-1px); }
  15%  { opacity: 0.84; transform: scaleX(1.03) scaleY(0.98) translateX(0px);  }
  20%  { opacity: 0.60; transform: scaleX(1.10) scaleY(0.91) translateX(2px);  }
  24%  { opacity: 0.88; transform: scaleX(0.96) scaleY(1.04) translateX(-1px); }
  30%  { opacity: 0.93; transform: scaleX(1.01) scaleY(1.02) translateX(0px);  }
  35%  { opacity: 0.76; transform: scaleX(1.07) scaleY(0.94) translateX(-2px); }
  40%  { opacity: 0.89; transform: scaleX(0.98) scaleY(1.06) translateX(1px);  }
  46%  { opacity: 0.82; transform: scaleX(1.02) scaleY(1.00) translateX(0px);  }
  52%  { opacity: 0.91; transform: scaleX(0.95) scaleY(1.05) translateX(1px);  }
  57%  { opacity: 0.64; transform: scaleX(1.11) scaleY(0.89) translateX(-3px); }
  62%  { opacity: 0.87; transform: scaleX(1.01) scaleY(1.01) translateX(0px);  }
  68%  { opacity: 0.93; transform: scaleX(0.96) scaleY(1.05) translateX(0px);  }
  74%  { opacity: 0.79; transform: scaleX(1.05) scaleY(0.95) translateX(2px);  }
  79%  { opacity: 0.89; transform: scaleX(1.00) scaleY(1.01) translateX(-1px); }
  85%  { opacity: 0.73; transform: scaleX(1.08) scaleY(0.92) translateX(1px);  }
  90%  { opacity: 0.91; transform: scaleX(0.97) scaleY(1.05) translateX(-1px); }
  95%  { opacity: 0.85; transform: scaleX(1.02) scaleY(0.99) translateX(0px);  }
  100% { opacity: 0.88; transform: scaleX(1.00) scaleY(1.00) translateX(0px);  }
}

/* ---------------------------------------------------------
   Island Soul world (1535 × 1024 px artwork)
   --------------------------------------------------------- */
.world--island-soul {
  position: relative;
  width: 100%;
  aspect-ratio: 1535 / 1024;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.island-soul-bg {
  position: absolute;
  inset: 0;
}
.island-soul-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   Vinyl Sundays world (1536 × 1024 px artwork)
   --------------------------------------------------------- */
.world--vinyl-sundays {
  position: relative;
  width: 100%;
  aspect-ratio: 1536 / 1024;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.vinyl-sundays-bg {
  position: absolute;
  inset: 0;
}
.vinyl-sundays-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   Vinyl Sundays — animation overlays

   Approach: ALL overlays use inset:0 (full section coverage).
   No border-radius = no visible circular boundary.
   Groove overlay rotates around record centre via transform-origin.
   Radial gradients are self-clipping — edges fade to transparent.

   Spindle centre in image: x=46.9%, y=43.0%  (remeasured)
   --------------------------------------------------------- */

/* 1. Groove rotation overlay — covers full section, rotates around
      record centre. No border-radius = no visible ellipse edge.   */
.vs-record-spin-clip {
  /* Clips sweep to record area only: inset removes VU/knob panel (top 40%).
     Inner element ellipse handles the disc boundary.
     Tonearm contact at one point in cycle accepted as known minor imperfection. */
  position: absolute;
  inset: 0;
  clip-path: inset(40% 0 0 0);
  pointer-events: none;
}

.vs-record-spin {
  position: absolute;
  inset: 0;
  transform-origin: 46.9% 43.0%;   /* pivot = spindle centre */
  /* STATIC scaleY only — no rotation. Gradient angle is animated via
     @property --sweep-angle, so the clip-path stays fixed in world space.
     If transform rotated, the polygon clip would spin with it — wrong.    */
  transform: scaleY(0.64);
  animation: vs-sweep 1.8s linear infinite;
  /* Polygon clip in LOCAL space (before scaleY). After scaleY(0.64) it maps
     to world space following the album sleeve's lower diagonal boundary,
     with a 57px safety margin. Diagonal cutoff: world (0,485)→(676,158).
     Sleeve boundary estimate: y_world ≈ 428 - 0.483×x.
     Cutoff + 57px margin: y_local = 510 - 0.755×x_local.             */
  /* Clip to the record's verified outer ellipse (rx=789px, ry=506px,
     measured via 7-point least-squares fit at v250/v251).
     
     In LOCAL space (before scaleY), ry must be divided by 0.64 so that
     after scaleY(0.64) the world ry equals the measured 506px:
       rx_local = 789 / 1536        = 51.37%  (no x-scaling → same in world)
       ry_local = (506 / 0.64) / 1024 = 77.21%  → world ry = ×0.64 = 506px ✓
     
     Element transform is STATIC scaleY(0.64) only (no rotation since v256),
     so this ellipse stays fixed in world space while --sweep-angle animates.
     Anything outside the record's true boundary is excluded automatically —
     no polygon notch-carving around sleeve / VU meters / knobs / tonearm. */
  clip-path: ellipse(44.00% 73.35% at 46.9% 43.0%);  /* rx 44% ≈ 675px — sits inside platter rim */
  /* Soft light sweep — illuminates photographed grooves.
     from var(--sweep-angle) rotates the arc while the clip stays fixed. */
  /* Soft light sweep — illuminates photographed grooves.
     v259: peak opacity raised 0.018 → 0.035 (×1.94), shape unchanged.
     Arc width 150°, boundary polygon, timing all untouched. */
  background: conic-gradient(
    from var(--sweep-angle) at 46.9% 43.0%,
    transparent                    0deg,
    rgba(255,215,100, 0.004)       6deg,
    rgba(255,215,100, 0.027)      20deg,
    rgba(255,215,100, 0.035)      30deg,
    rgba(255,215,100, 0.035)      40deg,
    rgba(255,215,100, 0.027)      50deg,
    rgba(255,215,100, 0.004)      64deg,
    transparent                   70deg,
    transparent                  360deg
  );
  mix-blend-mode: screen;
  pointer-events: none;
}

/* @keyframes vs-spin removed — transform is now static scaleY(0.64).
   The sweep is animated via CSS @property on the conic gradient from-angle. */
@property --sweep-angle {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

@keyframes vs-sweep {
  from { --sweep-angle: 0deg;   }
  to   { --sweep-angle: 360deg; }
}

/* 2. Fixed warm sunlight on the vinyl surface — does NOT rotate.
      Centred on upper-left of record (where room light strikes).  */
.vs-light-on-record {
  position: absolute;
  inset: 0;
  /* Window light enters upper-left — warm golden cast on grooves there */
  background: radial-gradient(
    circle at 30% 26%,
    rgba(255,218,128,0.22)  0%,
    rgba(255,188,72, 0.10)  14%,
    rgba(255,155,38, 0.038) 24%,
    transparent             34%
  );
  mix-blend-mode: screen;
  pointer-events: none;
}

/* .vs-groove-light removed — sweep approach uses photo's own lighting */

/* 3. Ambient room brightness — slow warm pulse, ~6% swing */
.vs-ambient-breathe {
  position: absolute;
  inset: 0;
  background: rgba(255,198,95,0.020);
  animation: vs-breathe 9s ease-in-out infinite;
  mix-blend-mode: screen;
  pointer-events: none;
}

@keyframes vs-breathe {
  0%   { opacity: 0.30; }
  48%  { opacity: 1.00; }
  100% { opacity: 0.30; }
}

/* 4. Canvas — full section; dust particles + spindle glint */
.vs-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* 5. Weekly album sleeve — hidden until dated file found.
   Position targets the linen/burlap sleeve panel in the photo
   (1536x1024 photo: panel top~5%, left~3.5%, width~25%, height~40%).
   z-index:1 keeps it below .vs-title z-index:5. */
.vs-album-sleeve {
  /* Hidden — sleeve position TBD when resuming with real album art.
     Re-enable and calibrate left/top/width/clip-path in a future session. */
  display: none;
}
.vs-album-sleeve img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  opacity: 0;
}

/* 6. VU meter needles — HIDDEN until pixel positions are measured.
      Will be calibrated in a follow-up pass.                      */
.vs-vu-left,
.vs-vu-right {
  display: none;
}

/* Reduced motion */
@media (prefers-reduced-motion: reduce) {
  .vs-record-spin,
  .vs-ambient-breathe { animation: none; }
}


/* ---------------------------------------------------------
   Vinyl Sundays — World Title
   VINYL: Cormorant Display 300, champagne-gold metallic
   SUNDAYS: Playfair Display Italic, dusty-lavender satin foil
   Only the light animates (gradient shift). Letters are
   static after the initial fade-in.
   Sync: both material-shift keyframes run on the same 9s
   cycle as vs-breathe so they pulse with the room light.
   --------------------------------------------------------- */
.vs-title {
  position: absolute;
  top: 5%;
  left: 4%;
  line-height: 1.2;       /* breathing room for script ascenders */
  overflow: visible;      /* never clip child glyphs at container edge */
  pointer-events: none;
  user-select: none;
  z-index: 5;
}

.vs-title__vinyl {
  display: block;
  font-family: 'Cormorant Display', 'Cormorant Garamond', Georgia, serif;
  font-weight: 300;
  -webkit-text-fill-color: red !important; /* v315 CSS-LOAD TEST — remove after confirming */
  font-size: clamp(1.6rem, 3.6vw, 4.2rem);
  letter-spacing: 0.30em;
  text-transform: uppercase;
  /* Champagne gold — aged brass highlight sweeps left→right at breathe peak */
  background: linear-gradient(
    110deg,
    #7a5a14  0%,
    #c08a2e 18%,
    #eece6a 36%,
    #fae89a 50%,
    #c8943a 68%,
    #7a5a14 100%
  );
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 2px 10px rgba(6,3,0,0.62))    /* legibility: dark halo against light bg */
          drop-shadow(0 1px 1px rgba(255,240,160,0.22))  /* warm top-edge highlight */
          drop-shadow(0 -1px 0   rgba(50,30,5,0.45));    /* dark bottom-edge emboss */
  opacity: 0;
  animation:
    vs-title-fade   1s  ease         forwards,
    vs-gold-shift   9s  ease-in-out  infinite;
  animation-delay: 0.8s, 0.8s;
}

.vs-title__sundays {
  display: block;
  font-family: 'Playfair Display', Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: clamp(2.0rem, 4.4vw, 5.0rem);
  letter-spacing: 0.04em;
  /* Serif italic needs only modest padding (no extreme script ascenders) */
  padding: 0.12em 0.05em 0.08em 0.05em;
  margin-top: -0.15em;
  /* Dusty lavender satin — slight warmth so it reads under amber room light */
  background: linear-gradient(
    110deg,
    #5e3f6e  0%,
    #9470aa 22%,
    #cfa8de 42%,
    #e0ccea 52%,
    #a07aba 72%,
    #5e3f6e 100%
  );
  background-size: 300% 100%;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  filter: drop-shadow(0 2px 10px rgba(6,3,0,0.62))     /* legibility: dark halo against light bg */
          drop-shadow(0 1px 1px rgba(190,155,215,0.20))  /* lavender top-edge highlight */
          drop-shadow(0 -1px 0   rgba(40,20,55,0.40));   /* dark bottom-edge emboss */
  opacity: 0;
  animation:
    vs-title-fade      1s  ease         forwards,
    vs-lavender-shift  9s  ease-in-out  infinite;
  animation-delay: 0.9s, 0.9s;
}

@keyframes vs-title-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Both shift keyframes are identical — same 9s breathe rhythm.
   Kept separate so either can be tuned independently later. */
@keyframes vs-gold-shift {
  0%   { background-position: 100% 50%; }
  48%  { background-position:   0% 50%; }
  100% { background-position: 100% 50%; }
}

@keyframes vs-lavender-shift {
  0%   { background-position: 100% 50%; }
  48%  { background-position:   0% 50%; }
  100% { background-position: 100% 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .vs-title__vinyl,
  .vs-title__sundays {
    animation: none;
    opacity: 1;
    background-position: 50% 50%;
  }
}

/* ---------------------------------------------------------
   Lo-Fi Mornings world (1672 × 941 px artwork)
   --------------------------------------------------------- */
.world--lofi-mornings {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.lofi-bg {
  position: absolute;
  inset: 0;
}
.lofi-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   Lo-Fi Mornings — coffee steam
   The mug already has a static steam wisp baked into the photo,
   rising from the coffee surface inside the rim (measured at
   left:45.75%, top:73.8% of the 1672x941 artwork) and curling
   slightly right as it goes. Four wisps rise from that same point
   on a staggered loop for continuous coverage (thicker column,
   never a gap between one wisp fading and the next starting).
   IMPORTANT: "top" here is the box's TOP edge, and the box is 9%
   tall — so top must sit a full box-height ABOVE the measured rim
   line (73.8% - 9% = ~64.8%), otherwise the box hangs mostly BELOW
   the coffee surface and the wisp renders over the mug's opaque
   outer wall instead of rising out of the opening. (Fixed after
   the wisps were reported floating outside/below the cup.)
   The rise keyframes swirl left/right in an S-curve (via alternating
   translateX + rotate at each step) and morph border-radius through
   asymmetric values each step, so the blob twists/curls the way real
   smoke does rather than just scaling up in place. Opacity stays
   high through the first ~60% of the loop and only tapers at the
   very top, so it reads as billowing motion rather than a quick
   dissipate. mix-blend-mode: normal (not screen — screen was proven
   invisible against bright pixels earlier this session, and normal
   composites reliably regardless of what's behind it, same reasoning
   as the car lights and sconces). left:0/top:0/100%x100% on the wrap
   is the pre-JS fallback; js/lofi-mornings.js (fitOverlay) sizes it
   to the photo's actual cover-crop box once it can measure the
   parent, same cover-crop-aware pattern used everywhere else on
   this site.
--------------------------------------------------------- */
.lofi-steam-wrap {
  position: absolute;
  left: 0; top: 0; width: 100%; height: 100%;
  pointer-events: none;
  z-index: 1;
}
/* Motion technique duplicated from Faith & Frequency's candle
   steam (.ff-wisp / @keyframes ff-steam-rise): fixed-size puff,
   NO scale/rotate/border-radius morphing at all — the "growing
   softer as it disperses" look comes only from blur increasing
   over the loop (3px -> 10.5px), plus a small one-way opacity fade
   and a gentle single wobble in translateX. That combination reads
   as a natural whisp of smoke; the earlier rotate + scale + morphing
   border-radius version read as jagged because it kept reversing
   direction and snapping shape every keyframe. animation-delay is
   negative on 3 of the 4 wisps (Faith & Frequency's trick) so every
   wisp is already mid-loop on page load instead of sitting invisible
   through its own start delay. */
.lofi-steam-wisp {
  position: absolute;
  left: 45.75%;
  top: 66.3%;
  width: 2.5%;
  height: 7.5%;
  pointer-events: none;
  mix-blend-mode: normal;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(255, 251, 243, 0.82) 0%,
    rgba(255, 250, 240, 0.44) 48%,
    transparent               76%
  );
  filter: blur(2.8px);
  opacity: 0;
  animation-name: lofi-steam-rise;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
.lofi-steam-wisp--1 { left: 44.6%; animation-duration: 7.8s; animation-delay: 0s;    }
.lofi-steam-wisp--2 { left: 45.9%; animation-duration: 6.6s; animation-delay: -2.6s; }
.lofi-steam-wisp--3 { left: 47.0%; animation-duration: 8.4s; animation-delay: -4.8s; }
.lofi-steam-wisp--4 { left: 45.2%; animation-duration: 7.2s; animation-delay: -1.4s; }
@keyframes lofi-steam-rise {
  0%   { opacity: 0;    transform: translate(-50%, 0%);    filter: blur(2.8px); }
  12%  { opacity: 0.72; transform: translate(-58%, -14%);  filter: blur(3.2px); }
  44%  { opacity: 0.44; transform: translate(-42%, -46%);  filter: blur(5px);   }
  74%  { opacity: 0.2;  transform: translate(-56%, -76%);  filter: blur(7.5px); }
  100% { opacity: 0;    transform: translate(-50%, -106%); filter: blur(10px);  }
}

/* ---------------------------------------------------------
   Lo-Fi Mornings — incense smoke (thin wisps from the incense
   stick tip, left of the mug). Same lofi-steam-rise keyframe as
   the coffee steam, just smaller/thinner and a cooler, more
   neutral tint so it reads as a separate, thinner smoke source.
   Tip measured at left:38.9%, top:65.6% of the 1672x941 artwork.
--------------------------------------------------------- */
.lofi-incense-wisp {
  position: absolute;
  left: 38.9%;
  top: 60.4%;
  width: 1.4%;
  height: 5.2%;
  pointer-events: none;
  mix-blend-mode: normal;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(232, 228, 225, 0.6)  0%,
    rgba(232, 228, 225, 0.28) 48%,
    transparent               74%
  );
  filter: blur(2.5px);
  opacity: 0;
  animation-name: lofi-steam-rise;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
}
.lofi-incense-wisp--1 { animation-duration: 6.4s; animation-delay: 0s;    }
.lofi-incense-wisp--2 { animation-duration: 7.6s; animation-delay: -3.1s; }

/* ---------------------------------------------------------
   Lo-Fi Mornings — candle flicker (glass holder, bottom-left).
   Technique duplicated from Faith & Frequency's candle-flame /
   candle-glow (see .ff-candle-glow / .ff-candle-flame): identical
   two-layer glow + flame keyframes, just renamed and repositioned
   to this photo's single flame. Flame measured at left:11.66%,
   top:91.2% of the 1672x941 artwork.
--------------------------------------------------------- */
/* Candle rebuilt after the wrap-parenting fix still read as invisible:
   the baked-in photo flame is already a near-white-hot core, and
   mix-blend-mode:screen can only ADD light — an overlay flickering
   at similar brightness on top of already-near-white pixels has
   almost no visible headroom to get brighter, so the flicker was
   real but imperceptible. Fix: push most of the visible flicker
   into the GLOW halo instead, which sits over dark glass/table
   pixels with lots of headroom for screen-blended light to read
   clearly, sped up from a slow 17s breathe to a fast, irregular
   4.4s flicker so it actually looks like firelight. The flame body
   keeps a wider scale/opacity swing plus a touch of translateY
   jitter so its tip visibly dances too. */
.lofi-candle-glow {
  position: absolute;
  left: 11.66%;
  top: 91.2%;
  transform: translate(-50%, -50%);
  width: 4.4%;
  height: 7.6%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 55%,
    rgba(255, 195, 60,  0.58) 0%,
    rgba(255, 140, 20,  0.28) 42%,
    rgba(180,  62,  0,  0.10) 66%,
    transparent                85%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  animation: lofi-candle-glow-flicker 4.4s ease-in-out infinite;
}
.lofi-candle-flame {
  position: absolute;
  left: 11.66%;
  top: 91.2%;
  margin-left: -0.75%;              /* = -(width/2): centres on left */
  margin-top:  -1.29%;              /* = -(height/2 × H/W): %h→%w unit fix */
  width: 1.5%;
  height: 4.6%;
  border-radius: 50% 50% 38% 38%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(255, 250, 200,  0.98)  0%,
    rgba(255, 210,  70,  0.74) 36%,
    rgba(255, 135,  15,  0.32) 68%,
    transparent                 88%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  transform-origin: bottom center;
  animation: lofi-candle-flame-flicker 5.5s linear infinite;
}
@keyframes lofi-candle-glow-flicker {
  0%   { opacity: 0.9;  transform: translate(-50%, -50%) scale(1.0);  }
  8%   { opacity: 0.55; transform: translate(-50%, -50%) scale(0.9);  }
  15%  { opacity: 1.0;  transform: translate(-50%, -50%) scale(1.08); }
  23%  { opacity: 0.68; transform: translate(-50%, -50%) scale(0.94); }
  30%  { opacity: 0.97; transform: translate(-50%, -50%) scale(1.05); }
  38%  { opacity: 0.6;  transform: translate(-50%, -50%) scale(0.92); }
  46%  { opacity: 0.9;  transform: translate(-50%, -50%) scale(1.02); }
  55%  { opacity: 0.5;  transform: translate(-50%, -50%) scale(0.88); }
  63%  { opacity: 0.95; transform: translate(-50%, -50%) scale(1.06); }
  71%  { opacity: 0.65; transform: translate(-50%, -50%) scale(0.93); }
  80%  { opacity: 1.0;  transform: translate(-50%, -50%) scale(1.1);  }
  88%  { opacity: 0.58; transform: translate(-50%, -50%) scale(0.9);  }
  100% { opacity: 0.9;  transform: translate(-50%, -50%) scale(1.0);  }
}
@keyframes lofi-candle-flame-flicker {
  0%   { opacity: 0.92; transform: scaleX(1.00) scaleY(1.00) translate(0px, 0px);   }
  5%   { opacity: 0.7;  transform: scaleX(1.12) scaleY(0.88) translate(1px, 1px);   }
  9%   { opacity: 0.97; transform: scaleX(0.9)  scaleY(1.12) translate(-1px, -1px); }
  15%  { opacity: 0.82; transform: scaleX(1.06) scaleY(0.94) translate(0px, 0px);   }
  20%  { opacity: 0.45; transform: scaleX(1.2)  scaleY(0.8)  translate(2px, 1px);   }
  24%  { opacity: 0.92; transform: scaleX(0.92) scaleY(1.1)  translate(-1px, -1px); }
  30%  { opacity: 1.0;  transform: scaleX(1.02) scaleY(1.04) translate(0px, -1px);  }
  35%  { opacity: 0.66; transform: scaleX(1.14) scaleY(0.86) translate(-2px, 1px);  }
  40%  { opacity: 0.94; transform: scaleX(0.95) scaleY(1.13) translate(1px, -1px);  }
  46%  { opacity: 0.78; transform: scaleX(1.04) scaleY(1.00) translate(0px, 0px);   }
  52%  { opacity: 0.96; transform: scaleX(0.89) scaleY(1.1)  translate(1px, -1px);  }
  57%  { opacity: 0.5;  transform: scaleX(1.22) scaleY(0.78) translate(-3px, 1px);  }
  62%  { opacity: 0.9;  transform: scaleX(1.02) scaleY(1.02) translate(0px, 0px);   }
  68%  { opacity: 1.0;  transform: scaleX(0.92) scaleY(1.1)  translate(0px, -1px);  }
  74%  { opacity: 0.72; transform: scaleX(1.1)  scaleY(0.9)  translate(2px, 1px);   }
  79%  { opacity: 0.93; transform: scaleX(1.00) scaleY(1.02) translate(-1px, 0px);  }
  85%  { opacity: 0.6;  transform: scaleX(1.16) scaleY(0.84) translate(1px, 1px);   }
  90%  { opacity: 0.96; transform: scaleX(0.94) scaleY(1.1)  translate(-1px, -1px); }
  95%  { opacity: 0.84; transform: scaleX(1.04) scaleY(0.98) translate(0px, 0px);   }
  100% { opacity: 0.92; transform: scaleX(1.00) scaleY(1.00) translate(0px, 0px);   }
}

/* ---------------------------------------------------------
   Lo-Fi Mornings — deck power LED pulse only. The record "spin"
   glint was removed per explicit feedback after several rounds
   (kept ending up shaped/angled wrong — "standing up, laying down,
   rotating... going all over the place") — not worth the fragility
   for a background ambient element. Just the LED remains.
   LED measured at left:76.3%, top:86.7% (fraction of the 1672x941
   artwork). Lives inside .lofi-steam-wrap so fitOverlay corrects
   its position the same way it does the steam.
--------------------------------------------------------- */
.lofi-deck-led {
  position: absolute;
  left: 76.3%;
  top: 86.7%;
  transform: translate(-50%, -50%);
  width: 0.55%;
  height: 1.0%;
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
    rgba(255, 90, 60, 0.95) 0%,
    rgba(255, 60, 30, 0.5) 55%,
    transparent             80%
  );
  filter: blur(1.5px);
  mix-blend-mode: screen;
  pointer-events: none;
  z-index: 2;
  animation: lofi-deck-led-pulse 3.6s ease-in-out infinite;
}
@keyframes lofi-deck-led-pulse {
  0%   { opacity: 0.55; }
  50%  { opacity: 1;    }
  100% { opacity: 0.55; }
}

/* ---------------------------------------------------------
   Soothe Sessions world (1672 × 941 px artwork)
   --------------------------------------------------------- */
.world--soothe-sessions {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.soothe-bg {
  position: absolute;
  inset: 0;
}
.soothe-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
  transform: scale(1.08);
  transform-origin: center center;
  transition: transform 0.25s ease-out;
  will-change: transform;
}

/* ── Soothe Sessions: drifting mist layers ─────────────────────────── */
.soothe-mist {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: hidden;
  z-index: 1;
  transition: transform 0.25s ease-out;
  will-change: transform;
}

.soothe-mist__layer {
  position: absolute;
  border-radius: 50%;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-direction: alternate;
}

/* Layer 1 — wide low fog bank, drifts right */
/* LEFT FLANK — hugs left treeline, center stays clear */
.soothe-mist__layer--1 {
  width: 34%; height: 13%;
  top: 53%; left: -5%;
  background: radial-gradient(ellipse, rgba(225,238,252,0.40) 0%, rgba(205,225,248,0.16) 55%, transparent 78%);
  filter: blur(32px);
  animation-name: soothe-mist-r;
  animation-duration: 68s;
  animation-delay: 0s;
}
/* LEFT FLANK lower — drifts right toward treeline */
.soothe-mist__layer--2 {
  width: 26%; height: 11%;
  top: 60%; left: 1%;
  background: radial-gradient(ellipse, rgba(230,240,255,0.38) 0%, rgba(210,228,250,0.15) 52%, transparent 76%);
  filter: blur(28px);
  animation-name: soothe-mist-r;
  animation-duration: 82s;
  animation-delay: -22s;
}
/* LEFT FLANK upper tendril */
.soothe-mist__layer--3 {
  width: 22%; height: 10%;
  top: 49%; left: -2%;
  background: radial-gradient(ellipse, rgba(218,235,252,0.34) 0%, rgba(198,220,248,0.12) 58%, transparent 80%);
  filter: blur(24px);
  animation-name: soothe-mist-l;
  animation-duration: 55s;
  animation-delay: -38s;
}
/* RIGHT FLANK — hugs right treeline, center stays clear */
.soothe-mist__layer--4 {
  width: 34%; height: 13%;
  top: 53%; left: 71%;
  background: radial-gradient(ellipse, rgba(225,238,252,0.40) 0%, rgba(205,225,248,0.16) 55%, transparent 78%);
  filter: blur(32px);
  animation-name: soothe-mist-l;
  animation-duration: 74s;
  animation-delay: -14s;
}
/* RIGHT FLANK lower */
.soothe-mist__layer--5 {
  width: 26%; height: 11%;
  top: 60%; left: 74%;
  background: radial-gradient(ellipse, rgba(230,240,255,0.38) 0%, rgba(210,228,250,0.15) 52%, transparent 76%);
  filter: blur(28px);
  animation-name: soothe-mist-l;
  animation-duration: 60s;
  animation-delay: -46s;
}

@keyframes soothe-mist-r {
  from { transform: translateX(0); }
  to   { transform: translateX(10%); }
}
@keyframes soothe-mist-l {
  from { transform: translateX(0); }
  to   { transform: translateX(-10%); }
}

/* ── Soothe Sessions: star + firefly canvas overlay ─────────────────── */
.soothe-stars {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 2;
  transition: transform 0.25s ease-out;
  will-change: transform;
}

/* ---------------------------------------------------------
   Still Mind world (1672 × 941 px artwork)
   --------------------------------------------------------- */
.world--still-mind {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.stillmind-bg {
  position: absolute;
  inset: 0;
}
.stillmind-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

.world--faith-and-frequency {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.faith-freq-bg {
  position: absolute;
  inset: 0;
}
.faith-freq-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}


/* ---------------------------------------------------------
   Faith & Frequency — Atmospheric Overlays (Pass 1)
   All effects gated to .ff-active added by faith-freq.js
   on world activation. No animations run before then.

   Image: Faith_and_frequency_wide.png  1672 × 941 px
   Key positions (percentage of section dimensions):
     Fireplace flames:  left ~88 %, top ~53 %   (image-pixel centroid)
     Candle flame:      left ~60 %, top ~64 %   (image-pixel centroid)
     Cup / steam:       left ~ 5 %, bottom ~20 %
   --------------------------------------------------------- */

/* Cloud drift — seamless atmospheric layer (v185).
   The painted background image cannot be tiled horizontally (left/right
   edge colour mismatch up to RGB diff 329 at mid-sky), so moving the image
   itself would produce a hard visible seam.  Instead this 200%-wide overlay
   carries soft radial-gradient cloud shapes.  Patterns at 0–50% and 50–100%
   of the element are identical, so translateX(-50%) loops invisibly.
   Element opacity is 0 in CSS; JS fades it in at ~35 s after world opens
   (once the full typography sequence has resolved).  No .ff-active auto-
   start rule — timing is JS-only so the sky stays still during the text. */
/* Cloud drift — final approach: mix-blend-mode:soft-light modulates the EXISTING
   painted pixels rather than layering fake shapes on top. A horizontal gradient
   (50% grey = neutral, lighter = brightens, darker = darkens) drifts right-to-left
   so the real painted cloud texture shifts in light — as if cloud density is slowly
   changing. #808080 at 0%/50%/100% ensures a perfectly seamless loop.
   Element opacity 0.35 via JS (~35% blend strength) makes the shift visible
   without overwhelming the painted scene. */
.ff-cloud-overlay {
  position: absolute;
  left: 0; top: 0;
  width: 200%; height: 38%;   /* sky area only; room interior starts ~40% down */
  pointer-events: none;
  z-index: 1;
  opacity: 0;
  mix-blend-mode: soft-light;
  /* 50% grey (#808080) = neutral in soft-light; lighter brightens, darker darkens.
     Positions 0%, 50%, 100% all equal #808080 — seamless horizontal loop. */
  background: linear-gradient(to right,
    #808080  0%,    /* neutral */
    #c0d0e4  14%,   /* light peak — brightens painted sky ~+12 RGB units */
    #808080  26%,   /* neutral */
    #2e3a4a  42%,   /* shadow trough — darkens painted sky ~−10 RGB units */
    #808080  50%,   /* neutral — same as 0%, seamless wrap */
    #c0d0e4  64%,
    #808080  76%,
    #2e3a4a  92%,
    #808080 100%
  );
  animation: ff-cloud-drift 160s linear infinite;
  animation-play-state: paused;
}
@keyframes ff-cloud-drift {
  from { transform: translateX(0);    }
  to   { transform: translateX(-50%); }
}

/* City lights — dark dots placed on real lit-window pixels.
   #151e28 = mean unlit-window colour (measured from image, RGB 21,30,40).
   transform: translate(-50%,-50%) centres each dot on its pixel position.
   Opacity and fades are entirely JS-controlled (faith-freq.js startCityLights). */
/* City section blobs — near-black soft ellipses, gaussian-like falloff.
   8-stop radial-gradient approximates a gaussian vignette:
   near-black centre, alpha drops smoothly to 0 at 88% radius.
   At element opacity 0.90, effective centre alpha ≈ 0.72 (clearly dark);
   edge alpha at 76% radius ≈ 0.03×0.90 = invisible — no visible outline.
   transform: translate(-50%,-50%) keeps each blob centred on its left/top. */
.ff-citysec {
  position: absolute;
  pointer-events: none;
  opacity: 0;
  z-index: 5;
  transform: translate(-50%, -50%);
  background: radial-gradient(ellipse at center,
    rgba(0, 0, 2, 0.80)  0%,
    rgba(0, 0, 2, 0.70) 10%,
    rgba(0, 0, 2, 0.55) 22%,
    rgba(0, 0, 2, 0.38) 36%,
    rgba(0, 0, 2, 0.22) 50%,
    rgba(0, 0, 2, 0.10) 64%,
    rgba(0, 0, 2, 0.03) 76%,
    rgba(0, 0, 2, 0)    88%
  );
}

/* Fireplace — multi-flame spread across the hearth.
   Pixel-centroid analysis (v164) at ≥25% warmth threshold:
     Main fire body: x 81.16%–90.31%, grate y=56.85% from top (43.15% from bottom)
     3 flame peaks (by strength): x=84.63%, 87.92% (dominant ×4), 89.53%
   Each peak has its own keyframe (ff-fire-lick-b/c/d) — flames never sync.

   MOTION DESIGN:
     skewX is the HERO (±12–16°): flames LEAN and SWAY like real fire licking.
     scaleY is secondary (0.88–1.14): subtle height variation, not vertical bounce.
     translateX mirrors skewX direction: lean + drift together.
     Crackle = brief opacity dip (0.40–0.50) during hard leans.
     Flare = skewX near 0° + scaleY up (center surge).
     4 distinct patterns + 4 durations (3.7–5.3 s) + staggered delays → never in unison.

   mix-blend-mode:screen on every element.
   border-radius: tapered top (flame tip), wider base — real flame silhouette.
   transform-origin: bottom center — tip dances, base stays grounded. */
.ff-fire-glow {
  position: absolute;
  left: 87.4%;
  top: 53.0%;
  transform: translate(-50%, -50%);
  width: 16%;
  height: 12%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 62%,
    rgba(255, 160, 30,  0.32)  0%,
    rgba(255, 100, 10,  0.14) 42%,
    rgba(200,  55,  0,  0.05) 65%,
    transparent                82%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  animation: ff-fire-glow-breathe 12s ease-in-out infinite;
  animation-play-state: paused;
}
/* Shared base for all 4 hearth flame tongues */
.ff-fire-flame {
  position: absolute;
  bottom: 43.15%;             /* grate — CSS bottom:% = % of parent HEIGHT ✓ */
  /* Taper: narrow/pointed top (tip), wide/flat bottom (base) */
  border-radius: 50% 50% 35% 35% / 78% 78% 22% 22%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(255, 238, 160, 0.88)  0%,
    rgba(255, 185,  45, 0.58) 38%,
    rgba(255, 110,  10, 0.20) 65%,
    transparent               82%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  transform-origin: bottom center;
  animation-play-state: paused;
}
/* Flame 2 — x=84.63% moderate (strength 2896) */
.ff-fire-flame--2 {
  left: 84.63%; margin-left: -0.35%; width: 0.7%; height: 2.5%;
  animation: ff-fire-lick-b 5.3s linear infinite -0.6s;
}
/* Flame 3 — x=87.92% dominant (strength 9986 — 4× peak) */
.ff-fire-flame--3 {
  left: 87.92%; margin-left: -0.50%; width: 1.0%; height: 6.5%;
  animation: ff-fire-lick-c 3.7s linear infinite -2.4s;
}
/* Flame 4 — x=89.53% strong right (strength 8365) */
.ff-fire-flame--4 {
  left: 89.53%; margin-left: -0.45%; width: 0.9%; height: 4.5%;
  animation: ff-fire-lick-d 4.8s linear infinite -1.1s;
}
.world--faith-and-frequency.ff-active .ff-fire-glow,
.world--faith-and-frequency.ff-active .ff-fire-flame {
  animation-play-state: running;
}
@keyframes ff-fire-glow-breathe {
  /* Fireplace ambient glow — wide brightness swings simulate crackle.
     Opacity 0.52–1.00; faster 12 s cycle for flare/settle feel. */
  0%   { opacity: 0.62; }
  3%   { opacity: 0.95; }
  7%   { opacity: 0.58; }
  11%  { opacity: 1.00; }
  15%  { opacity: 0.65; }
  19%  { opacity: 0.88; }
  23%  { opacity: 0.52; }
  27%  { opacity: 0.94; }
  31%  { opacity: 0.70; }
  36%  { opacity: 0.98; }
  40%  { opacity: 0.56; }
  44%  { opacity: 0.86; }
  48%  { opacity: 0.64; }
  52%  { opacity: 1.00; }
  56%  { opacity: 0.60; }
  61%  { opacity: 0.90; }
  65%  { opacity: 0.55; }
  69%  { opacity: 0.96; }
  73%  { opacity: 0.68; }
  78%  { opacity: 0.85; }
  82%  { opacity: 0.53; }
  86%  { opacity: 0.92; }
  90%  { opacity: 0.74; }
  94%  { opacity: 0.98; }
  97%  { opacity: 0.60; }
  100% { opacity: 0.62; }
}

/* ---------------------------------------------------------------
   HEARTH FLAME KEYFRAMES  — 4 independent patterns.
   skewX = lean/sway (HERO).  scaleY = height pulse (secondary).
   translateX follows skewX direction.  Crackle = opacity dip.
   No two flames share a keyframe → permanently out of phase.
--------------------------------------------------------------- */
@keyframes ff-fire-lick-b {
  /* Left-biased slow undulation — longer holds, smoother rolling lean */
  0%   { opacity: 0.86; transform: skewX(0deg)   scaleY(1.00) translateX(0px);  }
  5%   { opacity: 0.80; transform: skewX(-9deg)  scaleY(0.98) translateX(-3px); }
  10%  { opacity: 0.92; transform: skewX(-14deg) scaleY(1.03) translateX(-5px); }
  15%  { opacity: 0.86; transform: skewX(-7deg)  scaleY(1.08) translateX(-2px); }
  19%  { opacity: 0.94; transform: skewX(2deg)   scaleY(1.12) translateX(1px);  }
  24%  { opacity: 0.50; transform: skewX(12deg)  scaleY(0.90) translateX(4px);  }
  28%  { opacity: 0.84; transform: skewX(6deg)   scaleY(0.96) translateX(2px);  }
  33%  { opacity: 0.90; transform: skewX(-4deg)  scaleY(1.05) translateX(-1px); }
  38%  { opacity: 0.78; transform: skewX(-13deg) scaleY(0.94) translateX(-4px); }
  43%  { opacity: 0.88; transform: skewX(-16deg) scaleY(1.00) translateX(-5px); }
  47%  { opacity: 0.46; transform: skewX(-10deg) scaleY(0.92) translateX(-3px); }
  52%  { opacity: 0.92; transform: skewX(1deg)   scaleY(1.10) translateX(0px);  }
  57%  { opacity: 0.84; transform: skewX(10deg)  scaleY(0.97) translateX(3px);  }
  62%  { opacity: 0.88; transform: skewX(4deg)   scaleY(1.04) translateX(1px);  }
  66%  { opacity: 0.78; transform: skewX(-6deg)  scaleY(0.99) translateX(-2px); }
  71%  { opacity: 0.92; transform: skewX(-12deg) scaleY(1.02) translateX(-4px); }
  76%  { opacity: 0.42; transform: skewX(-8deg)  scaleY(0.91) translateX(-3px); }
  80%  { opacity: 0.86; transform: skewX(3deg)   scaleY(1.11) translateX(1px);  }
  85%  { opacity: 0.80; transform: skewX(11deg)  scaleY(0.96) translateX(4px);  }
  89%  { opacity: 0.90; transform: skewX(5deg)   scaleY(1.03) translateX(2px);  }
  94%  { opacity: 0.84; transform: skewX(-3deg)  scaleY(1.06) translateX(-1px); }
  100% { opacity: 0.86; transform: skewX(0deg)   scaleY(1.00) translateX(0px);  }
}
@keyframes ff-fire-lick-c {
  /* Dominant center flame — biggest sways (±16°), frequent crackle, fastest */
  0%   { opacity: 0.88; transform: skewX(0deg)   scaleY(1.00) translateX(0px);  }
  3%   { opacity: 0.92; transform: skewX(12deg)  scaleY(0.94) translateX(4px);  }
  6%   { opacity: 0.40; transform: skewX(16deg)  scaleY(0.88) translateX(6px);  }
  10%  { opacity: 0.90; transform: skewX(4deg)   scaleY(1.14) translateX(1px);  }
  14%  { opacity: 0.84; transform: skewX(-10deg) scaleY(0.96) translateX(-4px); }
  18%  { opacity: 0.92; transform: skewX(-15deg) scaleY(1.02) translateX(-5px); }
  22%  { opacity: 0.48; transform: skewX(-8deg)  scaleY(0.91) translateX(-3px); }
  26%  { opacity: 0.88; transform: skewX(2deg)   scaleY(1.10) translateX(1px);  }
  30%  { opacity: 0.82; transform: skewX(14deg)  scaleY(0.92) translateX(5px);  }
  34%  { opacity: 0.40; transform: skewX(11deg)  scaleY(0.90) translateX(4px);  }
  38%  { opacity: 0.94; transform: skewX(0deg)   scaleY(1.12) translateX(0px);  }
  42%  { opacity: 0.80; transform: skewX(-12deg) scaleY(0.95) translateX(-4px); }
  46%  { opacity: 0.90; transform: skewX(-16deg) scaleY(1.00) translateX(-6px); }
  50%  { opacity: 0.44; transform: skewX(-5deg)  scaleY(0.89) translateX(-2px); }
  54%  { opacity: 0.92; transform: skewX(8deg)   scaleY(1.08) translateX(3px);  }
  58%  { opacity: 0.76; transform: skewX(13deg)  scaleY(0.93) translateX(5px);  }
  62%  { opacity: 0.40; transform: skewX(15deg)  scaleY(0.88) translateX(6px);  }
  66%  { opacity: 0.90; transform: skewX(3deg)   scaleY(1.14) translateX(1px);  }
  70%  { opacity: 0.84; transform: skewX(-9deg)  scaleY(0.96) translateX(-3px); }
  74%  { opacity: 0.92; transform: skewX(-14deg) scaleY(1.01) translateX(-5px); }
  78%  { opacity: 0.46; transform: skewX(-7deg)  scaleY(0.91) translateX(-3px); }
  82%  { opacity: 0.88; transform: skewX(6deg)   scaleY(1.09) translateX(2px);  }
  86%  { opacity: 0.80; transform: skewX(12deg)  scaleY(0.94) translateX(4px);  }
  90%  { opacity: 0.92; transform: skewX(1deg)   scaleY(1.06) translateX(0px);  }
  94%  { opacity: 0.84; transform: skewX(-11deg) scaleY(0.97) translateX(-4px); }
  100% { opacity: 0.88; transform: skewX(0deg)   scaleY(1.00) translateX(0px);  }
}
@keyframes ff-fire-lick-d {
  /* Strong right flame — alternating deep leans with surge flares */
  0%   { opacity: 0.90; transform: skewX(0deg)   scaleY(1.00) translateX(0px);  }
  4%   { opacity: 0.84; transform: skewX(-10deg) scaleY(0.96) translateX(-3px); }
  8%   { opacity: 0.92; transform: skewX(-14deg) scaleY(1.04) translateX(-5px); }
  13%  { opacity: 0.48; transform: skewX(-8deg)  scaleY(0.91) translateX(-3px); }
  17%  { opacity: 0.88; transform: skewX(4deg)   scaleY(1.11) translateX(1px);  }
  21%  { opacity: 0.82; transform: skewX(13deg)  scaleY(0.93) translateX(4px);  }
  25%  { opacity: 0.90; transform: skewX(15deg)  scaleY(0.97) translateX(5px);  }
  29%  { opacity: 0.44; transform: skewX(10deg)  scaleY(0.90) translateX(3px);  }
  33%  { opacity: 0.92; transform: skewX(1deg)   scaleY(1.13) translateX(0px);  }
  38%  { opacity: 0.86; transform: skewX(-12deg) scaleY(0.97) translateX(-4px); }
  42%  { opacity: 0.78; transform: skewX(-15deg) scaleY(1.02) translateX(-5px); }
  46%  { opacity: 0.92; transform: skewX(-6deg)  scaleY(1.07) translateX(-2px); }
  51%  { opacity: 0.84; transform: skewX(7deg)   scaleY(0.95) translateX(2px);  }
  55%  { opacity: 0.40; transform: skewX(14deg)  scaleY(0.89) translateX(5px);  }
  59%  { opacity: 0.90; transform: skewX(5deg)   scaleY(1.10) translateX(2px);  }
  64%  { opacity: 0.82; transform: skewX(-9deg)  scaleY(0.96) translateX(-3px); }
  68%  { opacity: 0.90; transform: skewX(-13deg) scaleY(1.01) translateX(-4px); }
  72%  { opacity: 0.46; transform: skewX(-4deg)  scaleY(0.92) translateX(-1px); }
  76%  { opacity: 0.88; transform: skewX(9deg)   scaleY(1.08) translateX(3px);  }
  80%  { opacity: 0.84; transform: skewX(12deg)  scaleY(0.95) translateX(4px);  }
  85%  { opacity: 0.92; transform: skewX(2deg)   scaleY(1.05) translateX(1px);  }
  89%  { opacity: 0.80; transform: skewX(-8deg)  scaleY(0.98) translateX(-3px); }
  94%  { opacity: 0.90; transform: skewX(-11deg) scaleY(1.03) translateX(-4px); }
  100% { opacity: 0.90; transform: skewX(0deg)   scaleY(1.00) translateX(0px);  }
}


/* Candle flicker — two layers (Velvet Hours technique).
   Placed coords:  left 60.36 %, top 63.73 % (image-pixel centroid of candle cluster).

   ff-candle-glow  — ambient halo; large oval, slow 17 s breathing.
   ff-candle-flame — flame body. transform-origin:bottom center (wick).
                     7.3 s linear irregular cycle — identical keyframes
                     to velvet-flame-flicker, just different class name
                     and world-active gating.

   Glow: translate(-50%,-50%) centres on left/top. Flame: neg margins. */
.ff-candle-glow {
  position: absolute;
  left: 60.36%;     /* visual centre — translate(-50%,-50%) centres on left/top */
  top: 63.73%;
  transform: translate(-50%, -50%);
  width: 2.5%;
  height: 4.5%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 55%,
    rgba(255, 190, 55,  0.38)  0%,
    rgba(255, 135, 18,  0.16) 45%,
    rgba(180,  62,  0,  0.05) 68%,
    transparent                85%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  animation: ff-candle-glow-breathe 17s ease-in-out infinite;
  animation-play-state: paused;
}
.ff-candle-flame {
  position: absolute;
  left: 60.36%;     /* visual centre — neg margins centre on left/top */
  top: 63.73%;
  margin-left: -0.45%;              /* = -(width/2):  centres on left */
  margin-top:  -1.266%;             /* = -(height/2 × H/W): %h→%w unit fix */
  width: 0.9%;
  height: 4.5%;
  border-radius: 50% 50% 38% 38%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(255, 248, 185,  0.94)  0%,
    rgba(255, 205,  65,  0.66) 36%,
    rgba(255, 130,  12,  0.28) 68%,
    transparent                 88%
  );
  mix-blend-mode: screen;
  z-index: 2;
  pointer-events: none;
  transform-origin: bottom center;
  animation: ff-candle-flame-flicker 7.3s linear infinite;
  animation-play-state: paused;
}
.world--faith-and-frequency.ff-active .ff-candle-glow,
.world--faith-and-frequency.ff-active .ff-candle-flame {
  animation-play-state: running;
}
@keyframes ff-candle-glow-breathe {
  /* Same cadence as velvet-glow-breathe */
  0%   { opacity: 0.80; }
  7%   { opacity: 0.90; }
  13%  { opacity: 0.74; }
  21%  { opacity: 0.86; }
  28%  { opacity: 0.92; }
  36%  { opacity: 0.70; }
  42%  { opacity: 0.84; }
  49%  { opacity: 0.90; }
  55%  { opacity: 0.76; }
  62%  { opacity: 0.88; }
  70%  { opacity: 0.94; }
  76%  { opacity: 0.68; }
  83%  { opacity: 0.82; }
  89%  { opacity: 0.78; }
  95%  { opacity: 0.92; }
  100% { opacity: 0.80; }
}
@keyframes ff-candle-flame-flicker {
  /* Identical keyframes to velvet-flame-flicker — same proven motion */
  0%   { opacity: 0.88; transform: scaleX(1.00) scaleY(1.00) translateX(0px);  }
  5%   { opacity: 0.78; transform: scaleX(1.06) scaleY(0.96) translateX(1px);  }
  9%   { opacity: 0.91; transform: scaleX(0.97) scaleY(1.04) translateX(-1px); }
  15%  { opacity: 0.84; transform: scaleX(1.03) scaleY(0.98) translateX(0px);  }
  20%  { opacity: 0.60; transform: scaleX(1.10) scaleY(0.91) translateX(2px);  }
  24%  { opacity: 0.88; transform: scaleX(0.96) scaleY(1.04) translateX(-1px); }
  30%  { opacity: 0.93; transform: scaleX(1.01) scaleY(1.02) translateX(0px);  }
  35%  { opacity: 0.76; transform: scaleX(1.07) scaleY(0.94) translateX(-2px); }
  40%  { opacity: 0.89; transform: scaleX(0.98) scaleY(1.06) translateX(1px);  }
  46%  { opacity: 0.82; transform: scaleX(1.02) scaleY(1.00) translateX(0px);  }
  52%  { opacity: 0.91; transform: scaleX(0.95) scaleY(1.05) translateX(1px);  }
  57%  { opacity: 0.64; transform: scaleX(1.11) scaleY(0.89) translateX(-3px); }
  62%  { opacity: 0.87; transform: scaleX(1.01) scaleY(1.01) translateX(0px);  }
  68%  { opacity: 0.93; transform: scaleX(0.96) scaleY(1.05) translateX(0px);  }
  74%  { opacity: 0.79; transform: scaleX(1.05) scaleY(0.95) translateX(2px);  }
  79%  { opacity: 0.89; transform: scaleX(1.00) scaleY(1.01) translateX(-1px); }
  85%  { opacity: 0.73; transform: scaleX(1.08) scaleY(0.92) translateX(1px);  }
  90%  { opacity: 0.91; transform: scaleX(0.97) scaleY(1.05) translateX(-1px); }
  95%  { opacity: 0.85; transform: scaleX(1.02) scaleY(0.99) translateX(0px);  }
  100% { opacity: 0.88; transform: scaleX(1.00) scaleY(1.00) translateX(0px);  }
}

/* Coffee steam — soft oval wisps rising from the mug (lower-left of scene).
   Pixel-centroid analysis (v168):
     Cup mouth: x 12.26%–20.10%, centre x=16.18%, rim top y=51.97% from top.
   Container: left=11.2%, top=32.0%; bottom edge (32+20=52%) sits at cup rim.
   Wisps: position:absolute; bottom:0 within container → rise via translateY.
   overflow:visible → wisps visible above container top edge if needed.

   ROOT CAUSES OF PRIOR INVISIBILITY (fixed here):
     • Width was 2-3px + 2.5px blur = ~7px rendered — essentially sub-pixel thin
     • background rgba 0.40 × anim opacity 0.26 = 10.4% effective — invisible
   FIX: widen to 10-14px, radial-gradient for soft puff shape,
        raise gradient fill to 0.78, raise anim peak to 0.52.
   data-placeable="ff-steam" → fine-tune in ?place=1 mode.                    */
.ff-steam {
  position: absolute;
  left: 11.1%;    /* cup mouth centre 16.18% minus half-width 5% — nudged 0.1% left */
  top:  32.0%;    /* container bottom (32+20=52%) aligns with cup rim */
  width: 10%;
  height: 20%;
  pointer-events: none;
  overflow: visible;
  z-index: 3;
}
.ff-wisp {
  position: absolute;
  bottom: 0;
  border-radius: 50%;
  /* Radial-gradient oval puff — soft centre, fading edges */
  background: radial-gradient(ellipse at 50% 60%,
    rgba(215, 222, 235, 0.78)  0%,
    rgba(210, 218, 232, 0.38) 48%,
    transparent                74%
  );
  filter: blur(5px);
  opacity: 0;
  animation-timing-function: ease-out;
  animation-iteration-count: infinite;
  animation-name: ff-steam-rise;
  animation-play-state: paused;
}
/* Four wisps — wider oval puffs, varied timing, negative delays for instant stagger */
.ff-wisp--1 { left: 20%; width: 14px; height: 40px; animation-duration: 5.2s; animation-delay:  0.0s; }
.ff-wisp--2 { left: 45%; width: 11px; height: 34px; animation-duration: 4.4s; animation-delay: -1.8s; }
.ff-wisp--3 { left: 32%; width: 13px; height: 38px; animation-duration: 6.0s; animation-delay: -3.4s; }
.ff-wisp--4 { left: 60%; width: 10px; height: 32px; animation-duration: 4.9s; animation-delay: -0.9s; }
.world--faith-and-frequency.ff-active .ff-wisp {
  animation-play-state: running;
}
@keyframes ff-steam-rise {
  /* Subtle but VISIBLE: peak 52%, gentle x-drift, blur grows as wisp disperses */
  0%   { transform: translateY(0px)    translateX(0px);  opacity: 0;    filter: blur(4px);  }
  12%  { opacity: 0.54; }  /* +1.5% effective: 0.78×0.54=42.1% vs prior 40.6% */
  44%  { transform: translateY(-52px)  translateX(5px);  opacity: 0.30; filter: blur(6px);  }
  74%  { transform: translateY(-96px)  translateX(-4px); opacity: 0.12; filter: blur(9px);  }
  100% { transform: translateY(-140px) translateX(2px);  opacity: 0;    filter: blur(12px); }
}

/* Rain — clipped to window pane region (cool-toned left/centre of scene).
   Image analysis: window area is x 5–65%, y 2–56%; fireplace wall starts ~72%.
   Individual .ff-raindrop spans built by faith-freq.js on activation.

   ROOT CAUSE OF PRIOR INVISIBILITY (fixed here — same bug as steam):
     JS opacity 0.07–0.23 × CSS alpha 0.78 = 5–18% effective — invisible.
   FIX: JS opacity raised to 0.30–0.55 → effective 23–43%, avg 33%.
        Width 1px → 1.5px for sub-pixel visibility. Height 10–24px → 28–60px. */
.ff-rain {
  position: absolute;
  left: 0; top: 2%; width: 72%; height: 56%;
  pointer-events: none;
  overflow: hidden;
  /* opacity + fade-in controlled by JS setTimeout in faith-freq.js */
}
.ff-raindrop {
  position: absolute;
  width: 1.5px;
  background: linear-gradient(to bottom,
    transparent,
    rgba(192, 214, 242, 0.78),
    transparent
  );
  animation-name: ff-rain-fall;
  animation-timing-function: linear;
  animation-iteration-count: infinite;
  animation-play-state: paused;
}
.world--faith-and-frequency.ff-active .ff-raindrop {
  animation-play-state: running;
}
@keyframes ff-rain-fall {
  /* Slight diagonal (left-ward drift) — wind-driven rain on glass */
  0%   { transform: translateY(-50px) translateX(0);     }
  100% { transform: translateY(680px) translateX(-18px); }
}
@keyframes ff-rain-fall-right {
  /* Right pane only — sill at y≈39% (347px from container top); ≈360px travel */
  0%   { transform: translateY(-50px) translateX(0);     }
  100% { transform: translateY(360px) translateX(-18px); }
}

/* ---------------------------------------------------------
   Faith & Frequency — Lightning flash overlay
   Pixel analysis: painted bolt runs x≈13–16 %, y≈8–38 % (section %).
   Brightest point: x=13.3 %, y=8.2 % — gradient blooms from there.

   mix-blend-mode: screen — adds only light, never darkens; the
   radial gradient has no hard edges so there is no visible rectangle.
   isolation: isolate on the section confines the blend to this world.

   JS controls opacity directly for the multi-flicker stutter.
   --------------------------------------------------------- */
.ff-lightning {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  z-index: 8;               /* above rain layers, below typography */
  opacity: 0;
  /* Soft radial bloom centred on the painted bolt (14 % left, 9 % top).
     Ellipse radii are large so the light spills across the windows and
     into the room — no hard edge anywhere. */
  background: radial-gradient(
    ellipse 60% 52% at 14% 9%,
    rgba(228, 238, 255, 0.90)  0%,
    rgba(210, 225, 255, 0.55) 18%,
    rgba(192, 212, 255, 0.25) 38%,
    rgba(175, 202, 255, 0.08) 58%,
    transparent               78%
  );
}

/* ---------------------------------------------------------
   Faith & Frequency — Typography Overlay Sequence
   Fonts: Cinzel for FAITH / & / tagline; Great Vibes for Frequency script.
   Brittany Signature unavailable on Google Fonts — Great Vibes is closest.
   Layout: full-width centered over the scene (left:0; right:0).
   Timing (from world activation — all driven by JS timers):
     t= 13.0 s  FAITH fades in           (1.2 s ease-in)
     t= 14.8 s  & fades in               (0.9 s ease-in)
     t= 16.3 s  Frequency pen-reveal     (1.8 s cubic-bezier)
     t= 22.0 s  tagline line 1 fade in   (1.2 s ease-in)
     t= 23.4 s  dots pulse starts        (2.2 s, 2× blink, settles)
     t= 26.5 s  tagline line 2 fade in   (1.2 s ease-in)
     t= 31.0 s  title fades out          (tagline stays permanently)
   Handwriting note: clip-path inset(0 100%→0% 0 0) with a pen-pressure
   cubic-bezier. True SVG stroke-dashoffset animation would require extracting
   glyph paths from the font binary — not achievable in pure CSS/HTML.
   --------------------------------------------------------- */
.ff-typography {
  position: absolute;
  top:  12%;
  left:  0;
  right: 0;
  text-align: center;
  pointer-events: none;
  z-index: 10;
  opacity: 1;
}
/* FAITH — Cinzel, wide-tracked capitals, pure fade-in */
.ff-typo-faith {
  font-family: 'Cinzel', Georgia, serif;
  font-size: clamp(2rem, 4.2vw, 3.8rem);
  font-weight: 400;
  letter-spacing: 0.38em;
  color: rgba(242, 232, 215, 0.96);
  text-shadow:
    0 2px 28px rgba(0, 0, 0, 0.60),
    0 1px  8px rgba(0, 0, 0, 0.45);
  line-height: 1.0;
  opacity: 0;
}
/* & — Great Vibes script in cream; bridges FAITH to Frequency visually */
.ff-typo-amp {
  font-family: 'Great Vibes', cursive;
  font-size: clamp(1.4rem, 2.8vw, 2.4rem);
  color: rgba(242, 232, 215, 0.88);
  text-shadow:
    0 2px 24px rgba(0, 0, 0, 0.55),
    0 1px  7px rgba(0, 0, 0, 0.40);
  margin-top: -0.30em;
  line-height: 0.8;
  opacity: 0;
}
/* "Frequency" — Great Vibes script, muted brand purple, clip-path pen reveal */
.ff-typo-freq-wrap {
  margin-top: -0.22em;
  position: relative;
  overflow: visible;
}
.ff-typo-freq {
  display: inline-block;
  font-family: 'Great Vibes', cursive;
  font-size: clamp(2.6rem, 5.8vw, 5rem);
  color: rgba(155, 118, 178, 0.92);   /* muted dusty purple — MuSocí brand tone */
  text-shadow:
    0 2px 28px rgba(0, 0, 0, 0.60),
    0 1px  8px rgba(0, 0, 0, 0.45);
  line-height: 0.9;
  clip-path: inset(-60px 100% -10px 0); /* headroom above/below guards the F ascender */
}
/* Blinking dots on tagline line 1 — 2 pulses then settles */
.ff-dots {
  display: inline;
}
@keyframes ff-dots-pulse {
  0%   { opacity: 1;    }
  16%  { opacity: 0.06; }
  34%  { opacity: 1;    }
  52%  { opacity: 0.06; }
  68%  { opacity: 1;    }
  84%  { opacity: 0.55; }
  100% { opacity: 1;    }
}
.ff-dots.is-pulsing {
  animation: ff-dots-pulse 2.2s ease-in-out 1 forwards;
}
/* Tagline — Cinzel small-caps feel, spaced */
.ff-typo-tagline {
  margin-top: 0.6em;
  font-family: 'Cinzel', Georgia, serif;
  font-size: clamp(0.6rem, 1.2vw, 0.92rem);
  font-weight: 400;
  letter-spacing: 0.2em;
  color: rgba(220, 210, 194, 0.86);
  text-shadow:
    0 1px 16px rgba(0, 0, 0, 0.65),
    0 1px  5px rgba(0, 0, 0, 0.50);
  line-height: 2.2;
}
.ff-typo-line1,
.ff-typo-line2 {
  opacity: 0;
}

/* ---------------------------------------------------------
/* ---------------------------------------------------------
   Hip Hop Headroom world (1619 × 972 px artwork)
   --------------------------------------------------------- */
.world--hip-hop-headroom {
  position: relative;
  width: 100%;
  aspect-ratio: 1619 / 972;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.hiphop-bg {
  position: absolute;
  inset: 0;
}
.hiphop-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   Hip Hop Headroom — animated title overlay
   z-index 10 keeps text above canvas (z-index 3).
   All motion via @keyframes — reliable across browsers.
   --------------------------------------------------------- */

.hhh-title {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  z-index: 10;
  pointer-events: none;
  user-select: none;
  text-align: center;
}

.hhh-title__top {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 0.22em;
  line-height: 0.9;
}

.hhh-wrap { display: inline-block; }

/* Elements are ALWAYS visible — world section is display:none when
   inactive, so there is no need to hide individual children.
   The animation adds the cinematic fade-in on top of that.       */
.hhh-word {
  display: inline-block;
  font-family: 'Bebas Neue', Impact, 'Arial Narrow', sans-serif;
  font-size: clamp(3.5rem, 11vw, 9.5rem);
  font-weight: 400;
  color: #f0ebe0;
  letter-spacing: 0.04em;
  text-shadow:
    0 0 80px rgba(200,148,20,0.22),
    0 6px 48px rgba(0,0,0,0.95);
}

.hhh-title__divider {
  width: 38%;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    #c8902a 25%,
    #f0c060 50%,
    #c8902a 75%,
    transparent 100%
  );
  margin: 0.45em auto 0.55em;
}

.hhh-title__main {
  font-family: 'Cinzel', Georgia, serif;
  font-size: clamp(1.1rem, 3.2vw, 3rem);
  font-weight: 400;
  color: #c8902a;
  letter-spacing: 0.28em;
  text-shadow:
    0 0 40px rgba(200,144,42,0.75),
    0 0 90px rgba(200,144,42,0.35),
    0 3px 20px rgba(0,0,0,0.9);
}

.hhh-title__sub {
  font-family: 'Cinzel', Georgia, serif;
  font-size: clamp(0.45rem, 1vw, 0.8rem);
  font-weight: 400;
  color: rgba(240,235,224,0.50);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  margin-top: 0.9em;
}

/* ---------------------------------------------------------
   .hhh-animate — cinematic entrance, triggered by JS
   fill-mode: backwards means element starts at "from"
   keyframe during the delay (invisible), then animates in.
   --------------------------------------------------------- */

.hhh-title.hhh-animate .hhh-wrap:nth-child(1) .hhh-word {
  animation: hhhWordIn 0.65s cubic-bezier(0.16, 1, 0.3, 1) 0.05s backwards;
}
.hhh-title.hhh-animate .hhh-wrap:nth-child(2) .hhh-word {
  animation: hhhWordIn 0.65s cubic-bezier(0.16, 1, 0.3, 1) 0.18s backwards;
}
.hhh-title.hhh-animate .hhh-title__divider {
  animation: hhhLineIn 0.55s ease 0.40s backwards;
}
.hhh-title.hhh-animate .hhh-title__main {
  animation: hhhHeadroomIn 0.90s cubic-bezier(0.16, 1, 0.3, 1) 0.55s backwards;
}
.hhh-title.hhh-animate .hhh-title__sub {
  animation: hhhSubIn 0.70s ease 1.00s backwards;
}

@keyframes hhhWordIn {
  from { opacity: 0; transform: translateY(32px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes hhhLineIn {
  from { opacity: 0; transform: scaleX(0); }
  to   { opacity: 1; transform: scaleX(1); }
}
@keyframes hhhHeadroomIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes hhhSubIn {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  .hhh-title.hhh-animate .hhh-word,
  .hhh-title.hhh-animate .hhh-title__divider,
  .hhh-title.hhh-animate .hhh-title__main,
  .hhh-title.hhh-animate .hhh-title__sub {
    animation: none !important;
  }
}

/* ---------------------------------------------------------
   Midday Bounce world (1536 x 1024 px artwork)
   Static background only.
   --------------------------------------------------------- */
.world--midday-bounce {
  position: relative;
  width: 100%;
  aspect-ratio: 1536 / 1024;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.midday-bounce-bg {
  position: absolute;
  inset: 0;
}
.midday-bounce-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   Day Shift world (1672 × 941 px — Day_Shift_Wide.PNG)
   Static background; title baked into artwork.
   Same pattern as island-soul / midday-bounce.
   --------------------------------------------------------- */
.world--day-shift {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.day-shift-bg {
  position: absolute;
  inset: 0;
}
.day-shift-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}


/* ---------------------------------------------------------
   Sunday Grooves world (1672 × 941 px artwork)
   Static background only — no animation in this pass.
   Title is baked into the artwork; no overlay text needed.
   --------------------------------------------------------- */
.world--sunday-grooves {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.sunday-bg {
  position: absolute;
  inset: 0;
}
.sunday-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   World Stage — single-world container
   All worlds live inside; only .is-active one is shown.
   The stage itself carries the outer border so individual
   world sections don't need their own border-top/bottom.
   --------------------------------------------------------- */
.world-stage {
  position: relative;
  width: 100%;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  /* Offset for sticky header so scrollIntoView lands cleanly */
  scroll-margin-top: 80px;
}

/* Each child world: hidden by default, no redundant border */
.world-stage .world {
  display: none;
  border-top: none;
  border-bottom: none;
}

/* The currently on-air world */
.world-stage .world.is-active {
  display: block;
}

.world__eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.25em;
  font-size: 0.75rem;
  color: var(--color-pink);
  font-weight: 600;
  margin: 0 0 var(--space-1);
}

.world__title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2rem, 5vw, 3.2rem);
  margin: 0 0 var(--space-3);
  background: var(--grad-text);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.world__identity {
  font-size: 1.1rem;
  color: var(--text-1);
  max-width: 620px;
}

.world__meta {
  margin-top: var(--space-3);
  font-style: italic;
  color: var(--text-muted);
}

/* =========================================================
   EMAIL SIGNUP
   ========================================================= */

.signup-form {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-3);
}

.signup-form__input {
  flex: 1 1 260px;
  padding: 0.85rem 1.2rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.15);
  background: var(--bg-2);
  color: var(--text-0);
  font-family: var(--font-body);
  font-size: 0.95rem;
}

.signup-form__input::placeholder { color: var(--text-muted); }

.signup-form__input:focus {
  outline: 2px solid var(--color-blue);
  outline-offset: 2px;
}

.signup-form__note {
  margin-top: var(--space-2);
  font-weight: 600;
  color: var(--color-pink);
  min-height: 1.4em;
}

.form-todo {
  margin-top: var(--space-2);
  font-size: 0.85rem;
  color: var(--text-muted);
}

/* =========================================================
   CLOSING
   ========================================================= */

.closing {
  padding: var(--space-5) 0;
  text-align: center;
  background:
    linear-gradient(180deg, transparent 0%, rgba(91, 47, 134, 0.25) 100%),
    var(--bg-1);
}

.closing__heading {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(2rem, 5vw, 3rem);
  line-height: 1.15;
  margin: 0 0 var(--space-2);
}

.closing__line {
  font-family: var(--font-display);
  font-size: clamp(1.15rem, 3vw, 1.5rem);
  color: var(--text-1);
  max-width: 640px;
  margin: 0 auto var(--space-3);
}

/* =========================================================
   FOOTER
   ========================================================= */

.site-footer {
  background: var(--bg-0);
  border-top: 1px solid rgba(255, 255, 255, 0.06);
  padding: var(--space-5) 0 var(--space-4);
}

.footer-inner {
  text-align: center;
}

.footer-logo {
  height: 34px;
  margin: 0 auto var(--space-3);
  opacity: 0.85;
}

.footer-nav {
  display: flex;
  justify-content: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.footer-nav a {
  color: var(--text-1);
  font-size: 0.9rem;
}

.footer-nav a:hover { color: var(--text-0); }

.footer-copy {
  max-width: 600px;
  margin: 0 auto var(--space-2);
  color: var(--text-muted);
  font-size: 0.85rem;
}

.footer-fine {
  color: var(--text-muted);
  font-size: 0.8rem;
  margin: 0;
}

/* =========================================================
   NAV — current page indicator
   ========================================================= */

.site-nav a[aria-current="page"] {
  color: var(--text-0);
}

.site-nav a[aria-current="page"]::after {
  content: "";
  display: block;
  height: 2px;
  margin-top: 2px;
  border-radius: 2px;
  background: var(--grad-text);
}

/* =========================================================
   WHAT'S ON — Programming Schedule page
   View toggle (Day / Grid), day tabs, vertical day list, and
   full-week TV-guide grid. Moods stay hidden until a show is
   tapped, clicked, or hovered (kept subtle on purpose).
   ========================================================= */

.whatson-page-header {
  margin-bottom: var(--space-3);
}

.whatson-explicit-note {
  margin-top: -0.4rem;
  font-size: 0.85rem;
  color: var(--text-muted);
}

.whatson-toggle {
  display: inline-flex;
  gap: 0.3rem;
  padding: 0.3rem;
  border-radius: 999px;
  background: var(--bg-2);
  border: 1px solid rgba(255, 255, 255, 0.08);
  margin: var(--space-2) 0 var(--space-4);
}

.whatson-toggle__btn {
  border: none;
  background: transparent;
  color: var(--text-1);
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.85rem;
  padding: 0.55rem 1.3rem;
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease;
}

.whatson-toggle__btn.is-active {
  background: var(--grad-brand);
  color: #fff;
}

/* --- Day view --- */

.whatson-day__tabs {
  display: flex;
  gap: 0.4rem;
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

.whatson-day__tab {
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: var(--bg-2);
  color: var(--text-1);
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  padding: 0.5rem 1.1rem;
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
}

.whatson-day__tab.is-active {
  border-color: var(--color-pink);
  color: var(--text-0);
  box-shadow: 0 0 14px -4px var(--glow-pink);
}

.whatson-day__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.whatson-show {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius);
  background: var(--bg-2);
  border: 1px solid rgba(255, 255, 255, 0.06);
  cursor: pointer;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.whatson-show:hover,
.whatson-show:focus-visible,
.whatson-show.is-open {
  border-color: rgba(254, 102, 196, 0.4);
  outline: none;
}

/* Shared keyframe — slow border/glow pulse for live blocks.
   Uses inset box-shadow (not border-color) so the ring is
   perfectly uniform on all four sides — unaffected by anything
   adjacent (e.g. the is-today column-header stripe above). */
@keyframes on-air-pulse {
  0%, 100% {
    box-shadow: inset 0 0 0 2px rgba(184, 50, 106, 0.70);
  }
  50% {
    box-shadow: inset 0 0 0 2px rgba(254, 102, 196, 0.95);
  }
}

@keyframes on-air-pulse-micro {
  0%, 100% {
    box-shadow: inset 0 0 0 2px rgba(58, 160, 200, 0.70);
  }
  50% {
    box-shadow: inset 0 0 0 2px rgba(58, 160, 200, 0.95);
  }
}

/* ON AIR badge pulse — opacity-only, gentle */
@keyframes on-air-badge-pulse {
  0%, 100% { opacity: 0.75; }
  50%       { opacity: 1;    }
}

.whatson-show.is-now {
  animation: on-air-pulse 3s ease-in-out infinite;
}

.whatson-show__time {
  flex-shrink: 0;
  font-size: 0.78rem;
  color: var(--text-muted);
  min-width: 7.5rem;
  font-variant-numeric: tabular-nums;
}

.whatson-show__body {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  flex: 1;
  min-width: 0;
}

.whatson-show__name {
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--text-0);
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.whatson-show__onair {
  display: inline-block;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-pink);
  background: rgba(254, 102, 196, 0.12);
  border: 1px solid rgba(254, 102, 196, 0.3);
  border-radius: 999px;
  padding: 0.1em 0.5em;
  animation: on-air-badge-pulse 3s ease-in-out infinite;
}

.whatson-show__mood {
  font-size: 0.78rem;
  color: #e8c97a;
  font-style: italic;
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 0.25s ease, max-height 0.25s ease;
}

.whatson-show:hover .whatson-show__mood,
.whatson-show:focus-visible .whatson-show__mood,
.whatson-show.is-open .whatson-show__mood {
  opacity: 1;
  max-height: 3rem;
}

/* Explicit badge — shared between day view and grid view */
.whatson-explicit-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.15em;
  height: 1.15em;
  font-size: 0.6rem;
  font-weight: 700;
  color: #fff;
  background: rgba(255, 60, 60, 0.82);
  border-radius: 2px;
  margin-left: 0.3em;
  vertical-align: middle;
  flex-shrink: 0;
}

/* =========================================================
   GRID VIEW — full-week TV-guide layout
   Column 1 = time labels, columns 2–8 = Sun–Sat.
   Row 1 = day headers, rows 2+ = 30-min time slots.
   grid-auto-rows drives height; JS assigns grid-column
   and grid-row via inline style.
   ========================================================= */

.whatson-grid-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  margin: 0 calc(-1 * var(--space-2));
  padding: 0 var(--space-2) var(--space-3);
}

.whatson-grid {
  display: grid;
  grid-template-columns: 80px repeat(7, minmax(100px, 1fr));
  grid-auto-rows: 28px;
  gap: 1px;
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: var(--radius);
  overflow: hidden;
  min-width: 620px;
  font-size: 0.75rem;
}

/* --- Row 1: day-header cells --- */
.whatson-grid__head {
  background: var(--bg-2);
  color: var(--text-1);
  font-weight: 700;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0.6rem 0.3rem;
  position: sticky;
  top: 0;
  z-index: 3;
}

.whatson-grid__head--corner {
  background: var(--bg-1);
  z-index: 4;
}

.whatson-grid__head.is-today {
  color: var(--color-pink);
  box-shadow: inset 0 -2px 0 var(--color-pink);
}

/* --- Column 1: time labels --- */
.whatson-grid__time {
  grid-column: 1;
  background: var(--bg-1);
  color: var(--text-muted);
  font-size: 0.67rem;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  padding: 0.25rem 0.5rem 0;
  border-top: 1px solid rgba(255, 255, 255, 0.04);
  position: sticky;
  left: 0;
  z-index: 2;
}

/* --- Show cells (columns 2–8) --- */
.whatson-grid__cell {
  background: var(--bg-2);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 3px;
  padding: 0.28rem 0.45rem;
  overflow: hidden;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 0.15rem;
  transition: border-color 0.2s ease, background 0.2s ease;
  margin: 1px;
}

.whatson-grid__cell:hover,
.whatson-grid__cell:focus-visible,
.whatson-grid__cell.is-open {
  background: rgba(255, 255, 255, 0.07);
  border-color: rgba(254, 102, 196, 0.35);
  outline: none;
  position: relative;
  z-index: 1;
}

.whatson-grid__cell.is-now {
  animation: on-air-pulse 3s ease-in-out infinite;
  /* Suppress base border so only the inset shadow reads as
     the cell edge — no double-border on any side. */
  border-color: transparent;
}

.whatson-grid__cell-inner {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  min-height: 0;
  overflow: hidden;
}

.whatson-grid__cell-name {
  font-weight: 600;
  color: var(--text-0);
  line-height: 1.25;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.whatson-grid__cell--short .whatson-grid__cell-name {
  -webkit-line-clamp: 1;
}

.whatson-grid__cell-time {
  font-size: 0.64rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}

.whatson-grid__cell--short .whatson-grid__cell-time {
  display: none;
}

.whatson-grid__cell-mood {
  font-size: 0.67rem;
  color: #e8c97a;
  font-style: italic;
  opacity: 0;
  max-height: 0;
  overflow: hidden;
  transition: opacity 0.2s ease, max-height 0.2s ease;
}

.whatson-grid__cell:hover .whatson-grid__cell-mood,
.whatson-grid__cell:focus-visible .whatson-grid__cell-mood,
.whatson-grid__cell.is-open .whatson-grid__cell-mood {
  opacity: 1;
  max-height: 2.5rem;
}

/* ── Micro-segment chips (inside grid cells) ─────────────── */
.whatson-grid__micro {
  display: block;
  margin-top: 0.3rem;
  padding: 0.12rem 0.35rem;
  background: rgba(58, 160, 200, 0.12);
  border-left: 2px solid var(--color-blue, #3aa0c8);
  border-radius: 0 2px 2px 0;
  font-size: 0.68rem;
  font-family: 'Exo 2', sans-serif;
  letter-spacing: 0.02em;
  color: var(--color-blue, #3aa0c8);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 1.4;
}

/* ── Micro-segment entries in Day View ───────────────────── */
/* Micro-segments in Day View look identical to regular blocks.
   Only on-air state differs — blue glow instead of pink/purple. */
.whatson-show--micro.is-now {
  animation: on-air-pulse-micro 3s ease-in-out infinite;
}

/* --- Responsive: narrow screens --- */
@media (max-width: 700px) {
  .whatson-grid {
    grid-template-columns: 58px repeat(7, minmax(68px, 1fr));
    grid-auto-rows: 24px;
    font-size: 0.67rem;
    min-width: 520px;
  }

  .whatson-grid__cell {
    padding: 0.2rem 0.3rem;
  }
}

@media (prefers-reduced-motion: reduce) {
  .whatson-show.is-now {
    animation: none;
    box-shadow: inset 0 0 0 2px rgba(254, 102, 196, 0.75);
  }
  .whatson-grid__cell.is-now {
    animation: none;
    border-color: transparent;
    box-shadow: inset 0 0 0 2px rgba(254, 102, 196, 0.75);
  }
  .whatson-show__onair {
    animation: none;
    opacity: 1;
  }
}

/* =========================================================
   SUNDAY GROOVES WORLD — golden-hour porch scene
   Three light layers, staggered ~10–15 s apart:
     1. Sun glow bloom  (CSS-only, starts at world activation)
     2. String lights   (JS-driven, kindle over ~10–46 s)
     3. Neighbourhood windows + porch light (JS, ~35–58 s)
   Single .sunday-drift-frame parent carries ONE soul-bg-drift
   animation — bg + all overlays share the same transform,
   physically impossible to phase-drift apart.
   ========================================================= */

/* ---- Drift frame ------------------------------------------- */
.sunday-drift-frame {
  position: absolute;
  inset: 0;
  transform-origin: center center;
  will-change: transform;
  /* Reuse the soul-bg-drift keyframe (already defined above).
     Slightly longer period keeps the two worlds feeling distinct. */
  animation: soul-bg-drift 50s ease-in-out infinite alternate;
}

/* .sunday-bg and .sunday-bg__img are already defined above —
   they work unchanged inside the drift frame. */

/* ---- Layer 1: Sun glow ------------------------------------- */
/* Radial gradient centred on the actual sunset gap in the trees.
   Phase 1 (8 s): blooms from dark.
   Phase 2 (after 8 s): gentle infinite breathe.
   The two animations hand off at opacity:1 → no visible jump. */
.sunday-sun-glow {
  position: absolute;
  /* Element positioned so the ellipse hot-spot lands on the
     sunset gap (~42% from left, ~7% from top of the image).  */
  left:   20%;
  top:   -20%;
  width:  46%;
  height: 55%;
  background: radial-gradient(ellipse at 50% 50%,
    rgba(255, 215,  80, 0.60)  0%,
    rgba(255, 165,  40, 0.40) 35%,
    rgba(255, 120,   0, 0.18) 60%,
    transparent                80%
  );
  mix-blend-mode: screen;
  pointer-events: none;
  /* Default: invisible, no animation — starts only when world is active */
  opacity: 0;
}

/* Sun glow disabled — the image already bakes in golden sunlight at this
   position; mix-blend-mode:screen on a bright sky produces near-zero
   visible change (adding light to light). Stays at opacity:0 for now.
   Keyframes kept below in case a non-screen approach is tried later. */

@keyframes sunday-sun-bloom {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Breathe starts from opacity:1 — no jump at handoff from bloom */
@keyframes sunday-sun-breathe {
  from { opacity: 1.00; transform: scale(1.04); }
  to   { opacity: 0.78; transform: scale(1.00); }
}

/* ---- Layer 2: String lights -------------------------------- */
.sunday-lights-wrap {
  position: absolute;
  /* Default left:0/top:0/100%x100% is a fallback for the instant before
     JS runs (js/sunday-grooves.js fitAllOverlays) — once that fires it
     overrides these with the exact cover-crop box so the % dots below
     land on the right spot in the photo on world.html too, not just
     on index.html where the container's aspect ratio happens to match
     the artwork's. Left as left/top/width/height (not inset) so JS's
     inline left/top/width/height aren't fighting a shorthand-set
     right/bottom. */
  left: 0; top: 0; width: 100%; height: 100%;
  pointer-events: none;
  z-index: 2;
}

.sunday-light {
  position: absolute;
  /* Small warm point — size of a real string-light bulb seen at dusk */
  width:  0.45%;
  height: 0.70%;
  border-radius: 50%;
  mix-blend-mode: screen;
  opacity: 0;
  pointer-events: none;
  /* White-hot core → warm amber mid → soft orange fade.
     Full opacity centre makes each bulb a visible bright point. */
  background: radial-gradient(ellipse,
    rgba(255, 255, 220, 1.00)  0%,
    rgba(255, 225,  80, 0.88) 28%,
    rgba(255, 165,  20, 0.48) 58%,
    rgba(255, 110,   0, 0.12) 80%,
    transparent               100%
  );
}

/* Positions estimated from image inspection.
   Strand runs upper-right with natural catenary sag.
   Use ?place=1 to fine-tune any that miss the bulbs. */
.sunday-light--1 { left: 61.00%; top: 34.80%; }
.sunday-light--2 { left: 68.50%; top: 35.71%; }
.sunday-light--3 { left: 72.00%; top: 33.47%; }
.sunday-light--4 { left: 77.14%; top: 31.61%; }
.sunday-light--5 { left: 68.73%; top: 39.55%; }
.sunday-light--6 { left: 74.24%; top: 30.29%; }
.sunday-light--7 { left: 79.5%; top: 31.25%; }
.sunday-light--8 { left: 82.28%; top: 31.88%; }
.sunday-light--9 { left: 65.23%; top: 35.32%; }

/* ---- Layer 3: Neighbourhood windows + porch light ---------- */
.sunday-wins-wrap {
  position: absolute;
  /* See .sunday-lights-wrap above — same cover-crop-aware sizing,
     fitted in JS once the world is active. */
  left: 0; top: 0; width: 100%; height: 100%;
  pointer-events: none;
  z-index: 3;
}

.sunday-win {
  position: absolute;
  border-radius: 1px;
  /* normal blend — renders as an opaque tint over any background brightness.
     Screen was invisible against the bright golden image; normal is not. */
  mix-blend-mode: normal;
  opacity: 0;
  pointer-events: none;
  /* Warm amber window light — visible on bright or dark surfaces because it
     composites as a semi-transparent colored layer, not additive light. */
  background: radial-gradient(ellipse,
    rgba(255, 185,  40, 0.92)  0%,
    rgba(255, 145,  10, 0.65) 40%,
    rgba(235, 100,   0, 0.22) 70%,
    transparent               100%
  );
}

/* Positions estimated from image inspection.
   Win 1-2: Victorian house upper-floor windows (across street).
   Win 3:   Mid-street house window.
   Win 4:   Far-street house window (smaller, dimmer).
   Win 5:   Porch light on Victorian house (round).
   Use ?place=1 to fine-tune. */
/* Sized as small lit windows seen from across the street — compact warm spots.
   Positions are placeholder — use ?place=1 to set them on the actual houses. */
.sunday-win--1 { left: 8.41%; top: 30.03%; width: 1.0%; height: 1.5%; }
.sunday-win--2 { left: 41.40%; top: 34.26%; width: 1.0%; height: 1.5%; }
.sunday-win--3 { left: 30.98%; top: 38.89%; width: 0.8%; height: 1.2%; }
.sunday-win--4 { left: 6.25%; top: 40.48%; width: 0.6%; height: 0.9%; }
.sunday-win--5 { left: 57.48%; top: 37.96%; width: 0.7%; height: 0.7%; border-radius: 50%; }

/* ---- Drift pause in place mode ----------------------------- */
.is-placing .sunday-drift-frame {
  animation-play-state: paused !important;
}

/* ---- Reduced motion ---------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .sunday-drift-frame { animation: none; }
  .sunday-sun-glow {
    animation: none;
    opacity: 0.88;
  }
}


/* ====================================================================
   Sunday Grooves — Sweet Tea Glass (Pass 3)
   Two-phase sequential overlay on the glass sitting on the side table
   (lower-right corner of the image, beside the rocking chair).

   Both layers live inside .sunday-drift-frame so they drift in sync
   with the Ken Burns motion.  JS controls opacity via inline style.

   Phase 1 — Glint  (first ~5 s): warm golden catch of evening light
     hitting the rim / surface of the cold glass.  Small, soft gleam.
     mix-blend-mode: screen adds brightness without covering colour.

   Phase 2 — Condensation (~25 s+): cool white-blue fog slowly builds
     on the glass body as moisture condenses in the warm evening air.
     mix-blend-mode: normal so the frosty tint is visible on the warm
     amber glass. Droplets appear staggered after the fog starts.
   ==================================================================== */

/* --- Glint: upper rim catch of golden evening light --- */
.sunday-tea-glint {
  position: absolute;
  left: 89.8%;
  top:  69.2%;
  width:  2.0%;
  height: 1.6%;
  border-radius: 50%;
  background: radial-gradient(ellipse at 32% 38%,
    rgba(255, 248, 205, 1.00)  0%,
    rgba(255, 225,  90, 0.72) 35%,
    rgba(255, 180,  20, 0.22) 65%,
    transparent               100%
  );
  mix-blend-mode: screen;
  opacity: 0;
  pointer-events: none;
}

/* --- Condensation fog: builds on the glass body over time --- */
.sunday-tea-condensation {
  position: absolute;
  left: 89.5%;
  top:  69.8%;
  width:  2.6%;
  height: 4.8%;
  border-radius: 18% 18% 28% 28%;
  background: radial-gradient(ellipse at 50% 60%,
    rgba(215, 232, 255, 0.62)  0%,
    rgba(200, 218, 255, 0.36) 48%,
    rgba(182, 208, 252, 0.10) 76%,
    transparent               100%
  );
  mix-blend-mode: normal;
  opacity: 0;
  pointer-events: none;
}

/* --- Condensation droplets: tiny water beads on the glass --- */
.sunday-tea-drop {
  position: absolute;
  border-radius: 50% 50% 58% 58%;
  background: rgba(208, 228, 255, 0.82);
  mix-blend-mode: normal;
  opacity: 0;
  pointer-events: none;
}
.sunday-tea-drop--1 { left: 90.2%; top: 72.5%; width: 0.18%; height: 0.30%; }
.sunday-tea-drop--2 { left: 90.8%; top: 71.6%; width: 0.14%; height: 0.24%; }
.sunday-tea-drop--3 { left: 91.2%; top: 73.3%; width: 0.13%; height: 0.22%; }
.sunday-tea-drop--4 { left: 90.4%; top: 73.9%; width: 0.11%; height: 0.17%; }

/* ---------------------------------------------------------
   MuSocí Outside world (1672 × 941 px artwork)
   Static background — no animation this pass.
   Mirrors Hip Hop Headroom structure exactly.
--------------------------------------------------------- */
.world--musoci-outside {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.musoci-outside-bg {
  position: absolute;
  inset: 0;
}
.musoci-outside-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}

/* ---------------------------------------------------------
   MuSocí Outside — car light glows (headlights + brake pulse)
   First animated layer of this world. Sized/positioned in JS
   (js/musoci-outside.js, fitOverlay — same cover-crop-aware
   technique proven on Sunday Grooves) so the dots land on the
   actual lights in the photo regardless of viewport aspect
   ratio. left:0/top:0/100%x100% here is just the pre-JS
   fallback, same reasoning as .sunday-lights-wrap.
--------------------------------------------------------- */
.outside-lights-wrap {
  position: absolute;
  left: 0; top: 0; width: 100%; height: 100%;
  pointer-events: none;
  z-index: 2;
}
.outside-light {
  position: absolute;
  border-radius: 50%;
  /* normal, not screen — screen barely moves the needle on top of
     pixels that are already near-white/near-max-brightness (headlight
     beams, LED taillights), same lesson Sunday Grooves' windows
     already taught us. normal composites as a translucent colored
     layer, so it reads regardless of how bright the pixels under it
     already are. */
  pointer-events: none;
  transform: translate(-50%, -50%);
}
/* Headlights — G-Wagon, already lit in the artwork. Sized well past
   the actual beam so the pulse has room to breathe into the darker
   grille/pavement around it — that halo is where the animation
   actually reads, not the already-blown-out core. */
.outside-light--headlight {
  width: 3.4%;
  height: 2.4%;
  background: radial-gradient(ellipse,
    rgba(225, 240, 255, 0.95)  0%,
    rgba(200, 225, 255, 0.55) 30%,
    rgba(170, 205, 255, 0.22) 55%,
    transparent               80%
  );
  animation: outside-headlight-breathe 4.6s ease-in-out infinite;
}
.outside-light--hl-l { left: 16.3%; top: 78.4%; animation-delay: 0s; }
.outside-light--hl-r { left: 32.5%; top: 78.4%; animation-delay: 0.6s; }
@keyframes outside-headlight-breathe {
  0%, 100% { opacity: 0.45; transform: translate(-50%, -50%) scale(0.9); }
  50%      { opacity: 0.90; transform: translate(-50%, -50%) scale(1.15); }
}
/* Taillights — sedan, idling at the crosswalk. Base glow breathes
   like running lights; .is-braking (added briefly by JS at random
   intervals) blooms it out like a real brake tap throwing red light
   onto the bumper and road behind it. */
.outside-light--taillight {
  width: 3.8%;
  height: 2.2%;
  background: radial-gradient(ellipse,
    rgba(255, 50,  30, 0.95)  0%,
    rgba(255, 25,  15, 0.55) 32%,
    rgba(220, 10,   0, 0.24) 58%,
    transparent               82%
  );
  animation: outside-taillight-breathe 5.2s ease-in-out infinite;
  transition: opacity 0.15s ease-out, transform 0.15s ease-out;
}
.outside-light--tl-l { left: 67.8%; top: 87.8%; }
.outside-light--tl-r { left: 91.5%; top: 87.8%; }
@keyframes outside-taillight-breathe {
  0%, 100% { opacity: 0.40; transform: translate(-50%, -50%) scale(0.9); }
  50%      { opacity: 0.70; transform: translate(-50%, -50%) scale(1.05); }
}
.outside-light--taillight.is-braking {
  opacity: 1 !important;
  transform: translate(-50%, -50%) scale(1.6) !important;
  animation-play-state: paused;
}
/* Second car — the one ahead of the FLEX sedan in the traffic queue,
   further down the street and noticeably smaller from this distance.
   Same brake-tap behavior, own independent random timer (js), scaled
   down and given a later animation-delay so the two cars' idle
   breathing never lines up either. */
.outside-light--taillight-sm {
  width: 1.6%;
  height: 0.9%;
  animation-delay: 1.4s;
}
.outside-light--tl2-l { left: 62.3%; top: 68.3%; }
.outside-light--tl2-r { left: 65.8%; top: 68.3%; }
/* Third car — even further back, deep enough in the queue that it
   only reads as one small center brake light (many cars' third brake
   light sits above the rear windshield), not two distinct sides. Own
   size, own delay, same random-timer brake tap for depth. */
.outside-light--taillight-xs {
  width: 0.8%;
  height: 0.5%;
  animation-delay: 2.6s;
}
.outside-light--tl3-c { left: 64.0%; top: 65.5%; }

/* Wall sconces — the facade's own warm cone lights, on the stone
   pillars either side of the MuSocí logo. Same anchored-glow
   technique as the headlights (small radial gradient sized past the
   fixture's own bright core, mix-blend-mode normal, breathe via
   opacity+scale), not the free-floating shapes that had to be
   scrapped for the pavement — a sconce has an actual bright point to
   anchor to, same as a headlight does. */
.outside-light--sconce {
  /* First pass was sized to roughly match the fixture's own already-
     lit cone, so the overlay was fully swallowed inside pixels that
     were already warm and bright — no contrast, nothing to see. Same
     lesson as the original headlight fix: size well past the bright
     core into the darker stone around it, and swing opacity/scale
     hard, so the pulse actually reads against that dark surrounding
     brick instead of blending into light that's already there. */
  width: 5.5%;
  height: 7%;
  background: radial-gradient(ellipse,
    rgba(255, 235, 195, 0.95) 0%,
    rgba(255, 190, 110, 0.55) 30%,
    rgba(255, 150, 60,  0.20) 55%,
    transparent                82%
  );
  animation: outside-sconce-breathe 5.4s ease-in-out infinite;
}
.outside-light--sc-1 { left: 1.7%;  top: 33.5%; animation-delay: 0s;   }
.outside-light--sc-2 { left: 1.5%;  top: 39.3%; animation-delay: 1.8s; }
.outside-light--sc-3 { left: 23.5%; top: 29.8%; animation-delay: 0.9s; }
@keyframes outside-sconce-breathe {
  0%, 100% { opacity: 0.18; transform: translate(-50%, -50%) scale(0.7); }
  50%      { opacity: 1.0;  transform: translate(-50%, -50%) scale(1.35); }
}

/* ---------------------------------------------------------
   MuSocí Outside — skyline warmth drift
   The background towers already have plenty of small lit windows;
   this doesn't add or move any of them (that would mean placing
   dozens of tiny points by hand). Instead it's a single band over
   the skyline using mix-blend-mode: color, which replaces hue/sat
   but keeps the backdrop's own luminance — so black sky and dark
   building silhouettes stay exactly as dark as they already are
   (verified in a proper W3C color-blend simulation before shipping,
   same discipline as the pavement-shimmer rebuild), while the
   already-lit windows and the warm base tone visibly drift through
   a slow cycle of ambers/golds/coppers. Reads as the skyline's own
   lights shifting color temperature, not a shape laid on top of it.

   Shipped once already as a child of .outside-lights-wrap and came
   out as a flat rectangle — mix-blend-mode only blends against
   whatever is painted earlier *within the same stacking context*,
   and .outside-lights-wrap has its own z-index, which makes it a
   new stacking context. Every light dot inside it blends fine with
   each other but can never reach the background photo one context
   up, so the color blend had nothing but transparent black to mix
   with. Fixed by giving this its own wrapper (.outside-skyline-warm-
   wrap) placed as a sibling of .musoci-outside-bg with no z-index of
   its own, right after it in DOM order — same stacking level as the
   photo, so the blend now actually reaches it. That wrapper gets the
   same JS cover-crop fit as .outside-lights-wrap (musoci-outside.js),
   since plain % positioning still needs that to land correctly. */
.outside-skyline-warm-wrap {
  position: absolute;
  left: 0; top: 0; width: 100%; height: 100%;
  pointer-events: none;
}
.outside-skyline-warm {
  /* Moved up and widened: was 36-52% (mostly the building silhouettes,
     which is why it read as sitting "on" the towers rather than in
     the sky), now 20-52% so it actually covers the sunset clouds
     above the rooflines, not just the buildings themselves. Alpha
     dropped from 0.70 to 0.30 for a different reason than position —
     at 0.70 the color-blend was strong enough to flatten the sky's
     own orange/purple cloud variation into a single flat hue (the
     "yellow blob" complaint). At 0.30 the original cloud color still
     reads through clearly; the drift nudges the whole sky's shade
     without overwriting it. */
  position: absolute;
  left: 28%;
  top: 20%;
  width: 59%;
  height: 32%;
  pointer-events: none;
  mix-blend-mode: color;
  animation: outside-skyline-drift 18s ease-in-out infinite;
}
@keyframes outside-skyline-drift {
  0%   { background-color: rgba(255, 179, 71,  0.30); }
  30%  { background-color: rgba(230, 70,  20,  0.30); }
  55%  { background-color: rgba(255, 140, 110, 0.30); }
  80%  { background-color: rgba(255, 205, 60,  0.30); }
  100% { background-color: rgba(255, 179, 71,  0.30); }
}

/* ---------------------------------------------------------
   MuSocí Outside — sign glows (storefront + vertical neon)
   Rectangular soft-glow overlays (not the circular dot pattern
   the car lights use) sized/positioned via left/top/width/height
   as literal box edges over each sign, measured directly off
   the artwork's bright pixels. Same non-negotiables the car
   lights already taught us: mix-blend-mode normal (screen is
   invisible against pixels this bright already), and glow
   generously oversized past the sign's own edges so the pulse
   reads in the surrounding dark brick instead of just recoloring
   already-lit pixels.
   Breathing is a slow ambient opacity/scale drift. Flicker is a
   separate, rare (JS, ~8-22s random) stutter class that briefly
   interrupts the breathe animation to read as a real neon tube
   flicker, then hands back to the breathe loop.
--------------------------------------------------------- */
.outside-sign-glow {
  position: absolute;
  pointer-events: none;
  mix-blend-mode: normal;
  border-radius: 14%;
  filter: blur(7px);
  animation: outside-sign-breathe 6s ease-in-out infinite;
  transform-origin: center center;
}
@keyframes outside-sign-breathe {
  0%, 100% { opacity: 0.45; transform: scale(0.96); }
  50%      { opacity: 0.80; transform: scale(1.04); }
}
.outside-sign-glow.is-flickering {
  animation: outside-sign-flicker 0.55s steps(1, end) 1 !important;
}
@keyframes outside-sign-flicker {
  0%   { opacity: 0.85; }
  10%  { opacity: 0.15; }
  20%  { opacity: 0.80; }
  32%  { opacity: 0.10; }
  46%  { opacity: 0.75; }
  60%  { opacity: 0.20; }
  76%  { opacity: 0.85; }
  100% { opacity: 0.85; }
}
/* Storefront sign — logo + "MuSocí" wordmark. Sits against busy,
   textured, already-lit brick (sconces, patio string lights above),
   unlike the vertical sign's flat dark backdrop — so this one needs
   to hug the actual lit pixels closely and stay very soft, or the
   halo reads as a pasted-on blob against the texture instead of
   part of the sign. Tightened to the measured bright-pixel bbox
   (no bleed past the sign's own edges), much larger blur so it has
   no discernible border, and a lower opacity ceiling + its own
   gentler flicker so it never flashes bright enough to separate
   from the brick around it. */
.outside-sign-glow--store {
  left: 3.0%;
  top: 20.0%;
  width: 15.9%;
  height: 23.9%;
  filter: blur(12px);
  background: radial-gradient(ellipse closest-side at center,
    rgba(255, 255, 255, 0.28) 0%,
    rgba(255, 130, 205, 0.18) 45%,
    rgba(140, 195, 255, 0.09) 68%,
    transparent 82%
  );
  animation-delay: 0s;
}
.outside-sign-glow--store.is-flickering {
  animation: outside-sign-flicker-soft 0.55s steps(1, end) 1 !important;
}
@keyframes outside-sign-flicker-soft {
  0%   { opacity: 0.70; }
  10%  { opacity: 0.10; }
  20%  { opacity: 0.65; }
  32%  { opacity: 0.08; }
  46%  { opacity: 0.62; }
  60%  { opacity: 0.15; }
  76%  { opacity: 0.70; }
  100% { opacity: 0.70; }
}
/* "GOOD MUSIC. GOOD PEOPLE. GOOD VIBES." vertical neon — lavender/
   purple bloom matching the tube color, own delay so the two signs
   never breathe in sync. */
.outside-sign-glow--vibes {
  /* Tightened to the sign's own measured bright-pixel bbox (was
     oversized past the actual neon border into the brick/sky around
     it) — that overshoot is exactly what was reading as a visible
     rectangle: a translucent box brightening as a whole against
     plain, mostly-uniform surroundings, independent of the gradient
     edge-cutoff bug already fixed via closest-side. Same lesson the
     store sign already taught: hug the real lit pixels, don't
     generously overshoot, on a background this plain/dark. */
  left: 83.1%;
  top: 8.8%;
  width: 10.9%;
  height: 40.4%;
  filter: blur(14px);
  background: radial-gradient(ellipse closest-side at center,
    rgba(222, 160, 255, 0.30) 0%,
    rgba(196, 110, 255, 0.20) 42%,
    rgba(150, 75, 220, 0.11) 68%,
    transparent 78%
  );
  animation-delay: 2.4s;
}

/* One letter of "GOOD VIBES." dies and relights — the terminal S.
   Measured directly on the glyph (91.0-91.9% x, 41.6-43.7% y) with a
   little padding so the patch also covers the letter's own glow, not
   just its core. This is a literal dark patch (not a glow, not a
   blend trick) painted over the lit letter in the same near-black
   as the sign's own interior (sampled off the photo: rgb(6,2,16)) —
   opaque, mix-blend-mode normal, so it reliably reads as "the tube
   is dark" regardless of what's under it, same reliability car
   lights' normal-blend approach already proved.
   JS (musoci-outside.js) drives the state machine: long random
   interval on its own timer → quick stutter to fully out → holds
   dark a few seconds → quick stutter back to lit → reschedules.
   steps(1,end) on both keyframes makes each stop an instant jump
   rather than a smooth fade, which is what actually reads as a
   flicker instead of a dim/brighten pulse. */
.outside-sign-letter-out {
  /* Widened to also cover the period right after the S (was leaving
     it lit, which read as the S vanishing into a gap rather than
     the tube actually going dark) and pulled the left edge in off
     the E (was bleeding into it). Blur trimmed down to match the
     tighter margin against the E. */
  position: absolute;
  left: 91.8%;
  top: 42.6%;
  width: 1.7%;
  height: 2.4%;
  pointer-events: none;
  background: rgb(6, 2, 16);
  filter: blur(1.5px);
  border-radius: 25%;
  opacity: 0;
  transform: translate(-50%, -50%);
}
.outside-sign-letter-out.is-flicker-off {
  animation: outside-letter-flicker-off 0.7s steps(1, end) 1 forwards;
}
.outside-sign-letter-out.is-out {
  opacity: 1;
}
.outside-sign-letter-out.is-flicker-on {
  animation: outside-letter-flicker-on 0.7s steps(1, end) 1 forwards;
}
@keyframes outside-letter-flicker-off {
  0%   { opacity: 0;   }
  10%  { opacity: 0.8; }
  18%  { opacity: 0.1; }
  28%  { opacity: 0.9; }
  38%  { opacity: 0.2; }
  50%  { opacity: 1;   }
  65%  { opacity: 0.3; }
  80%  { opacity: 1;   }
  100% { opacity: 1;   }
}
@keyframes outside-letter-flicker-on {
  0%   { opacity: 1;   }
  12%  { opacity: 0.2; }
  22%  { opacity: 0.9; }
  35%  { opacity: 0.1; }
  48%  { opacity: 0.8; }
  60%  { opacity: 0;   }
  75%  { opacity: 0.6; }
  100% { opacity: 0;   }
}

/* ---------------------------------------------------------
   MuSocí Outside — wet-pavement shimmer
   Foreground street between the two cars is already rendered with
   baked-in wet-asphalt reflections (crosswalk tile glare, a purple
   puddle catching the storefront's glow bottom-left, red pooling
   under the sedan's taillights). This layer doesn't invent new
   reflections — it makes the ones already there gently ripple, the
   same "enhance what's already lit" approach used for the car
   lights and signs, so it reads as motion in the water rather than
   a new light source.
   No JS here: each patch runs a continuous CSS drift (opacity +
   slight side-to-side wobble, like a puddle catching passing light)
   at its own duration/delay, which is enough staggering for a
   continuous ambient effect — same pattern the headlight/taillight
   *breathe* animations already use. JS random-timer scheduling is
   reserved for discrete one-off events (brake taps, sign flicker),
   not this kind of always-on ambient drift.
   Kept deliberately subtle (peak opacity ~0.25-0.55, closest-side
   gradients, generous blur) — this is meant to be felt more than
   seen, on top of detail that's already there.
--------------------------------------------------------- */
.outside-shimmer {
  /* Fourth build. Attempts 1-3 all invented a new highlight shape in
     open space (blob, sweep, then a soft-light ellipse) — even with
     correct blend math, a smooth isolated shape this size reads as a
     sticker because nothing about it matches the jagged, broken-up
     texture of real reflections on grouted tile.
     This build doesn't invent a shape at all. Positions are the exact
     coordinates of small bright specks the photo already has (found
     programmatically by scanning for local brightness peaks in the
     open-pavement zones, then eyeballed at 6x zoom to confirm each is
     a genuine highlight and not a car/pedestrian edge) — this element
     just makes an already-existing speck of light quietly pulse
     brighter and dimmer. Deliberately tiny (~2% of the image) and
     capped well below full opacity so it enhances, it doesn't add.
     mix-blend-mode: soft-light kept from the last attempt — that part
     was correct, it was the size/isolation that was wrong, not the
     blend mode. */
  position: absolute;
  pointer-events: none;
  mix-blend-mode: soft-light;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  width: 2.1%;
  height: 1.9%;
  background: radial-gradient(ellipse closest-side at center,
    rgba(255, 255, 255, 0.95) 0%,
    rgba(255, 255, 255, 0.45) 50%,
    transparent 80%
  );
  animation-name: outside-shimmer-glint;
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-duration: var(--sweep-dur, 7s);
  animation-delay: var(--sweep-delay, 0s);
}
@keyframes outside-shimmer-glint {
  0%, 100% { opacity: 0.22; transform: translate(-50%, -50%) scale(0.8); }
  50%      { opacity: 0.65; transform: translate(-50%, -50%) scale(1.15); }
}
.outside-shimmer--crosswalk {
  /* Small bright speck already visible in the wet cobble just past
     the crosswalk's edge. */
  left: 48%;
  top: 94.3%;
  --sweep-dur: 7s;
}
.outside-shimmer--puddle-l {
  /* Existing highlight inside the purple puddle. */
  left: 11.2%;
  top: 88.3%;
  --sweep-dur: 8.5s;
  --sweep-delay: 1.5s;
}
.outside-shimmer--puddle-r {
  /* Reflection pooling on the pavement immediately left of the
     sedan, not on the car body itself. */
  left: 61.5%;
  top: 80%;
  --sweep-dur: 6.5s;
  --sweep-delay: 3s;
}
.outside-shimmer--mid {
  /* Existing highlight on the crosswalk stripe itself, between the
     other two. */
  left: 53.7%;
  top: 90.9%;
  --sweep-dur: 9s;
  --sweep-delay: 4.2s;
}

/* ---------------------------------------------------------
   MuSocí Outside — animated tagline overlay
   "Good Music, Good People, Good Vibes"

   Placed in the lower-center of the world section, over
   the dark wet pavement — the clearest readable zone in
   the image.  JS controls the staggered fade-in; all three
   phrases start at opacity:0 and stay there until the world
   is active.
--------------------------------------------------------- */
.musoci-outside-tagline {
  position: absolute;
  top: 43%;          /* just below the "Outside" neon title */
  left: 0;
  width: 100%;
  text-align: center;
  pointer-events: none;
  z-index: 2;
  white-space: nowrap;
}

.musoci-outside-phrase {
  /* All three phrases are absolutely stacked so --3 ("We Outside")
     can sit in the same vertical zone as --1/--2 without layout shift */
  position: absolute;
  left: 0;
  width: 100%;
  text-align: center;
  font-family: var(--font-display);
  font-style: italic;
  font-weight: 400;
  font-size: clamp(1.05rem, 1.9vw, 1.65rem);
  letter-spacing: 0.09em;
  line-height: 1.55;
  color: #faefd4;
  text-shadow:
    0 1px 10px rgba(0, 0, 0, 0.90),
    0 0  40px rgba(0, 0, 0, 0.55);
  opacity: 0;
}

/* --1: line-1 wrapper — always transparent (inner words control their own opacity)
   Position: absolute top:0, same slot as --3. opacity:1 so the wrapper
   never hides the children; JS fades the children individually. */
.musoci-outside-phrase--1 { top: 0; opacity: 1; }

/* Inline word fragments inside --1: JS fades each in separately */
.musoci-word {
  display: inline;
  opacity: 0;        /* JS takes over from here */
}

/* --3: "We Outside" sits at same vertical position as --1 so it
   replaces it cleanly with no layout shift */
.musoci-outside-phrase--3 {
  top: 0;
  font-size: clamp(1.15rem, 2.1vw, 1.80rem);
  font-weight: 500;
  letter-spacing: 0.11em;
}

/* ---------------------------------------------------------
   5 O'Clock Flex world  (1672 × 941 px artwork)
   Schedule-only — no tile in the lineup.
   Static background; title is baked into the artwork.
   Image: assets/shows/5_0clock_flex_wide.PNG
   Preview URL: index.html?world=5-oclock-flex
--------------------------------------------------------- */
.world--5-oclock-flex {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.flex5-bg {
  position: absolute;
  inset: 0;
}
.flex5-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}
/* ---------------------------------------------------------
   5 O'Clock Flex — Streetlamp glow flicker
   Four lamps: L1 foreground-left, L2 mid-left (receding),
   R1 foreground-right, R2 mid-right (receding).
   mix-blend-mode: screen brightens the existing photo glow.
   Staggered --lamp-delay keeps them from pulsing in unison.
--------------------------------------------------------- */
.flex5-lamp {
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
  mix-blend-mode: screen;
  transform: translate(-50%, -50%);
  background: radial-gradient(
    circle,
    rgba(255, 225, 110, 0.50) 0%,
    rgba(255, 165,  45, 0.24) 35%,
    rgba(255, 120,  20, 0.08) 60%,
    transparent 75%
  );
  animation: lamp-flicker var(--lamp-dur, 2.8s) ease-in-out infinite;
  animation-delay: var(--lamp-delay, 0s);
  z-index: 1;
  will-change: opacity, filter;
}

/* Foreground lamps are larger; receding ones smaller */
.flex5-lamp--L1 { width: 5.5%; height: 10%; left: 21.11%; top: 10.63%; --lamp-dur: 2.6s; --lamp-delay: 0.0s; }
.flex5-lamp--L2 { width: 2.8%; height:  5%; left: 28.53%; top: 24.23%; --lamp-dur: 3.1s; --lamp-delay: 0.7s; }
.flex5-lamp--R1 { width: 4.5%; height:  8%; left: 70.75%; top: 16.68%; --lamp-dur: 2.9s; --lamp-delay: 1.3s; }

@keyframes lamp-flicker {
  0%   { opacity: 0.55; filter: brightness(1.00); }
  14%  { opacity: 0.78; filter: brightness(1.12); }
  27%  { opacity: 0.46; filter: brightness(0.91); }
  44%  { opacity: 0.75; filter: brightness(1.09); }
  61%  { opacity: 0.42; filter: brightness(0.89); }
  77%  { opacity: 0.70; filter: brightness(1.07); }
  90%  { opacity: 0.58; filter: brightness(0.96); }
  100% { opacity: 0.55; filter: brightness(1.00); }
}

/* ---------------------------------------------------------
   5 O'Clock Flex — Animated text intro
   Placement: top 7%, centered — clean golden-sky band above
   the streetscape, no competing content.
   ?place=1 mode: all phrases visible at opacity:1 for nudging.
--------------------------------------------------------- */
.flex5-tagline {
  position: absolute;
  top: 7%;
  left: 0;
  width: 100%;
  text-align: center;
  pointer-events: none;
  z-index: 2;
}

/* Shared phrase base — JS drives opacity & transform from here */
.flex5-phrase {
  display: block;
  font-family: var(--font-display);
  color: #fff6e3;
  opacity: 0;
}

/* Title — Exo 2 (geometric sans) to match the clean, even-weight
   car infotainment display in the artwork. Uppercase, spaced out.
   Fraunces (site default) would feel wrong here — this needs techy,
   modern, even strokes. Exo 2 at 600 is the closest web-font match. */
.flex5-phrase--title {
  font-family: 'Exo 2', 'Inter', sans-serif;
  font-size: clamp(1.7rem, 3.0vw, 2.6rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.10em;
  line-height: 1.2;
  text-shadow:
    0 2px 18px rgba(0, 0, 0, 0.88),
    0 0  55px rgba(0, 0, 0, 0.55);
  transform: translateY(-40px);   /* reset by JS after animation */
  will-change: transform, opacity, filter;
}

/* Subtitle lines — italic, lighter */
.flex5-phrase--line1,
.flex5-phrase--line2 {
  font-size: clamp(0.88rem, 1.55vw, 1.35rem);
  font-weight: 400;
  font-style: italic;
  letter-spacing: 0.09em;
  line-height: 1.55;
  text-shadow:
    0 1px 10px rgba(0, 0, 0, 0.90),
    0 0  38px rgba(0, 0, 0, 0.55);
}
.flex5-phrase--line1 { margin-top: 0.55em; }
.flex5-phrase--line2 { margin-top: 0.30em; }

/* Drop-and-settle keyframe for the title:
   Falls from -40px, overshoots 6px, bounces back to rest.
   Easing on JS side; forward-fill keeps final state. */
.flex5-eq {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;           /* below tagline text (z-index:2) */
}

@keyframes flex5-title-drop {
  0%   { transform: translateY(-40px); opacity: 0; }
  60%  { transform: translateY(6px);   opacity: 1; }
  78%  { transform: translateY(-3px);  opacity: 1; }
  90%  { transform: translateY(2px);   opacity: 1; }
  100% { transform: translateY(0);     opacity: 1; }
}



/* =========================================================
   Terms of Service page  (terms.html)
   ========================================================= */

/* Outer wrapper — full-page dark background */
.terms-page {
  background: var(--bg-0);
  min-height: 100vh;
  padding: var(--space-5) var(--space-2) var(--space-6);
}

/* Inner document column — comfortable reading width, centered */
.terms-doc {
  max-width: 760px;
  margin: 0 auto;
}

/* Page heading block */
.terms-header {
  text-align: center;
  padding-bottom: var(--space-5);
  border-bottom: 1px solid rgba(255,255,255,0.08);
  margin-bottom: var(--space-5);
}

.terms-title {
  font-family: var(--font-display);
  font-size: clamp(1.8rem, 4vw, 2.8rem);
  font-weight: 500;
  line-height: 1.2;
  color: var(--text-0);
  margin: 0 0 var(--space-2);
  /* subtle brand gradient on the title */
  background: var(--grad-text);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.terms-date {
  font-size: 0.875rem;
  color: var(--text-muted);
  margin: 0 0 var(--space-2);
  letter-spacing: 0.03em;
}

.terms-date strong {
  color: var(--text-1);
}

.terms-intro {
  font-size: 0.95rem;
  color: var(--text-1);
  line-height: 1.7;
  max-width: 640px;
  margin: 0 auto;
}

/* Individual section */
.terms-section {
  padding: var(--space-4) 0;
  border-bottom: 1px solid rgba(255,255,255,0.06);
}

.terms-section:last-of-type {
  border-bottom: none;
}

/* Section heading (h2) */
.terms-h2 {
  font-family: var(--font-display);
  font-size: clamp(1rem, 2.2vw, 1.25rem);
  font-weight: 500;
  color: var(--text-0);
  margin: 0 0 var(--space-2);
  padding-bottom: 0.4rem;
  border-bottom: 1px solid rgba(58,160,200,0.25);
  letter-spacing: 0.01em;
}

/* Sub-section heading (h3) — for 19.1–19.14 etc. */
.terms-h3 {
  font-family: var(--font-body);
  font-size: 0.975rem;
  font-weight: 600;
  color: var(--color-blue);
  margin: var(--space-3) 0 var(--space-1);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  font-size: 0.825rem;
}

/* Body text */
.terms-body {
  color: var(--text-1);
  font-size: 0.9375rem;   /* 15px — comfortable legal reading size */
  line-height: 1.75;
}

.terms-body p {
  margin: 0 0 var(--space-2);
}

.terms-body p:last-child {
  margin-bottom: 0;
}

.terms-body strong, .terms-body b {
  color: var(--text-0);
  font-weight: 600;
}

.terms-body ul, .terms-body ol {
  padding-left: 1.6em;
  margin: 0 0 var(--space-2);
}

.terms-body li {
  margin-bottom: 0.4em;
  line-height: 1.65;
}

.terms-body a {
  color: var(--color-blue);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.terms-body a:hover {
  color: var(--text-0);
}

/* Back/nav links at the bottom */
.terms-back {
  padding: var(--space-4) 0 var(--space-2);
  text-align: center;
  font-size: 0.85rem;
}

.terms-back a {
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.2s;
}

.terms-back a:hover {
  color: var(--text-0);
}

/* Footer legal link row */
.footer-legal {
  margin: var(--space-1) 0 0;
  font-size: 0.8rem;
}

.footer-legal a {
  color: var(--text-muted);
  text-decoration: none;
  transition: color 0.2s;
}

.footer-legal a:hover {
  color: var(--text-0);
}

/* Mobile */
@media (max-width: 640px) {
  .terms-page {
    padding: var(--space-4) var(--space-2) var(--space-5);
  }
  .terms-body {
    font-size: 0.9rem;
  }
}


/* =============================================================
   Midday Bounce — Title Animation (v201)
   Two-word entrance: MIDDAY zooms in, BOUNCE drops + bounces.
   Gated to .is-active on [data-world-id="midday-bounce"].
   Replay on every activation via JS class reset.
   ============================================================= */

.mb-title {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.06em;
  pointer-events: none;
  z-index: 2;
}

.mb-word {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 7vw, 6rem);
  font-weight: 600;
  letter-spacing: 0.1em;
  color: #ffffff;
  text-shadow: 0 4px 28px rgba(0,0,0,0.60), 0 2px 8px rgba(0,0,0,0.80);
  line-height: 1;
  opacity: 0;
  will-change: transform, opacity;
  display: block;
}

@keyframes mb-zoom-in {
  from { transform: scale(2.5); opacity: 0; }
  to   { transform: scale(1);   opacity: 1; }
}

@keyframes mb-drop-bounce {
  0%   { transform: translateY(-80px); opacity: 0;
         animation-timing-function: ease-in; }
  38%  { transform: translateY(0);     opacity: 1;
         animation-timing-function: ease-out; }
  50%  { transform: translateY(-36px);
         animation-timing-function: ease-in; }
  61%  { transform: translateY(0);
         animation-timing-function: ease-out; }
  70%  { transform: translateY(-14px);
         animation-timing-function: ease-in; }
  78%  { transform: translateY(0);
         animation-timing-function: ease-out; }
  84%  { transform: translateY(-5px);
         animation-timing-function: ease-in; }
  89%  { transform: translateY(0); }
  100% { transform: translateY(0); opacity: 1; }
}

.mb-word--midday.mb-animate {
  animation: mb-zoom-in 0.85s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  animation-delay: 2s;
}

.mb-word--bounce.mb-animate {
  animation: mb-drop-bounce 1.15s ease forwards;
  animation-delay: 2.7s;
}

@media (prefers-reduced-motion: reduce) {
  .mb-word--midday.mb-animate,
  .mb-word--bounce.mb-animate {
    animation: none;
    opacity: 1;
  }
}

/* =============================================================
   Day Shift — Sunrise Title Animation (v206)
   Colors matched to Day_Shift_Lineup tile:
     "DAY"   — warm gold  #E8B84B
     "SHIFT" — sky-blue → purple gradient (matches tile sky palette)
   Font: Exo 2 600 (loaded) for modern condensed-sans feel.
   Animation: unified sunrise — brightness + saturation + warm
   drop-shadow rise over 1.8 s. Subtle upward drift.
   Triggers: IntersectionObserver (scroll) + MutationObserver
   (is-active). Replays on every re-entry. prefers-reduced-
   motion: skip to final state.
   ============================================================= */

/* ── Container ────────────────────────────────────────────── */
.ds-title {
  position: absolute;
  top: 8%;
  right: 5%;
  text-align: right;
  pointer-events: none;
  z-index: 2;
  opacity: 0;         /* hidden until .ds-animate is added    */
  will-change: transform, opacity, filter;
}

/* ── Words ────────────────────────────────────────────────── */
.ds-word {
  font-family: 'Exo 2', sans-serif;
  font-size: clamp(2.4rem, 7.5vw, 6.5rem);
  font-weight: 600;
  letter-spacing: 0.04em;
  line-height: 0.92;
  text-transform: uppercase;
  display: block;
}

/* "DAY" — warm gold, matching the tile */
.ds-word--day {
  color: #E8B84B;
}

/* "SHIFT" — sky-blue to purple gradient, matching tile palette */
.ds-word--shift {
  background: linear-gradient(to bottom, #0391C1 0%, #2B3993 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

/* ── Keyframe: warm sunrise rise ──────────────────────────── */
/*  0 %  pre-dawn: almost black, fully desaturated
   25 %  first light: dim, colour starts returning
   65 %  golden hour: nearly full colour, warm glow building
   82 %  sun crests horizon: brief overbright, peak glow
  100 %  settled daylight: natural colour + soft residual glow */
@keyframes ds-sunrise {
  0% {
    opacity: 0;
    transform: translateY(14px);
    filter: brightness(0.12) saturate(0)
            drop-shadow(0 0 0px rgba(255,155,40,0));
  }
  25% {
    opacity: 0.35;
    transform: translateY(7px);
    filter: brightness(0.40) saturate(0.45)
            drop-shadow(0 0 10px rgba(255,155,40,0.25));
  }
  65% {
    opacity: 0.88;
    transform: translateY(1px);
    filter: brightness(0.95) saturate(1.0)
            drop-shadow(0 0 22px rgba(255,155,40,0.55));
  }
  82% {
    opacity: 1;
    transform: translateY(0);
    filter: brightness(1.18) saturate(1.08)
            drop-shadow(0 0 28px rgba(255,155,40,0.65));
  }
  100% {
    opacity: 1;
    transform: translateY(0);
    filter: brightness(1.0) saturate(1.0)
            drop-shadow(0 0 14px rgba(255,155,40,0.30));
  }
}

/* ── Activation ───────────────────────────────────────────── */
.ds-title.ds-animate {
  animation: ds-sunrise 1.8s cubic-bezier(0.25, 0.1, 0.25, 1) forwards;
}

/* Reduced motion: skip to final state immediately */
@media (prefers-reduced-motion: reduce) {
  .ds-title.ds-animate {
    animation: none;
    opacity: 1;
    filter: none;
    transform: none;
  }
}

/* =========================================================
   SHOW SEO PAGE — small EQ bars (generic, reusable)
   A tiny animated audio-meter for programming/*.html hero
   images, so each show page carries at least one small,
   always-on animated element alongside its title treatment.
   Reuses the same eq-p1..eq-p9 keyframes as the Soul EQ
   overlay in the World section.
   ========================================================= */
.show-eq {
  position: absolute;
  left: 1.75rem;
  bottom: 1.75rem;
  z-index: 3;
  width: 64px;
  height: 42px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  pointer-events: none;
  filter: drop-shadow(0 0 8px rgba(232,184,75,0.45));
}
.show-eq-bar {
  display: block;
  width: 15%;
  height: 100%;
  background: linear-gradient(to top, rgba(232,184,75,0.98) 0%, rgba(255,210,110,0.75) 100%);
  border-radius: 2px 2px 0 0;
  transform-origin: bottom center;
  transform: scaleY(0.2);
}
.show-eq-bar--1 { animation: eq-p1 3.2s ease-in-out infinite; animation-delay: 0s; }
.show-eq-bar--2 { animation: eq-p2 2.8s ease-in-out infinite; animation-delay: -0.8s; }
.show-eq-bar--3 { animation: eq-p4 2.5s ease-in-out infinite; animation-delay: -0.3s; }
.show-eq-bar--4 { animation: eq-p6 2.9s ease-in-out infinite; animation-delay: -1.7s; }
.show-eq-bar--5 { animation: eq-p8 3.5s ease-in-out infinite; animation-delay: -2.8s; }
@media (prefers-reduced-motion: reduce) {
  .show-eq-bar { animation: none; transform: scaleY(0.55); }
}


/* =========================================================
   ARTIST JOURNEY PAGE
   ========================================================= */

.journey-page-wrap {
  background: linear-gradient(
    to bottom,
    #08080f  0%,
    #0c0c1a 15%,
    #0e0a1e 30%,
    #100c20 45%,
    #0f0c18 60%,
    #120e14 75%,
    #130f0a 88%,
    #0a0800 100%
  );
  min-height: 100vh;
}

/* ---- Hero ---- */
.journey-hero {
  text-align: center;
  padding: var(--space-6) var(--space-3) var(--space-5);
  position: relative;
}

.journey-hero::after {
  content: '';
  display: block;
  width: 1px;
  height: 56px;
  background: linear-gradient(to bottom, rgba(232,184,75,0.55), rgba(232,184,75,0));
  margin: var(--space-4) auto 0;
}

.journey-hero__eyebrow {
  font-family: var(--font-display);
  font-size: 0.72rem;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  color: #e8b84b;
  margin: 0 0 var(--space-2);
  opacity: 0.8;
}

.journey-hero__title {
  font-family: var(--font-display);
  font-size: clamp(2.2rem, 6vw, 4.2rem);
  font-weight: 600;
  color: var(--text-0);
  margin: 0 0 var(--space-2);
  line-height: 1.1;
}

.journey-hero__sub {
  font-size: clamp(0.95rem, 2.4vw, 1.15rem);
  color: var(--text-1);
  margin: 0 auto;
  max-width: 40ch;
  line-height: 1.6;
}

/* ---- Climb container ---- */
.journey-climb {
  max-width: 700px;
  margin: 0 auto;
  padding: var(--space-3) var(--space-3) var(--space-6);
  position: relative;
}

/* Vertical connector line */
.journey-climb::before {
  content: '';
  position: absolute;
  left: calc(var(--space-3) + 1.15rem);
  top: 0;
  bottom: 0;
  width: 2px;
  background: linear-gradient(
    to bottom,
    rgba(80, 90, 160, 0.15) 0%,
    rgba(110, 80, 180, 0.30) 40%,
    rgba(160, 90, 210, 0.45) 70%,
    rgba(232, 184, 75, 0.60) 100%
  );
  pointer-events: none;
}

@media (min-width: 640px) {
  .journey-climb::before {
    left: calc(var(--space-3) + 1.15rem);
  }
}

/* ---- Stage cards ---- */
.journey-stage {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  margin-bottom: var(--space-4);
  position: relative;
  opacity: 0;
  transform: translateY(22px);
  transition: opacity 0.65s ease, transform 0.65s ease;
}

.journey-stage.js-revealed {
  opacity: 1;
  transform: translateY(0);
}

@media (prefers-reduced-motion: reduce) {
  .journey-stage {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* Marker circle */
.journey-stage__marker {
  flex: none;
  width: 2.3rem;
  height: 2.3rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95rem;
  color: #fff;
  position: relative;
  z-index: 1;
  transition: box-shadow 0.65s ease;
  flex-shrink: 0;
  margin-top: 0.2rem;
}

/* Body card */
.journey-stage__body {
  flex: 1;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-radius: 10px;
  padding: var(--space-2) var(--space-3);
  transition: background 0.65s ease, border-color 0.65s ease, box-shadow 0.65s ease;
}

.journey-stage.js-revealed .journey-stage__body {
  background: rgba(255, 255, 255, 0.055);
  border-color: rgba(255, 255, 255, 0.11);
}

.journey-stage__name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.05rem, 2.4vw, 1.25rem);
  margin: 0 0 0.35rem;
  line-height: 1.2;
}

.journey-stage__tagline {
  font-size: clamp(0.88rem, 2vw, 0.98rem);
  color: var(--text-1);
  margin: 0;
  line-height: 1.55;
}

/* --- Stage-specific marker backgrounds --- */
.stage-1  .journey-stage__marker { background: #252848; }
.stage-2  .journey-stage__marker { background: #2c3060; }
.stage-3  .journey-stage__marker { background: #313572; }
.stage-4  .journey-stage__marker { background: #393c82; }
.stage-5  .journey-stage__marker { background: #254870; }
.stage-6  .journey-stage__marker { background: #482a80; }
.stage-7  .journey-stage__marker { background: #582e92; }
.stage-8  .journey-stage__marker { background: #682ea8; }
.stage-9  .journey-stage__marker {
  background: linear-gradient(135deg, #b07820 0%, #e8b84b 100%);
  box-shadow: 0 0 18px rgba(232, 184, 75, 0.45);
}

/* --- Stage-specific name colors --- */
.stage-1  .journey-stage__name { color: #7a88be; }
.stage-2  .journey-stage__name { color: #8896ca; }
.stage-3  .journey-stage__name { color: #96a4d6; }
.stage-4  .journey-stage__name { color: #a0aee0; }
.stage-5  .journey-stage__name { color: #5aaad8; }
.stage-6  .journey-stage__name { color: #b07ad8; }
.stage-7  .journey-stage__name { color: #c088e8; }
.stage-8  .journey-stage__name { color: #d09cf8; }
.stage-9  .journey-stage__name { color: #e8b84b; }

/* --- Stage 5: Amplification (optional) --- */
.stage-5 .journey-stage__body {
  border-color: rgba(90, 170, 216, 0.12);
}

.stage-5.js-revealed .journey-stage__body {
  border-color: rgba(90, 170, 216, 0.22);
  background: rgba(90, 170, 216, 0.04);
}

/* --- Stage 9: Legacy — most luminous --- */
.stage-9 .journey-stage__body {
  background: rgba(232, 184, 75, 0.05);
  border-color: rgba(232, 184, 75, 0.18);
}

.stage-9.js-revealed .journey-stage__body {
  background: rgba(232, 184, 75, 0.07);
  border-color: rgba(232, 184, 75, 0.35);
  box-shadow: 0 0 48px rgba(232, 184, 75, 0.12) inset,
              0 4px 32px rgba(232, 184, 75, 0.10);
}

.stage-9.js-revealed .journey-stage__marker {
  box-shadow: 0 0 28px rgba(232, 184, 75, 0.65);
}

/* --- Optional badge (Amplification) --- */
.journey-badge--optional {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #5aaad8;
  border: 1px solid rgba(90, 170, 216, 0.40);
  border-radius: 4px;
  padding: 0.12em 0.5em;
  margin-left: 0.55rem;
  vertical-align: middle;
  line-height: 1.5;
}

/* --- "Earned, never bought" label (Spotlight) --- */
.journey-earned-label {
  display: block;
  font-family: var(--font-display);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: #d09cf8;
  margin-top: 0.55rem;
  opacity: 0.85;
}

/* --- Legacy summit label --- */
.journey-summit-label {
  display: block;
  font-family: var(--font-display);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: #e8b84b;
  margin-top: 0.55rem;
  opacity: 0.75;
}

/* --- Back link --- */
.journey-back {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  color: var(--text-muted);
  font-size: 0.88rem;
  text-decoration: none;
  padding: var(--space-3) var(--space-3) 0;
  display: block;
  transition: color 0.2s;
}

.journey-back:hover { color: var(--text-1); }

/* ---- Homepage Journey CTA ---- */
.journey-cta {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  margin-top: var(--space-3);
  color: var(--text-1);
  font-size: 0.95rem;
  font-family: var(--font-display);
  font-weight: 500;
  text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,0.18);
  padding-bottom: 0.15em;
  transition: color 0.2s, border-color 0.2s;
}

.journey-cta:hover {
  color: var(--text-0);
  border-color: rgba(255,255,255,0.45);
}

/* ---- Additional journey badge types (v210) ---- */
.journey-badge--editorial {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #9878d0;
  border: 1px solid rgba(152, 120, 208, 0.30);
  border-radius: 4px;
  padding: 0.12em 0.5em;
  margin-left: 0.55rem;
  vertical-align: middle;
  line-height: 1.5;
}

.journey-badge--free {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.62rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #52c090;
  border: 1px solid rgba(82, 192, 144, 0.30);
  border-radius: 4px;
  padding: 0.12em 0.5em;
  margin-left: 0.55rem;
  vertical-align: middle;
  line-height: 1.5;
}

/* Body paragraph below the tagline */
.journey-stage__desc {
  font-size: 0.9rem;
  color: var(--text-1);
  margin: 0.5rem 0 0;
  line-height: 1.65;
}

/* Spotlight Award — proper name treatment */
.journey-spotlight-award {
  color: #d09cf8;
  font-style: normal;
  font-weight: 600;
}

/* =========================================================
   WORLD STAGE — "Now Airing" live label  (v213)
   ========================================================= */

.world-now-airing {
  position: absolute;
  top: 1.1rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  background: rgba(10, 8, 20, 0.60);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255, 255, 255, 0.13);
  border-radius: 999px;
  padding: 0.35em 1.1em 0.35em 0.85em;
  font-family: var(--font-display);
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.78);
  white-space: nowrap;
  pointer-events: none;
  transition: opacity 0.4s ease;
}

.world-now-airing__dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--color-pink);
  animation: world-dot-pulse 2.2s ease-in-out infinite;
}

@keyframes world-dot-pulse {
  0%, 100% { opacity: 1;   transform: scale(1); }
  50%       { opacity: 0.4; transform: scale(0.75); }
}

/* ---------------------------------------------------------------
   CHANNEL-CHANGE TRANSITION
   world-stage.js toggles is-rolling / is-static / is-static-out /
   is-idshow on #world-stage whenever the active show genuinely
   changes — either the real schedule flipping (world.html) or a
   listener clicking a Lineup tile (index.html). Shared here so
   both contexts get the same static/glitch/channel-ID moment.
   The JS no-ops gracefully wherever #world-stage-noise isn't
   present in the markup, so this is safe to load everywhere.
--------------------------------------------------------------- */
#world-stage.is-rolling .world.is-active {
  animation: chan-roll 0.30s steps(7) both;
}
@keyframes chan-roll {
  0%   { transform: translateY(0) scaleY(1); filter: none; }
  16%  { transform: translateY(-4%) scaleY(1.04) skewX(1.2deg); filter: hue-rotate(25deg) brightness(1.5); }
  32%  { transform: translateY(5%) scaleY(0.96) skewX(-1.6deg); filter: hue-rotate(-20deg) brightness(0.7); }
  48%  { transform: translateY(-6%) scaleY(1.05); filter: hue-rotate(15deg) brightness(1.7) saturate(1.6); }
  64%  { transform: translateY(3%) scaleY(0.97) skewX(1deg); filter: hue-rotate(-10deg) brightness(0.8); }
  80%  { transform: translateY(-2%) scaleY(1.02); filter: brightness(1.3); }
  100% { transform: translateY(0) scaleY(1); filter: none; }
}

.world-stage__flash {
  position: absolute;
  inset: 0;
  z-index: 40;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
#world-stage.is-rolling .world-stage__flash { animation: chan-flash 0.30s steps(5) both; }
@keyframes chan-flash {
  0%   { opacity: 0; }
  18%  { opacity: 0.75; }
  40%  { opacity: 0.12; }
  60%  { opacity: 0.45; }
  100% { opacity: 0; }
}

.world-stage__noise {
  position: absolute;
  inset: 0;
  z-index: 41;
  width: 100%;
  height: 100%;
  opacity: 0;
  pointer-events: none;
  mix-blend-mode: screen;
  image-rendering: pixelated;
}
#world-stage.is-static .world-stage__noise { opacity: 1; }
#world-stage.is-static-out .world-stage__noise { opacity: 0; transition: opacity 0.28s ease; }

.world-stage__chan-id {
  position: absolute;
  left: 32px;
  bottom: 104px;
  z-index: 42;
  display: flex;
  flex-direction: column;
  gap: 6px;
  transform: translateX(-24px);
  opacity: 0;
  pointer-events: none;
}
#world-stage.is-idshow .world-stage__chan-id {
  /* Bumped from 2.6s to 5.5s — the old duration was clearing before
     people scrolled down far enough to even see it. Slide-in/out kept
     short and snappy in absolute terms (~0.3s each); the extra time
     all goes into the fully-visible hold. */
  animation: chan-id-slide 5.5s cubic-bezier(0.2, 0.9, 0.25, 1) both;
}
@keyframes chan-id-slide {
  0%   { transform: translateX(-24px); opacity: 0; }
  5%   { transform: translateX(0); opacity: 1; }
  89%  { transform: translateX(0); opacity: 1; }
  100% { transform: translateX(-16px); opacity: 0; }
}
.world-stage__chan-id__card {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 20px 10px 14px;
  background: rgba(8, 4, 14, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-left: 3px solid var(--color-pink);
  border-radius: 6px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.world-stage__chan-id__label {
  font-family: Georgia, serif;
  font-style: italic;
  font-weight: 400;
  font-size: 1.15rem;
  color: #fff;
}
.world-stage__chan-id__live {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-left: 6px;
  font-family: 'Exo 2', system-ui, sans-serif;
  font-size: 0.55rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: #e84545;
}
.world-stage__chan-id__dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #e84545;
  box-shadow: 0 0 6px 2px rgba(232, 69, 69, 0.6);
}
/* Not actually live right now — swapped in by world-stage.js whenever
   a visitor channel-surfs to a show that isn't the true on-air one.
   "Preview" is named explicitly (not just a muted restyle of "Live")
   so it reads as its own piece of information rather than a quieter
   version of the live badge; the air-time trails after it in a lower
   weight so "Preview" stays the thing your eye catches first. */
.world-stage__chan-id__live.is-scheduled {
  color: #ffb8e2;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.world-stage__chan-id__time {
  color: rgba(255, 255, 255, 0.55);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0.02em;
  margin-left: 2px;
}
/* "Full show details" link — optional deep-link under the ID card,
   only visible when the active world has a real programming page
   (world-stage.js hides it otherwise via the is-hidden class). */
.world-stage__chan-id__details {
  align-self: flex-start;
  pointer-events: auto;
  font-family: 'Exo 2', system-ui, sans-serif;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  text-decoration: none;
  padding: 4px 12px 4px 17px;
  transition: color 0.15s;
}
.world-stage__chan-id__details:hover { color: var(--color-pink); }
.world-stage__chan-id__details::before { content: '\21B3  '; opacity: 0.5; }
.world-stage__chan-id__details.is-hidden { display: none; }

/* Prev/Next controls + the Enter the Experience link live together in
   one corner cluster, stacked and centered on each other — NOT page-
   centered. Bottom-right on index.html only — these buttons don't
   exist on world.html at all (removed entirely; the remote already
   covers show switching there). flex-direction:column +
   align-items:center centers the two rows relative to each other
   regardless of their different widths. */
.world-stage__surf-cluster {
  position: absolute;
  right: 28px;
  bottom: 40px;
  z-index: 42;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
}
.world-stage__surf-controls {
  display: flex;
  align-items: center;
  gap: 10px;
}
.world-stage__surf-btn {
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: rgba(10, 6, 16, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.18);
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background 0.15s, transform 0.1s;
}
.world-stage__surf-btn:hover { background: rgba(255, 255, 255, 0.16); }
.world-stage__surf-btn:active { transform: scale(0.92); }
.world-stage__surf-label {
  font-family: 'Exo 2', system-ui, sans-serif;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.4);
  margin: 0 4px;
}
/* Contextual "Enter the Experience" link — sits in the World Stage
   panel itself (index.html only; world.html IS the experience, so it
   doesn't need a link to itself) so a visitor who's deep in
   channel-surfing doesn't have to scroll back up to the nav to find
   the full immersive version of whatever they're currently browsing. */
.world-stage__enter-cta {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 11px 20px;
  background: rgba(10, 6, 16, 0.75);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 30px;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  font-family: 'Exo 2', system-ui, sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #fff;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s, transform 0.15s;
}
.world-stage__enter-cta:hover {
  background: rgba(255, 255, 255, 0.16);
  transform: translateY(-2px);
}
.world-stage__enter-cta__arrow {
  color: var(--color-pink);
  transition: transform 0.15s;
}
.world-stage__enter-cta:hover .world-stage__enter-cta__arrow { transform: translateX(3px); }

@media (max-width: 640px) {
  .world-stage__chan-id { left: 16px; bottom: 96px; }
  .world-stage__chan-id__card { padding: 8px 16px 8px 12px; }
  .world-stage__chan-id__label { font-size: 1rem; }
  .world-stage__surf-cluster { right: 16px; bottom: 32px; gap: 10px; }
  .world-stage__surf-btn { width: 40px; height: 40px; font-size: 1rem; }
  .world-stage__enter-cta { padding: 9px 16px; font-size: 0.62rem; }
}

/* ---------------------------------------------------------
   Channel-surf title panels — per-show title treatments that
   flash in during a channel change, same beat as the chan-id
   bumper.
     - Soul Theory, Lo-Fi Mornings, Island Soul, Velvet Hours: NOTHING
       here — title is already baked into the world artwork.
     - Day Shift: NOTHING here — its real title (js/day-shift.js) is
       only 1.8s, already about as light as these panels, so it just
       plays everywhere on its own, channel-surf included.
     - Faith & Frequency, Hip Hop Headroom: these DO have real titles
       of their own, but they're full cinematic sequences (30s+ / a
       multi-layer canvas) reserved for world.html (Enter the
       Experience) — js/faith-freq.js and js/hip-hop-headroom.js both
       check body.world-page and skip their reveal otherwise. So on
       index.html specifically, these two get a quick (~1.6s) title
       panel here instead, built from the exact same real fonts/colors/
       reveal choreography, just sped way up — a preview, not the
       payoff.
     - Sunday Grooves: has no title of its own anywhere (real or
       baked-in), so it gets a title-panel built from its Lineup tile
       artwork (font + colors sampled from the tile) — the only show
       whose panel plays on BOTH pages, since there's no full version
       to differentiate from.
   #world-stage.is-lite (set by world-stage.js whenever body lacks
   .world-page) gates the Faith & Frequency / Hip Hop Headroom panels
   so they never double up with the real thing on world.html.
   --------------------------------------------------------- */
.title-panel {
  position: absolute;
  left: 0; right: 0; top: 14%;
  z-index: 8;
  text-align: center;
  pointer-events: none;
  display: none;
}
#world-stage.is-titlecard .title-panel.is-current { display: block; }

/* Sunday Grooves — "SUNDAY" heavy distressed-poster caps (Anton) +
   "grooves" in Pacifico script sitting almost flush underneath,
   with the tile's hand-drawn swash underline (SVG so it scales
   with the text). */
#tp-sunday-grooves .sg-line1 {
  font-family: 'Anton', 'Arial Narrow', sans-serif;
  font-size: clamp(2.6rem, 7.5vw, 5.2rem);
  letter-spacing: 0.02em;
  line-height: 0.85;
  color: #f4efe4;
  -webkit-text-stroke: 1.5px rgba(0, 0, 0, 0.6);
  text-shadow:
    0 2px 4px rgba(0, 0, 0, 0.9),
    0 6px 22px rgba(0, 0, 0, 0.9),
    0 0 46px rgba(0, 0, 0, 0.7);
  opacity: 0;
  transform: scale(0.85);
}
#tp-sunday-grooves .sg-line2-wrap {
  position: relative;
  display: inline-block;
  margin-top: -0.85em;
  opacity: 0;
  transform: translateY(10px);
}
#tp-sunday-grooves .sg-line2 {
  display: block;
  font-family: 'Pacifico', cursive;
  font-size: clamp(1.7rem, 5vw, 3.2rem);
  color: #e8b653;
  line-height: 1;
  -webkit-text-stroke: 0.6px rgba(0, 0, 0, 0.55);
  text-shadow:
    0 2px 3px rgba(0, 0, 0, 1),
    0 4px 18px rgba(0, 0, 0, 0.95),
    0 0 38px rgba(0, 0, 0, 0.75);
}
#tp-sunday-grooves .sg-swash {
  position: absolute;
  left: 50%;
  bottom: -0.62em;
  width: 90%;
  height: 0.62em;
  transform: translateX(-50%);
  overflow: visible;
  filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.9));
  opacity: 0;
}
#tp-sunday-grooves .sg-swash path { fill: #e8b653; }
#world-stage.is-titlecard #tp-sunday-grooves .sg-line1     { animation: sg-pop 0.55s cubic-bezier(0.22, 1.4, 0.36, 1) 0.1s both; }
#world-stage.is-titlecard #tp-sunday-grooves .sg-line2-wrap { animation: sg-rise 0.5s ease-out 0.4s both; }
#world-stage.is-titlecard #tp-sunday-grooves .sg-swash      { animation: tp-fade 0.4s ease-out 0.56s both; }
@keyframes sg-pop  { to { opacity: 1; transform: scale(1); } }
@keyframes sg-rise { to { opacity: 1; transform: translateY(0); } }
@keyframes tp-fade { from { opacity: 0; } to { opacity: 1; } }

/* Faith & Frequency (index.html-only light panel) — same Cinzel /
   Great Vibes fonts and colors as the real ff-typography sequence,
   compressed from ~31s to ~1.6s. */
#world-stage.is-lite #tp-faith-and-frequency .ff-faith {
  font-family: 'Cinzel', Georgia, serif;
  font-size: clamp(2rem, 4.2vw, 3.8rem);
  font-weight: 400;
  letter-spacing: 0.38em;
  color: rgba(242, 232, 215, 0.96);
  text-shadow: 0 2px 28px rgba(0, 0, 0, 0.6), 0 1px 8px rgba(0, 0, 0, 0.45);
  line-height: 1;
  opacity: 0;
}
#world-stage.is-lite #tp-faith-and-frequency .ff-amp {
  font-family: 'Great Vibes', cursive;
  font-size: clamp(1.4rem, 2.8vw, 2.4rem);
  color: rgba(242, 232, 215, 0.88);
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.55), 0 1px 7px rgba(0, 0, 0, 0.4);
  margin-top: -0.3em;
  line-height: 0.8;
  opacity: 0;
}
#world-stage.is-lite #tp-faith-and-frequency .ff-freq-wrap { margin-top: -0.22em; position: relative; }
#world-stage.is-lite #tp-faith-and-frequency .ff-freq {
  display: inline-block;
  font-family: 'Great Vibes', cursive;
  font-size: clamp(2.6rem, 5.8vw, 5rem);
  color: rgba(155, 118, 178, 0.92);
  text-shadow: 0 2px 28px rgba(0, 0, 0, 0.6), 0 1px 8px rgba(0, 0, 0, 0.45);
  line-height: 0.9;
  clip-path: inset(-60px 100% -10px 0);
}
#world-stage.is-lite #tp-faith-and-frequency .ff-tag {
  margin-top: 0.7em;
  font-family: 'Cinzel', Georgia, serif;
  font-size: clamp(0.62rem, 1.2vw, 0.82rem);
  letter-spacing: 0.2em;
  color: rgba(220, 210, 194, 0.86);
  text-shadow: 0 1px 16px rgba(0, 0, 0, 0.65), 0 1px 5px rgba(0, 0, 0, 0.5);
  opacity: 0;
}
#world-stage.is-lite.is-titlecard #tp-faith-and-frequency .ff-faith { animation: tp-fade 0.5s ease-in 0.1s both; }
#world-stage.is-lite.is-titlecard #tp-faith-and-frequency .ff-amp   { animation: tp-fade 0.4s ease-in 0.35s both; }
#world-stage.is-lite.is-titlecard #tp-faith-and-frequency .ff-freq  { animation: tp-pen  0.55s cubic-bezier(0.15, 0, 0.85, 1) 0.5s both; }
#world-stage.is-lite.is-titlecard #tp-faith-and-frequency .ff-tag   { animation: tp-fade 0.5s ease-in 1.15s both; }
@keyframes tp-pen { from { clip-path: inset(-60px 100% -10px 0); } to { clip-path: inset(-60px 0% -10px 0); } }

/* Hip Hop Headroom (index.html-only light panel) — same Bebas Neue /
   Cinzel fonts and gold divider as the real canvas title, compressed
   to ~1.6s, using the real tagline copy from js/hip-hop-headroom.js. */
#world-stage.is-lite #tp-hip-hop-headroom .hhh-top {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 0.22em;
  line-height: 0.9;
}
#world-stage.is-lite #tp-hip-hop-headroom .hhh-word {
  font-family: 'Bebas Neue', Impact, 'Arial Narrow', sans-serif;
  font-weight: 400;
  font-size: clamp(2.6rem, 7.5vw, 5.2rem);
  color: #f0ebe0;
  letter-spacing: 0.04em;
  text-shadow: 0 0 40px rgba(200, 148, 20, 0.25), 0 6px 30px rgba(0, 0, 0, 0.9);
  opacity: 0;
  transform: translateY(18px);
}
#world-stage.is-lite #tp-hip-hop-headroom .hhh-divider {
  width: 30%;
  height: 1px;
  margin: 0.38em auto 0.42em;
  background: linear-gradient(90deg, transparent 0%, #c8902a 25%, #f0c060 50%, #c8902a 75%, transparent 100%);
  opacity: 0;
}
#world-stage.is-lite #tp-hip-hop-headroom .hhh-main {
  font-family: 'Cinzel', Georgia, serif;
  font-weight: 400;
  font-size: clamp(1rem, 3vw, 2.2rem);
  color: #c8902a;
  letter-spacing: 0.28em;
  text-shadow: 0 0 30px rgba(200, 144, 42, 0.7), 0 3px 16px rgba(0, 0, 0, 0.9);
  opacity: 0;
  transform: translateY(12px);
}
#world-stage.is-lite #tp-hip-hop-headroom .hhh-sub {
  margin-top: 0.7em;
  font-family: 'Cinzel', Georgia, serif;
  font-weight: 400;
  font-size: clamp(0.55rem, 1.2vw, 0.78rem);
  color: rgba(240, 235, 224, 0.65);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.7);
  opacity: 0;
}
#world-stage.is-lite.is-titlecard #tp-hip-hop-headroom .hhh-word:nth-child(1) { animation: hhh-rise 0.45s cubic-bezier(0.16, 1, 0.3, 1) 0.05s both; }
#world-stage.is-lite.is-titlecard #tp-hip-hop-headroom .hhh-word:nth-child(2) { animation: hhh-rise 0.45s cubic-bezier(0.16, 1, 0.3, 1) 0.16s both; }
#world-stage.is-lite.is-titlecard #tp-hip-hop-headroom .hhh-divider { animation: tp-fade 0.35s ease 0.32s both; }
#world-stage.is-lite.is-titlecard #tp-hip-hop-headroom .hhh-main    { animation: hhh-rise 0.5s cubic-bezier(0.16, 1, 0.3, 1) 0.42s both; }
#world-stage.is-lite.is-titlecard #tp-hip-hop-headroom .hhh-sub     { animation: tp-fade 0.4s ease 0.78s both; }
@keyframes hhh-rise { to { opacity: 1; transform: translateY(0); } }


/* =========================================================
   AMPLIFIED ARTISTS SECTION  (v213)
   Visually distinct from editorial "Here's Who Earned It"
   ========================================================= */

.section--amplified-artists {
  background: var(--bg-0);
  border-top: 1px solid rgba(90, 170, 216, 0.14);
  border-bottom: 1px solid rgba(90, 170, 216, 0.14);
}

.amplified-header {
  text-align: center;
  margin-bottom: var(--space-4);
}

.amplified-disclosure {
  display: inline-block;
  font-family: var(--font-display);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: #5aaad8;
  border: 1px solid rgba(90, 170, 216, 0.38);
  border-radius: 4px;
  padding: 0.18em 0.65em;
  margin-bottom: var(--space-2);
}

.amplified-sub {
  color: var(--text-muted);
  font-size: 0.88rem;
  margin: var(--space-1) 0 0;
}

.amplified-placeholder {
  margin: var(--space-3) auto;
  max-width: 480px;
  padding: var(--space-4) var(--space-3);
  border: 1px dashed rgba(90, 170, 216, 0.22);
  border-radius: var(--radius);
  text-align: center;
}

.amplified-placeholder__text {
  color: var(--text-muted);
  font-style: italic;
  font-size: 0.9rem;
  margin: 0;
}


/* === ARTISTS PROMISE LINE === */
.artists-promise-wrap {
  text-align: center;
  margin: 2.25rem 0;
}
.artists-promise {
  display: inline-block;
  position: relative;
  font-size: 1.2rem;
  font-weight: 600;
  padding-bottom: 0.55rem;
  letter-spacing: 0.01em;
}
.artists-promise::after {
  content: '';
  position: absolute;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 4px;
  background: linear-gradient(90deg, #4158d0, #6a3de8, #c850c0, #6a3de8, #4158d0);
  background-size: 300% 100%;
  animation: promise-sweep 6s ease-in-out infinite;
  border-radius: 2px;
}
@keyframes promise-sweep {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

/* === CONTACT PAGE === */
.contact-block {
  margin: var(--space-4) 0;
  padding-bottom: var(--space-3);
  border-bottom: 1px solid rgba(255,255,255,0.07);
}
.contact-block:last-of-type {
  border-bottom: none;
}
.contact-block__heading {
  font-family: var(--font-display);
  font-size: 1.1rem;
  font-weight: 600;
  margin: 0 0 0.5rem;
  color: var(--text-primary);
}
.contact-block p {
  margin: 0;
  color: var(--text-muted);
  line-height: 1.7;
}
.contact-block a {
  color: var(--accent);
  text-decoration: none;
}
.contact-block a:hover {
  text-decoration: underline;
}

/* === ABOUT PAGE === */
.about-opener {
  font-size: 1.25rem;
  font-weight: 600;
  font-family: var(--font-display);
  margin-bottom: var(--space-4);
}
.section--about p {
  line-height: 1.85;
  margin-bottom: var(--space-3);
  color: var(--text-secondary, rgba(255,255,255,0.85));
}
.about-signature {
  margin-top: var(--space-5) !important;
  font-style: italic;
  font-size: 1.05rem;
  color: var(--text-primary) !important;
  border-top: 1px solid rgba(255,255,255,0.1);
  padding-top: var(--space-3);
}


/* ============================================================
   SPOKEN WORD HOUR — WORLD
   Animation sequence:
     0–3 s   camera push-in (scale 1→1.2)
     3–5 s   hold on mic
     5–16s   camera pull-back (scale 1.2→1) + overlay reveals
     4–10s   headline phrases fade in one at a time
   The sw-overlay uses CSS mask-size animation to expand the
   transparent centre outward from the spotlight — a genuine
   radial reveal, not a flat fade.
   ============================================================ */

/* Base world */
.world--spoken-word-hour {
  position: relative;
  background: #05030a;
  aspect-ratio: 1536 / 1024;
  min-height: 340px;
  overflow: hidden;
}

/* Background image — camera push/hold/pull via scale */
.spoken-word-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
}
.spoken-word-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  display: block;
  transform-origin: 35% 68%; /* mic head — pixel-measured */
  will-change: transform;
}
.world--spoken-word-hour.is-active .spoken-word-bg__img {
  animation: sw-camera 16s cubic-bezier(0.4,0,0.2,1) forwards;
}
@keyframes sw-camera {
  0%   { transform: scale(1);    }
  22%  { transform: scale(1.2);  }  /* push in */
  38%  { transform: scale(1.2);  }  /* hold    */
  100% { transform: scale(1);    }  /* pull back */
}

/* ── Animatable spotlight radius (CSS Houdini — Chrome 85+, Safari 16.4+) ── */
@property --sw-spot {
  syntax: '<percentage>';
  inherits: false;
  initial-value: 4%;
}

/* ── Dark overlay — expanding spotlight reveals audience ── */
/* Starts as a tight circle on the mic (6% radius).
   The mask fills the ENTIRE element (no uncovered gaps),
   so everything outside the spotlight stays dark.
   Spotlight expands outward until the audience is fully lit.  */
.sw-overlay {
  position: absolute;
  inset: 0;
  background: rgba(5, 3, 10, 1.0);
  -webkit-mask-image: radial-gradient(circle at 35% 68%,
      transparent var(--sw-spot),
      rgba(0,0,0,1) calc(var(--sw-spot) + 8%));
          mask-image: radial-gradient(circle at 35% 68%,
      transparent var(--sw-spot),
      rgba(0,0,0,1) calc(var(--sw-spot) + 8%));
  pointer-events: none;
}
.world--spoken-word-hour.is-active .sw-overlay {
  animation: sw-reveal 30s ease-in forwards;
}
@keyframes sw-reveal {
  0%   { --sw-spot:   4%; }  /* tight spotlight on mic only */
  13%  { --sw-spot:   4%; }  /* hold — camera push in darkness (0-3.9s) */
  45%  { --sw-spot:   9%; }  /* barely breathes, still tight (4-13.5s) */
  65%  { --sw-spot:  28%; }  /* audience just starting to emerge (13.5-19.5s) */
  82%  { --sw-spot:  85%; }  /* most rows visible (19.5-24.6s) */
  100% { --sw-spot: 130%; }  /* fully revealed (24.6-30s) */
}

/* ── Dust / ember particles in the spotlight beam ── */
.sw-dust {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.sw-ember {
  position: absolute;
  border-radius: 50%;
  background: rgba(220, 175, 90, 0.55);
  animation: sw-drift linear infinite;
  will-change: transform, opacity;
}
/* Individual ember sizing + position (clustered in spotlight area) */
.sw-ember--1 { width:2px; height:2px; left:45%; top:60%; animation-duration:7s;  animation-delay:0s;   }
.sw-ember--2 { width:1px; height:1px; left:49%; top:55%; animation-duration:9s;  animation-delay:1.2s; }
.sw-ember--3 { width:2px; height:2px; left:51%; top:65%; animation-duration:6s;  animation-delay:0.4s; }
.sw-ember--4 { width:1px; height:1px; left:47%; top:50%; animation-duration:11s; animation-delay:2.1s; }
.sw-ember--5 { width:2px; height:2px; left:53%; top:58%; animation-duration:8s;  animation-delay:0.8s; }
.sw-ember--6 { width:1px; height:1px; left:44%; top:63%; animation-duration:10s; animation-delay:3.3s; }
.sw-ember--7 { width:2px; height:2px; left:50%; top:70%; animation-duration:7.5s;animation-delay:1.7s; }
.sw-ember--8 { width:1px; height:1px; left:52%; top:45%; animation-duration:12s; animation-delay:0.2s; }

/* ── Wall sconce flicker ── */
/* Positioned behind .sw-overlay so they appear only as the spotlight reveals the scene */
.sw-wall-lights {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.sw-sconce {
  position: absolute;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: rgba(255, 200, 80, 0.9);
  transform: translate(-50%, -50%);
  opacity: 0;
}
/* Pixel-measured positions */
.sw-sconce--left-top    { left:  4%; top: 20%; }
.sw-sconce--left-mid    { left: 11%; top: 51%; }
.sw-sconce--right-top-1 { left: 74%; top: 13%; }
.sw-sconce--right-top-2 { left: 87%; top: 14%; }
.sw-sconce--right-mid   { left: 77%; top: 34%; }
/* Flicker fires when world is active; staggered delays so they never sync */
.world--spoken-word-hour.is-active .sw-sconce--left-top    { animation: sw-sconce-flicker 3.1s ease-in-out infinite 0.0s; }
.world--spoken-word-hour.is-active .sw-sconce--left-mid    { animation: sw-sconce-flicker 2.7s ease-in-out infinite 1.3s; }
.world--spoken-word-hour.is-active .sw-sconce--right-top-1 { animation: sw-sconce-flicker 3.4s ease-in-out infinite 0.7s; }
.world--spoken-word-hour.is-active .sw-sconce--right-top-2 { animation: sw-sconce-flicker 2.9s ease-in-out infinite 2.1s; }
.world--spoken-word-hour.is-active .sw-sconce--right-mid   { animation: sw-sconce-flicker 3.7s ease-in-out infinite 1.8s; }
@keyframes sw-sconce-flicker {
  0%   { opacity: 0.80; box-shadow: 0 0 6px 3px rgba(255,180,50,0.55), 0 0 18px 8px rgba(255,140,30,0.25); }
  14%  { opacity: 0.95; box-shadow: 0 0 9px 5px rgba(255,215,80,0.80), 0 0 26px 11px rgba(255,170,45,0.40); }
  30%  { opacity: 0.68; box-shadow: 0 0 4px 2px rgba(255,155,40,0.45), 0 0 11px 5px rgba(255,115,20,0.18); }
  48%  { opacity: 0.88; box-shadow: 0 0 7px 4px rgba(255,195,65,0.68), 0 0 21px 9px rgba(255,150,35,0.32); }
  65%  { opacity: 0.74; box-shadow: 0 0 5px 2px rgba(255,165,45,0.50), 0 0 14px 6px rgba(255,125,25,0.22); }
  82%  { opacity: 0.92; box-shadow: 0 0 8px 4px rgba(255,205,72,0.75), 0 0 23px 10px rgba(255,158,40,0.36); }
  100% { opacity: 0.80; box-shadow: 0 0 6px 3px rgba(255,180,50,0.55), 0 0 18px 8px rgba(255,140,30,0.25); }
}

@keyframes sw-drift {
  0%   { transform: translateY(0)   translateX(0)    scale(1);   opacity: 0;    }
  10%  { opacity: 0.8; }
  50%  { transform: translateY(-55px) translateX(6px)  scale(0.9); opacity: 0.6; }
  90%  { opacity: 0.2; }
  100% { transform: translateY(-110px) translateX(-4px) scale(0.5); opacity: 0; }
}

/* ── World title — gold serif, all caps ── */
.sw-title {
  position: absolute;
  bottom: 2.8rem;
  left: 0;
  right: 0;
  text-align: center;
  pointer-events: none;
  opacity: 0;
}
.world--spoken-word-hour.is-active .sw-title {
  animation: sw-title-in 1.5s ease-out 19s forwards;
}
@keyframes sw-title-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0);   }
}
.sw-title__text {
  font-family: 'Fraunces', 'Cormorant Garamond', 'EB Garamond', Georgia, serif;
  font-size: clamp(0.85rem, 2vw, 1.35rem);
  font-weight: 400;
  letter-spacing: 0.38em;
  text-transform: uppercase;
  /* Brushed antique gold gradient */
  background: linear-gradient(135deg, #D4B06A 0%, #E3C98A 45%, #C9963E 70%, #E3C98A 100%);
  -webkit-background-clip: text;
          background-clip: text;
  -webkit-text-fill-color: transparent;
  /* Soft bevel approximation */
  filter: drop-shadow(0 1px 2px rgba(0,0,0,0.6));
  display: inline-block;
}

/* ── Headline — phrase-by-phrase reveal ── */
.sw-headline {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  pointer-events: none;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  width: 90%;
}
.sw-phrase {
  display: block;
  font-family: 'Fraunces', 'Cormorant Garamond', Georgia, serif;
  font-size: clamp(1rem, 2.6vw, 1.7rem);
  font-weight: 300;
  font-style: italic;
  color: rgba(255, 240, 210, 0.92);
  letter-spacing: 0.04em;
  opacity: 0;
  text-shadow: 0 1px 8px rgba(0,0,0,0.8);
}
.sw-phrase--final {
  color: rgba(255, 248, 230, 1);
  font-weight: 400;
  font-style: normal;
  letter-spacing: 0.06em;
}
/* Staggered fade-in — words appear in darkness, audience revealed beneath */
.world--spoken-word-hour.is-active .sw-phrase--1 {
  animation: sw-phrase-in 1.8s ease-out 5s forwards;
}
.world--spoken-word-hour.is-active .sw-phrase--2 {
  animation: sw-phrase-in 1.8s ease-out 8.5s forwards;
}
.world--spoken-word-hour.is-active .sw-phrase--3 {
  animation: sw-phrase-in 1.8s ease-out 12s forwards;
}
.world--spoken-word-hour.is-active .sw-phrase--4 {
  animation: sw-phrase-linger 3s ease-out 15.5s forwards;
}
@keyframes sw-phrase-in {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: translateY(0);    }
}
@keyframes sw-phrase-linger {
  0%   { opacity: 0;    transform: translateY(22px); }
  40%  { opacity: 1;    transform: translateY(0);    }
  100% { opacity: 1;    transform: translateY(0);    }
}
/* ─── Show page links in TV guide ──────────────────────────────────── */
a.show-link {
  color: inherit;
  text-decoration: none;
  transition: color 0.2s;
}
a.show-link:hover,
a.show-link:focus {
  color: var(--gold);
  outline: none;
}

/* ─── Show page hero / body (shows/*.html) ──────────────────────────── */
/* Styles are inlined in each show page for isolation. No global overrides needed. */

/* =============================================================
   STILL MIND — placeholder world  (v347)
   Static background image. Full animation TBD post-launch.
   Activates daily 5:00–5:30 AM.
============================================================= */
.world--still-mind {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.still-mind-bg {
  position: absolute;
  inset: 0;
}
.still-mind-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}
/* Soft dark overlay so text-over-image stays readable */
.still-mind-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.28);
  z-index: 1;
  pointer-events: none;
}

/* =============================================================
   R&B ROOM — placeholder world  (v347)
   Static background image. Full animation TBD post-launch.
   Activates weeknights 9 PM – 11 PM.
============================================================= */
.world--rb-room {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.rb-room-bg {
  position: absolute;
  inset: 0;
}
.rb-room-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* This asset is a square 1024x1024 image inside a 1672:941 (~16:9)
     box, so center-center cropping cut the top off "R&B ROOM" — the
     title sits in the upper half of the square, so bias the crop to
     the top instead. Newly reachable via Prev/Next channel-surf, which
     is how this got noticed; not new to this feature. */
  object-position: center top;
  display: block;
}
/* Soft dark overlay */
.rb-room-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.28);
  z-index: 1;
  pointer-events: none;
}

/* =============================================================
   SATURDAY SOUL — coming-soon placeholder world  (v348)
   Static "Coming Soon" artwork. Full animation TBD post-launch.
   Activates all day Saturday.
============================================================= */
.world--saturday-soul {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.saturday-soul-bg {
  position: absolute;
  inset: 0;
}
.saturday-soul-bg__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center center;
  display: block;
}
/* Soft dark overlay */
.saturday-soul-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.22);
  z-index: 1;
  pointer-events: none;
}

/* =============================================================
   WEEKEND GROOVE — coming-soon placeholder world
   Static artwork + JS speckle animation. Full world TBD.
   Activates Saturday 5 PM – 7 PM.
============================================================= */
.world--weekend-groove {
  position: relative;
  width: 100%;
  aspect-ratio: 1672 / 941;
  min-height: 340px;
  overflow: hidden;
  border-top:    1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  isolation: isolate;
}
.weekend-groove-bg { position: absolute; inset: 0; }
.weekend-groove-bg__img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center center; display: block;
}
.weekend-groove-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0,0.15);
  z-index: 1; pointer-events: none;
}

/* =============================================================
   SOUL SERVICE — coming-soon placeholder world
   Activates Sundays 6 AM – 8 AM. Full world TBD post-launch.
============================================================= */
.world--soul-service {
  position: relative; width: 100%;
  aspect-ratio: 1672 / 941; min-height: 340px;
  overflow: hidden; isolation: isolate;
  border-top: 1px solid rgba(255,255,255,0.06);
  border-bottom: 1px solid rgba(255,255,255,0.06);
}
.soul-service-bg { position: absolute; inset: 0; }
.soul-service-bg__img {
  width: 100%; height: 100%;
  object-fit: cover; object-position: center center; display: block;
}
.soul-service-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0,0.12);
  z-index: 1; pointer-events: none;
}

/* ── Midday Bounce: headphone earpiece glow ─────────────────────────────
   Single ring on his left earpiece (our right, side-view cup).
   Image 1536×1024, no crop. Cup estimated at left≈11%, top≈40%.
   ─────────────────────────────────────────────────────────────────────── */
.mb-hp-glow {
  position: absolute;
  width: 1.9%;
  aspect-ratio: 10 / 13;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 3;
  border: 2px solid transparent;
  animation: mb-hp-pulse 3.5s ease-in-out infinite;
  animation-play-state: paused;
}
/* LOCKED: inner driver circle — left:9.05%, top:43.35%, spans 8.1–10.0% x, 41.5–45.2% y */
.mb-hp-glow--right { left: 9.05%;  top: 43.35%; }
.mb-hp-glow--left  { display: none; }

@keyframes mb-hp-pulse {
  0%, 100% {
    border-color: rgba(255,185,60,0);
    box-shadow: 0 0 0 0 rgba(255,185,60,0);
  }
  50% {
    border-color: rgba(255,185,60,0.35);
    box-shadow: 0 0 4px 1px rgba(255,185,60,0.12), inset 0 0 3px rgba(255,185,60,0.06);
  }
}

[data-world-id="midday-bounce"].is-active .mb-hp-glow {
  animation-play-state: running;
}

/* Satisfaction shimmer moved to canvas in midday-bounce.js v221 */


/* =========================================================
   ROTATION REVEAL PAGE
   ========================================================= */
.rr-page-header { text-align: center; padding: 60px 20px 8px; }
.rr-page-header__eyebrow {
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--color-pink); margin: 0 0 8px;
}
.rr-page-header__title { font-family: var(--font-display); font-weight: 500; font-size: 2.1rem; margin: 0 0 10px; color: var(--text-0); }
.rr-page-header__sub { color: var(--text-1); font-size: 0.95rem; max-width: 520px; margin: 0 auto; line-height: 1.5; }

.rr-live-section { max-width: 480px; margin: 40px auto 90px; padding: 0 20px; text-align: center; }
.rr-live-tag {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 0.65rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--color-pink); margin-bottom: 14px;
}
.rr-live-tag__dot { width: 6px; height: 6px; border-radius: 50%; background: var(--color-pink); animation: rr-livepulse 1.6s ease-in-out infinite; }
@keyframes rr-livepulse { 0%,100%{opacity:1;} 50%{opacity:0.35;} }

.rr-live-frame {
  position: relative; aspect-ratio: 3198/3831; width: 100%;
  border-radius: var(--radius); overflow: hidden; border: 1px solid rgba(255,255,255,0.12);
  background: var(--bg-2); box-shadow: 0 24px 70px rgba(0,0,0,0.55), 0 0 0 1px rgba(254,102,196,0.08);
}
.rr-full-img { position:absolute; inset:0; width:100%; height:100%; object-fit:cover; object-position:50% 12%; display:block; }
.rr-dark-bg { position:absolute; inset:0; background: radial-gradient(ellipse at 50% 55%, var(--bg-2) 0%, var(--bg-1) 55%, var(--bg-0) 100%); }
.rr-glow-bg { position:absolute; inset:0; pointer-events:none; }
.rr-vignette { position:absolute; inset:0; background: radial-gradient(ellipse at 50% 45%, transparent 35%, rgba(0,0,0,0.62) 100%); pointer-events:none; }
.rr-name-wrap { position:absolute; left:50%; bottom:8%; transform:translateX(-50%); text-align:center; z-index:6; }
.rr-name-wrap svg { display:block; margin:0 auto; }
.rr-role {
  font-family: var(--font-body); font-size:0.6rem; font-weight:600; letter-spacing:0.22em; text-transform:uppercase;
  color: var(--text-0); margin-top:10px; opacity:0;
  display:inline-block; background: #000000; padding:5px 13px; border-radius:20px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
.rr-blocks {
  font-family: var(--font-body); font-size:0.62rem; font-weight:500; letter-spacing:0.02em;
  color: rgba(246,244,251,0.9); margin-top:6px; opacity:0;
  display:inline-block; background: #000000; padding:5px 13px; border-radius:12px;
  max-width: 250px; white-space:normal; line-height:1.5;
  box-shadow: 0 2px 10px rgba(0,0,0,0.3);
}
.rr-block-link { color: var(--color-pink); text-decoration: none; border-bottom: 1px solid rgba(254,102,196,0.5); transition: border-color 0.2s ease, color 0.2s ease; }
.rr-block-link:hover { border-color: var(--color-pink); color: var(--text-0); }

@keyframes rr-photo-pulse { 0%,100%{ transform:scale(1); filter:brightness(1); } 50%{ transform:scale(1.018); filter:brightness(1.16); } }
.rr-is-pulsing { animation: rr-photo-pulse 0.85s ease-in-out 2; }
.rr-tiles-wrap { position:absolute; inset:0; transform-origin:50% 50%; }
.rr-tile { position:absolute; background-repeat:no-repeat; transition: transform 2.2s cubic-bezier(0.16,0.9,0.2,1), opacity 1.4s ease; will-change: transform, opacity; }

.rr-img-base { z-index:2; filter: brightness(0.08) contrast(1.5) saturate(0.25); }
.rr-img-r { z-index:3; mix-blend-mode:screen; opacity:0.6; transform:translateX(-5px); filter: brightness(0.7) contrast(1.4) sepia(1) saturate(8) hue-rotate(-52deg); }
.rr-img-c { z-index:3; mix-blend-mode:screen; opacity:0.6; transform:translateX(5px); filter: brightness(0.7) contrast(1.4) sepia(1) saturate(8) hue-rotate(148deg); }
.rr-grain-layer { position:absolute; inset:0; mix-blend-mode:overlay; pointer-events:none; z-index:4; }

.rr-archive-section { max-width: 900px; margin: 0 auto 90px; padding: 0 20px; }
.rr-archive-header { text-align: center; margin-bottom: 30px; }
.rr-archive-header h2 { font-family: var(--font-display); font-weight: 500; font-size: 1.5rem; margin: 0 0 8px; color: var(--text-0); }
.rr-archive-header p { color: var(--text-1); font-size: 0.88rem; margin: 0; }

.rr-archive-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 150px));
  justify-content: center; gap: 16px;
}
.rr-archive-card {
  position: relative; border-radius: calc(var(--radius) * 0.7); overflow: hidden; aspect-ratio: 4/5;
  border: 1px solid rgba(255,255,255,0.1); background: var(--bg-2);
}
.rr-archive-card img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rr-archive-card__shade {
  position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0.1) 45%, transparent 65%);
}
.rr-archive-card__name {
  position: absolute; left: 10px; right: 10px; bottom: 44px;
  font-family: var(--font-display); font-size: 0.82rem; font-weight: 500; color: var(--text-0);
}
.rr-archive-card__genre {
  position: absolute; left: 10px; bottom: 24px;
  font-size: 0.56rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: #fff; background: rgba(254,102,196,0.9); padding: 2px 8px; border-radius: 20px; display: inline-block;
}
.rr-archive-card__genre:empty { display: none; padding: 0; }
.rr-archive-card__blocks {
  position: absolute; left: 10px; right: 34px; bottom: 9px;
  font-size: 0.5rem; font-weight: 500; letter-spacing: 0.02em; color: var(--text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.rr-archive-card__blocks:empty { display: none; }
.rr-archive-card__blocks a { color: var(--text-muted); text-decoration: underline; text-decoration-color: rgba(255,255,255,0.25); }
.rr-archive-card__blocks a:hover { color: var(--text-0); }
.rr-archive-card__replay {
  position: absolute; right: 8px; bottom: 9px;
  display: flex; align-items: center; gap: 3px;
  font-size: 0.55rem; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-0); background: rgba(0,0,0,0.45); border: 1px solid rgba(255,255,255,0.25);
  border-radius: 20px; padding: 4px 8px 4px 7px; cursor: pointer; font-family: var(--font-body);
  transition: border-color 0.2s ease, background 0.2s ease;
}
.rr-archive-card__replay:hover { border-color: var(--color-pink); background: rgba(254,102,196,0.15); }

.rr-archive-note { text-align: center; color: var(--text-muted); font-size: 0.78rem; margin-top: 22px; }

.rr-lightbox {
  position: fixed; inset: 0; background: rgba(6,4,12,0.88); backdrop-filter: blur(6px);
  display: none; align-items: center; justify-content: center; z-index: 100; padding: 30px 20px;
}
.rr-lightbox.is-open { display: flex; }
.rr-lightbox-frame-wrap { position: relative; width: 100%; max-width: 420px; }
.rr-lightbox-close {
  position: absolute; top: -42px; right: 0; background: none; border: none; color: var(--text-1);
  font-size: 1.3rem; cursor: pointer; line-height: 1; padding: 6px;
}
.rr-lightbox-close:hover { color: var(--text-0); }
.rr-lightbox-label {
  text-align: center; color: var(--text-muted); font-size: 0.68rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 12px;
}

.rr-lb-frame {
  position: relative; aspect-ratio: 3198/3831; width: 100%;
  border-radius: var(--radius); overflow: hidden; border: 1px solid rgba(255,255,255,0.14);
  background: var(--bg-2); box-shadow: 0 30px 80px rgba(0,0,0,0.6); display: none;
}
.rr-lb-frame.is-active { display: block; }

.rr-cube-scene { position:absolute; inset:0; perspective:1500px; z-index:2; }
.rr-cube { position:relative; width:100%; height:100%; transform-style:preserve-3d; }
.rr-cube-face { position:absolute; inset:0; background-size:cover; background-position:center; background-color:#0b0a0d; transform-style:preserve-3d; backface-visibility:hidden; box-shadow: inset 0 0 60px rgba(0,0,0,0.45); overflow:hidden; }

.rr-vinyl-mask-wrap { position:absolute; inset:0; z-index:2; }
.rr-vinyl-grooves {
  position:absolute; left:50%; top:50%; width:74%; aspect-ratio:1/1; transform:translate(-50%,-50%) rotate(0deg);
  border-radius:50%; pointer-events:none;
  background: repeating-radial-gradient(circle, rgba(0,0,0,0) 0px, rgba(0,0,0,0) 3px, rgba(0,0,0,0.22) 4px, rgba(0,0,0,0) 5px);
  mix-blend-mode:multiply;
}
.rr-vinyl-spindle {
  position:absolute; left:50%; top:50%; width:3%; aspect-ratio:1/1; transform:translate(-50%,-50%);
  border-radius:50%; background:#0c0a10; box-shadow:0 0 3px rgba(0,0,0,0.6), inset 0 0 2px rgba(255,255,255,0.18); z-index:4;
}
.rr-vinyl-tonearm {
  position:absolute; width:5px; z-index:5; pointer-events:none;
  transform-origin: top center; transform: rotate(-32deg);
  transition: transform 0.55s cubic-bezier(0.34,1.56,0.64,1);
}
.rr-vinyl-tonearm__shaft {
  position:absolute; inset:0;
  background: linear-gradient(90deg, rgba(108,106,118,0.95) 0%, rgba(232,229,238,0.98) 45%, rgba(142,140,152,0.92) 100%);
  clip-path: polygon(32% 0%, 68% 0%, 60% 100%, 40% 100%);
  box-shadow: 0 0 8px rgba(0,0,0,0.35);
}
.rr-vinyl-tonearm__pivot {
  position:absolute; top:-7px; left:50%; transform:translateX(-50%);
  width:14px; height:14px; border-radius:50%;
  background: radial-gradient(circle at 35% 30%, #eeebf4, #8d8a9b 65%, #4c4a56 100%);
  box-shadow: 0 0 4px rgba(0,0,0,0.55), inset 0 0 2px rgba(255,255,255,0.35);
}
.rr-vinyl-tonearm__headshell {
  position:absolute; bottom:-8px; left:50%;
  width:10px; height:14px;
  background: linear-gradient(150deg, #4a4750 0%, #1c1b20 75%);
  border-radius: 2px 6px 6px 2px;
  transform: translateX(-50%) rotate(14deg);
  transform-origin: top center;
  box-shadow: 0 1px 4px rgba(0,0,0,0.5);
}
.rr-vinyl-tonearm__stylus {
  position:absolute; bottom:-13px; left:50%; transform:translateX(-50%) rotate(14deg) translateY(2px);
  width:5px; height:5px; border-radius:50%;
  background:#e2dfe8; box-shadow:0 0 5px rgba(0,0,0,0.45);
}
