/* ══════════════════════════════════════════════════════════════════════
   tokens.css — design tokens
   ──────────────────────────────────────────────────────────────────────
   Every colour, type size and timing value in the site resolves to one
   of these custom properties. The Spotify integration rewrites the
   --accent-*, --bg-* and --text-* values at runtime via applyTheme()
   in assets/js/theme.js. Nothing else is overwritten.
   ══════════════════════════════════════════════════════════════════════ */

:root {
  /* ── Base palette — warm dark editorial ─────────────────────────── */
  --bg:     #0f0f11;   /* page background                          */
  --bg-1:   #141417;   /* slightly elevated surfaces (nav, footer) */
  --bg-2:   #1a1a1e;   /* cards, inset panels                      */
  --bg-3:   #202024;   /* deepest surface (code blocks, inputs)    */

  /* ── Text tones ─────────────────────────────────────────────────
     Contrast ratios against the bare --bg above:
       --text    ≈ 15.4:1
       --text-2  ≈  7.1:1
       --text-3  ≈  5.0:1
     --text-3 is the floor: do not darken it without re-checking.

     Note these are against the *untinted* --bg. applyTheme() lifts --bg
     toward the album colour and the hero adds a gradient layer over it, so
     the real ratio in the hero is lower — that is why --text-3 was raised
     from #7c7c81 (which measured 4.0:1 there, failing AA) to the value
     below. See TEXT_FLOOR in assets/js/theme.js. */
  --text:   #ddd8cf;
  --text-2: #9d9d9f;
  --text-3: #86868b;

  /* ── Type scale ─────────────────────────────────────────────────── */
  --fs-xs: 0.875rem;  /* 14px — metadata, tags, labels        */
  --fs-sm: 1rem;      /* 16px — body copy                     */
  --fs-md: 1.125rem;  /* 18px — prominent body / small headings */
  --fs-lg: 1.25rem;   /* 20px — standard headings             */
  --fs-xl: 2rem;      /* 32px — stat values                   */
  --fs-display-sm: clamp(1.8rem, 3vw, 2.5rem);
  --fs-display-lg: clamp(3rem, 6vw, 5.5rem);

  /* ── Spacing scale ──────────────────────────────────────────────── */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 2.5rem;
  --sp-8: 4rem;
  --sp-9: 6rem;

  /* ── Measure ────────────────────────────────────────────────────── */
  --wrap:      1100px;   /* content column width  */
  --gutter:    2rem;     /* page side padding     */
  --nav-h:     4.5rem;   /* fixed header height, used for scroll-margin */

  /* ── Borders & rules ────────────────────────────────────────────── */
  --rule:   rgba(221, 216, 207, 0.08);
  --rule-2: rgba(221, 216, 207, 0.14);

  /* ── Accent colours ─────────────────────────────────────────────
     Overwritten by album-art extraction. The defaults are neutral
     greys so the site reads correctly before Spotify responds. */
  --accent:   #8a8a8a;   /* dominant album colour  */
  --accent-2: #a3a3a3;   /* contrast album colour  */
  --accent-3: #bcbcbc;   /* mid-tone album colour  */

  /* ── Hero gradient cross-fade ───────────────────────────────────
     Written by theme.js from the track tempo. See --grad-* below. */
  --t-grad: 1400ms;

  /* ── BPM animation timing ───────────────────────────────────────
     Updated from Spotify audio features. Default ≈ 100 BPM. */
  --bpm:      600ms;
  --bpm-half: 300ms;

  /* ── Hero drift cycle ───────────────────────────────────────────
     One musical phrase, written by theme.js as --bpm × HERO_MOTION
     .phraseBeats and clamped. The default is 32 beats at the 600ms
     default above, so the layer looks right before Spotify answers. */
  --drift: 19200ms;

  /* ── Global theme transition ────────────────────────────────────
     Applied to colour-bearing properties so palette swaps ease in
     rather than snapping. */
  --t: 700ms ease;
}


/* ══════════════════════════════════════════════════════════════════════
   HERO GRADIENT STOPS
   ──────────────────────────────────────────────────────────────────────
   Registered so they interpolate. A plain custom property inside a
   gradient snaps to its new value, which makes the hero jump on every
   track change; a registered <color> cross-fades properly.
   @property has been Baseline since July 2024, and degrades gracefully:
   unregistered, the gradient still renders, it just snaps.

   theme.js writes literal rgba() strings here rather than the colours
   being composed in CSS from var(--accent). Interpolating a registered
   property whose value arrives through var() indirection is the least
   reliable corner of @property across engines, and theme.js already has
   the RGB triples in hand.
   ══════════════════════════════════════════════════════════════════════ */

@property --grad-1 {
  syntax: '<color>';
  inherits: true;
  initial-value: rgba(138, 138, 138, 0.34);
}

@property --grad-2 {
  syntax: '<color>';
  inherits: true;
  initial-value: rgba(163, 163, 163, 0.28);
}

@property --grad-3 {
  syntax: '<color>';
  inherits: true;
  initial-value: rgba(188, 188, 188, 0.24);
}

/* Energy-driven amounts, written by setEnergy() in theme.js. Registered as
   <number> for two reasons: the drift keyframes multiply a percentage by
   --drift-amp, which needs a real number rather than a substituted token,
   and registration lets both values transition so a change of track eases
   between energies instead of stepping. Where an engine does not recompute
   a keyframe on a custom-property change, the failure mode is that step —
   the animation still runs. Initial values are the neutral 0.5 energy. */

@property --drift-amp {
  syntax: '<number>';
  inherits: true;
  initial-value: 0.85;
}

@property --pulse-depth {
  syntax: '<number>';
  inherits: true;
  initial-value: 0.15;
}

@property --pulse-scale {
  syntax: '<number>';
  inherits: true;
  initial-value: 0.013;
}

/* The transition belongs on :root, the element whose values actually
   change. Declaring it on the gradient layer would do nothing — the
   value only inherits there. */
:root {
  transition: --grad-1 var(--t-grad) ease,
              --grad-2 var(--t-grad) ease,
              --grad-3 var(--t-grad) ease,
              --drift-amp var(--t-grad) ease,
              --pulse-depth var(--t-grad) ease,
              --pulse-scale var(--t-grad) ease;
}
