/* Her Hour — the aging system.
   js/scroll.js interpolates the primitives below from scroll position;
   everything else in this file is derived from them, so the page ages as one thing. */

:root {
  /* set by js/scroll.js; era 0 defaults (1959 Blue Note paper) */
  --bg: #f5f1e8;
  --ink: #1a1a1a;
  --grain: 0.35;
  --rule: rgba(26, 26, 26, 0.55);
  --wdth: 79;       /* Archivo width axis: condensed news-gothic -> normal */
  --wdth-tight: 66; /* the same axis, one step narrower, for catalog metadata */
  --tracking: 0.26; /* kicker letter-spacing, in em */
  --glow: 0;        /* 0 = printed contact shadow, 1 = beat-scene glow */
  --track-accent: #4a6b8a;
}

/* Accent text is always pulled toward the ink so every sampled cover colour
   stays legible on both paper and dark-mode backgrounds. */
:root, section {
  /* 35% is measured, not chosen by eye: it is the highest accent share at which all
     twelve sampled cover colours clear 4.5:1 against their own era's background
     (worst case 4.81:1, Roy Ayers' yellow on warm paper). Rules, borders, outlines
     and hover fills still use the accent at full strength. */
  --accent-ink: color-mix(in oklab, var(--track-accent) 35%, var(--ink));
  --rule-accent: color-mix(in srgb, var(--track-accent) 55%, transparent);
}
/* Each chapter colours itself from its own sampled accent — no wait for scroll JS. */
.chapter { --track-accent: var(--chapter-accent); }

body { background: var(--bg); color: var(--ink); }

body::after { /* grain layer */
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 30;
  opacity: var(--grain);
  will-change: opacity;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E");
}

/* The player bar is the page inverted — a constant physical object whose polarity
   flips with the room's light. Contrast is guaranteed: it is ink-on-bg, reversed. */
:root {
  --player-bg: color-mix(in srgb, var(--ink) 94%, var(--bg));
  --player-ink: var(--bg);
  --player-rule: color-mix(in srgb, var(--bg) 34%, transparent);
}

/* ---------- The typographic handoff ----------
   The paper eras speak in a didone whose hairline thins are the same line as the
   sleeve rules. The voice changes once, at 1985 — when she arrives. */
:is([data-era='0'], [data-era='0.75'], [data-era='1'], [data-era='1.25'], [data-era='1.5']) h2 {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 72, 'wght' 500;
  letter-spacing: -0.018em;
  line-height: 0.98;
}

/* ---------- Hero: the front of the sleeve ---------- */

/* Rules rest drawn. The animation only supplies the from-state, so the page is
   correct with no JS, no animation support, and under reduced motion. */
#hero::before {
  content: '';
  height: var(--hairline);
  background: var(--rule);
  transform-origin: left center;
  animation: rule-draw 1.5s cubic-bezier(0.16, 1, 0.3, 1) 0.15s backwards;
}

@keyframes rule-draw { from { transform: scaleX(0); } }

/* ---------- 1985 — Rae arrives ---------- */

#interstitial-1985 {
  --track-accent: #c98f4a;
  max-width: 34rem;
}

.rae-cover {
  position: relative;
  isolation: isolate;
  display: grid;
  gap: 0;
  padding: clamp(0.75rem, 2.5vw, 1.25rem);
  border: var(--hairline) solid var(--rule);
}
.rae-cover img {
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  object-position: 50% 28%;   /* bias the square crop to face and shoulders */
  filter: grayscale(1) contrast(1.06) brightness(1.02);
}
/* Duotone: shadows lifted to warm plum, highlights pulled to amber, grain over the top. */
.rae-cover::before,
.rae-cover::after {
  content: '';
  position: absolute;
  inset: clamp(0.75rem, 2.5vw, 1.25rem) clamp(0.75rem, 2.5vw, 1.25rem) auto;
  aspect-ratio: 1;
  pointer-events: none;
}
.rae-cover::before { background: #2a1c2e; mix-blend-mode: lighten; }
.rae-cover::after {
  mix-blend-mode: multiply;
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='90' height='90'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='90' height='90' filter='url(%23g)' opacity='0.34'/%3E%3C/svg%3E"),
    linear-gradient(165deg, #f6d9a8, #e8b877 62%, #d9a06a 100%);
  background-color: #f0cf9b;
}

.rae-cover figcaption {
  display: grid;
  gap: 0.35rem;
  padding-top: clamp(1rem, 3vw, 1.5rem);
}
/* The year is the cover's headline numeral, not a label. */
.rae-cover .kicker {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 60, 'wght' 400;
  font-size: clamp(2.75rem, 11vw, 4.5rem);
  line-height: 0.9;
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--accent-ink);
}
.rae-cover h2 { font-size: clamp(1.6rem, 5.5vw, 2.4rem); margin-top: 0; }
.rae-cover .catalog {
  font-variation-settings: 'wdth' var(--wdth-tight), 'wght' 500;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  font-variant-numeric: tabular-nums;
  opacity: 0.7;
  margin-top: 0.5rem;
  padding-top: 0.75rem;
  border-top: var(--hairline) solid var(--rule);
}

/* ---------- Chapter artwork: printed contact shadow -> beat-scene glow ---------- */

.chapter figure img {
  position: relative;
  z-index: 1;
  box-shadow: 0 1.5rem 3rem rgba(0, 0, 0, calc(0.2 * (1 - var(--glow))));
}
/* The glow is a wide radial gradient, not a blurred layer — same read, no filter
   for a phone GPU to rasterise every frame. */
.chapter figure::after {
  content: '';
  position: absolute;
  inset: -18%;
  z-index: 0;
  background: radial-gradient(closest-side circle, var(--track-accent) 0%, transparent 78%);
  opacity: calc(var(--glow) * 0.6);
  pointer-events: none;
}

.chapter.now-playing figure img {
  outline: var(--hairline) solid var(--track-accent);
  outline-offset: 0.5rem;
}
.chapter.now-playing h2 { text-shadow: 0 0 40px var(--track-accent); }

/* ---------- Track 12: the 1959 hairline rules, reprised in the dark ---------- */

#track-12 h2 {
  font-family: var(--serif);
  font-variation-settings: 'opsz' 72, 'wght' 500;
  letter-spacing: -0.018em;
  line-height: 0.98;
}
#track-12 .chapter-text::before,
#track-12 .chapter-text::after {
  content: '';
  height: var(--hairline);
  background: var(--track-accent);
  opacity: 0.55;
  transform-origin: left center;
}
#track-12.reprise .chapter-text::before { animation: rule-draw 1.2s cubic-bezier(0.16, 1, 0.3, 1) 0.1s backwards; }
#track-12.reprise .chapter-text::after  { animation: rule-draw 1.2s cubic-bezier(0.16, 1, 0.3, 1) 0.35s backwards; }
#track-12 figure img {
  outline: var(--hairline) solid var(--track-accent);
  outline-offset: clamp(0.5rem, 1.5vw, 0.85rem);
}

/* ---------- Atmosphere: four static fields, opacity-crossfaded by era ---------- */

.atmo { position: fixed; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; }
.atmo i { position: absolute; inset: 0; display: block; }

/* era 0 — ruled paper */
.atmo i:nth-child(1) {
  opacity: calc(var(--atmo-0, 1) * 0.9);
  background: repeating-linear-gradient(transparent 0 26px,
    color-mix(in srgb, var(--ink) 7%, transparent) 26px 27px);
}
/* era 1 — warm sun glow */
.atmo i:nth-child(2) {
  opacity: var(--atmo-1, 0);
  background:
    radial-gradient(40vw 40vw at 18% 26%, rgba(232, 193, 48, 0.22), transparent 70%),
    radial-gradient(46vw 46vw at 84% 74%, rgba(214, 115, 90, 0.18), transparent 70%);
}
/* era 2 — editorial geometry, hairline rings */
.atmo i:nth-child(3) {
  opacity: var(--atmo-2, 0);
  background:
    radial-gradient(circle at 76% 28%, transparent 0 21vw,
      color-mix(in srgb, var(--ink) 10%, transparent) 21vw 21.06vw, transparent 21.1vw),
    radial-gradient(circle at 14% 78%, transparent 0 14vw,
      color-mix(in srgb, var(--ink) 8%, transparent) 14vw 14.06vw, transparent 14.1vw);
}
/* era 3 — drifting orbs. transform only, so the drift stays on the compositor. */
.atmo i:nth-child(4) { opacity: var(--atmo-3, 0); }
.atmo i:nth-child(4)::before, .atmo i:nth-child(4)::after {
  content: ''; position: absolute; width: 62vw; aspect-ratio: 1; border-radius: 50%;
}
.atmo i:nth-child(4)::before {
  top: -12vw; left: -10vw;
  background: radial-gradient(closest-side circle, rgba(209, 58, 122, 0.30), transparent 72%);
  animation: drift-a 34s ease-in-out infinite alternate;
}
.atmo i:nth-child(4)::after {
  bottom: -16vw; right: -12vw;
  background: radial-gradient(closest-side circle, rgba(47, 143, 196, 0.28), transparent 72%);
  animation: drift-b 44s ease-in-out infinite alternate;
}
@keyframes drift-a { to { transform: translate3d(14vw, 9vh, 0) scale(1.15); } }
@keyframes drift-b { to { transform: translate3d(-12vw, -8vh, 0) scale(1.2); } }

/* main sits above the atmosphere; the player and grain are already above both. */
main { position: relative; z-index: 1; }

/* ---------- Timeline rail: the year counter and the progress spine ---------- */

.rail {
  position: fixed; z-index: 15; pointer-events: none;
  left: max(2px, env(safe-area-inset-left, 0px));
  top: 0.9rem; bottom: calc(var(--player-h) + 0.9rem);
  display: grid; grid-template-rows: auto auto 1fr; gap: 0.5rem; justify-items: center;
  color: var(--accent-ink);
}
.rail .yr {
  font-family: var(--sans);
  font-variation-settings: 'wdth' var(--wdth-tight, 88), 'wght' 600;
  font-size: 1.0625rem; letter-spacing: 0.08em; line-height: 1;
  font-variant-numeric: tabular-nums;
  /* vertical so four digits never need more than the phone's left gutter, and the
     whole rail reads as one timeline rather than a label sitting on a line */
  writing-mode: vertical-rl;
  transform-origin: center top;
  transition: transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
.rail .yr-note {
  font-family: var(--sans);
  font-variation-settings: 'wdth' var(--wdth-tight, 88), 'wght' 500;
  font-size: 0.625rem; letter-spacing: 0.1em; line-height: 1;
  writing-mode: vertical-rl;
  opacity: 0;
  transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.16, 1, 0.3, 1);
}
/* the beat when the counter lands on 1985. The year scales without reflowing, so
   the note is pushed clear by a transform rather than by a gap change. */
.rail.beat .yr { transform: scale(1.35); }
.rail.beat .yr-note { opacity: 0.8; transform: translateY(1.1rem); }

.rail .spine {
  width: 2px; height: 100%; align-self: stretch;
  /* currentColor is --accent-ink, so the spine adapts to paper and to dark
     instead of fading out at one end like --rule did. */
  background: currentColor; opacity: 0.45;
  transform: scaleY(var(--progress, 0));
  transform-origin: top center;
}

@media (min-width: 720px) {
  .rail { left: 1.5rem; top: 1.5rem; gap: 0.75rem; }
  .rail .yr { font-size: 1.5rem; letter-spacing: 0.06em; }
  .rail .yr-note { font-size: 0.6875rem; }
  .rail .spine { width: 3px; }
}

/* ---------- Now-playing equalizer ---------- */

/* relative so the equaliser can be absolute (starting a track must not shift the
   text down a row); z-index 2 keeps copy above the vinyl even if they ever touch. */
.chapter-text { position: relative; z-index: 2; }
.eq { position: absolute; top: -1.35rem; left: 0;
  display: none; gap: 3px; align-items: flex-end; height: 0.85rem; }
.chapter.now-playing .eq { display: inline-flex; }
.eq i {
  width: 3px; height: 100%; background: var(--track-accent); transform-origin: bottom center;
  animation: eq 900ms ease-in-out infinite alternate;
}
.eq i:nth-child(2) { animation-duration: 700ms; animation-delay: -260ms; }
.eq i:nth-child(3) { animation-duration: 1100ms; animation-delay: -520ms; }
.eq i:nth-child(4) { animation-duration: 800ms; animation-delay: -140ms; }
@keyframes eq { from { transform: scaleY(0.22); } to { transform: scaleY(1); } }

/* ---------- Vinyl: the record behind the first sleeve, and again behind the last ---------- */

/* Uniform grooves are rotationally symmetric, so spinning them looks static. The
   label's off-centre mark and the specular wedge are the features the eye tracks.
   The disc is centred on the sleeve's right edge: half shows, the sleeve overlaps
   the rest, and the label lands inside the viewport even at 390px. */
/* The disc slides out of the sleeve's right edge and is centred on it vertically.
   The overhang is capped in absolute units — a percentage of the figure outgrows
   the column gap at some widths and lands on the copy. 2.5rem is always smaller
   than the narrowest gap (54px at the 900px breakpoint); --gutter keeps the disc
   inside the viewport on a phone, where it stays mostly tucked behind the cover.
   The tracking mark sits near the rim, not on the label, so it orbits through the
   exposed crescent instead of staying hidden behind the sleeve. */
#track-1 figure::before, #track-12 figure::before {
  content: ''; position: absolute; z-index: 0;
  top: calc(50% - min(19%, 4rem));
  right: calc(-1 * min(14%, 2.5rem, var(--gutter)));
  width: min(38%, 8rem); aspect-ratio: 1; border-radius: 50%;
  background:
    radial-gradient(circle at 50% 12%, var(--mark, #e8e2d4) 0 3.5%, transparent 3.8%),
    radial-gradient(circle, var(--label, #b6252a) 0 19%, transparent 19.4%),
    conic-gradient(from 0deg, transparent 0 6%, var(--sheen, rgba(255,255,255,0.20)) 11%, transparent 19%),
    repeating-radial-gradient(circle, transparent 0 3px, var(--groove, rgba(255,255,255,0.07)) 3px 4px),
    radial-gradient(circle, var(--disc, #16161a) 0 100%);
  animation: spin 14s linear infinite;
}
#track-12 figure::before {
  --disc: #17130a; --label: #c9a04a; --groove: rgba(201, 160, 74, 0.16);
  --mark: #c9a04a; --sheen: rgba(201, 160, 74, 0.28);
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------- Gift-unwrap opening ---------- */
/* Literal era-0 colours: the era vars are not set until the first scroll. */

#gift {
  position: fixed; inset: 0; z-index: 50;
  display: grid; place-content: center; gap: 2.5rem; justify-items: center;
  background: #f5f1e8; color: #1a1a1a; cursor: pointer;
}
.gift-box {
  position: relative; width: min(58vw, 15rem); aspect-ratio: 1;
  background: #ece5d6; border: 1px solid rgba(26, 26, 26, 0.5);
}
.gift-lid {
  position: absolute; left: -6%; right: -6%; top: -11%; height: 20%;
  background: #ece5d6; border: 1px solid rgba(26, 26, 26, 0.5);
}
/* one element, both ribbon bands */
.gift-ribbon { position: absolute; inset: 0; }
.gift-ribbon::before, .gift-ribbon::after { content: ''; position: absolute; background: #b6252a; }
.gift-ribbon::before { left: 50%; top: -11%; bottom: 0; width: 13%; transform: translateX(-50%); }
.gift-ribbon::after { top: 50%; left: 0; right: 0; height: 13%; transform: translateY(-50%); }
.gift-tag {
  position: absolute; right: -0.5rem; bottom: -1.1rem;
  font-family: var(--serif); font-style: italic; font-size: 1.05rem;
  background: #f5f1e8; border: 1px solid rgba(26, 26, 26, 0.45); padding: 0.3rem 0.75rem;
}
.gift-hint {
  font-family: var(--sans); font-size: 0.65rem; text-transform: uppercase;
  letter-spacing: 0.26em; opacity: 0;
  animation: gift-whisper 900ms ease 2s forwards;   /* the affordance, after 2s */
}
@keyframes gift-whisper { to { opacity: 0.55; } }

/* Opening: ribbon pulls away, lid falls, box lifts, veil clears. ~1.8s total. */
#gift.opening { animation: gift-veil 800ms ease 1s forwards; pointer-events: none; }
#gift.opening .gift-ribbon { animation: gift-ribbon-away 600ms cubic-bezier(0.4, 0, 0.2, 1) forwards; }
#gift.opening .gift-lid   { animation: gift-lid-away 750ms cubic-bezier(0.5, 0, 0.75, 0) 350ms forwards; }
#gift.opening .gift-box   { animation: gift-box-away 700ms cubic-bezier(0.16, 1, 0.3, 1) 600ms forwards; }
#gift.opening .gift-hint  { animation: gift-veil 200ms ease forwards; }

@keyframes gift-ribbon-away { to { transform: translate(55%, -140%) rotate(26deg); opacity: 0; } }
@keyframes gift-lid-away    { to { transform: translateY(150%) rotate(-12deg); opacity: 0; } }
@keyframes gift-box-away    { to { transform: scale(1.14); opacity: 0; } }
@keyframes gift-veil        { to { opacity: 0; } }

/* ---------- Reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  #hero::before { transform: scaleX(1); }
  .gift-hint { opacity: 0.55; }
  /* the whole sequence collapses to one crossfade; audio still starts */
  #gift.opening { animation-name: gift-veil !important; animation-duration: 300ms !important;
                  animation-delay: 0s !important; }
  #gift.opening .gift-box { visibility: hidden; }
}
