/* ══════════════════════════════════════════════════════════════════════
   base.css — reset, document defaults, typography utilities
   ══════════════════════════════════════════════════════════════════════ */

/* ── 1. RESET ─────────────────────────────────────────────────────── */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  scroll-behavior: smooth;
}

body {
  background: var(--bg);
  color: var(--text);
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--fs-sm);
  line-height: 1.6;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  transition: background var(--t), color var(--t);
}

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

/* Vue mounts after the module script loads. Without this the raw
   {{ mustaches }} flash on screen first. */
[v-cloak] { display: none; }

/* Available to screen readers, invisible on screen */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}


/* ── 2. FOCUS ─────────────────────────────────────────────────────
   Keyboard users need a visible focus ring on every interactive
   element. :focus-visible keeps it off mouse clicks. */
:focus-visible {
  outline: 2px solid var(--accent-2);
  outline-offset: 3px;
  border-radius: 1px;
}

/* Skip link — first tab stop, hidden until focused */
.skip-link {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 9999;
  padding: var(--sp-3) var(--sp-4);
  background: var(--bg-2);
  color: var(--text);
  border: 1px solid var(--accent);
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-120%);
  transition: transform 150ms ease;
}

.skip-link:focus-visible {
  transform: translateY(0);
}


/* ── 3. SCROLLBAR ─────────────────────────────────────────────────── */
::-webkit-scrollbar         { width: 3px; }
::-webkit-scrollbar-track   { background: var(--bg); }
::-webkit-scrollbar-thumb   {
  background: var(--rule-2);
  border-radius: 2px;
  transition: background var(--t);
}
::-webkit-scrollbar-thumb:hover { background: var(--accent-2); }


/* ── 4. SELECTION ─────────────────────────────────────────────────── */
::selection {
  background: var(--accent);
  color: var(--bg);
}


/* ── 5. NOISE TEXTURE OVERLAY ─────────────────────────────────────
   A faint SVG fractal grain fixed over the viewport. Adds material
   depth without visual loudness. pointer-events: none so it never
   intercepts clicks. */
body::after {
  content: '';
  position: fixed;
  inset: 0;
  z-index: 9000;
  pointer-events: none;
  opacity: 0.025;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");
  background-repeat: repeat;
  background-size: 128px 128px;
}


/* ── 6. TYPOGRAPHY UTILITIES ──────────────────────────────────────── */

/* Fraunces serif display */
.display {
  font-family: 'Fraunces', Georgia, serif;
  font-optical-sizing: auto;
  letter-spacing: -0.02em;
  line-height: 1.05;
}

.display-italic {
  font-family: 'Fraunces', Georgia, serif;
  font-style: italic;
  font-weight: 300;
  font-optical-sizing: auto;
  letter-spacing: -0.01em;
}

/* Small-caps style metadata text */
.label,
.label-accent {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: var(--fs-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: color var(--t);
}

.label        { color: var(--text-2); }
.label-accent { color: var(--accent); }

/* Block-level variant — used wherever a label sits above content */
.label--block,
.label-accent--block { display: block; }


/* ── 7. LAYOUT PRIMITIVES ─────────────────────────────────────────── */
.container {
  max-width: var(--wrap);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

.rule {
  width: 100%;
  height: 1px;
  background: var(--rule);
  transition: background var(--t);
}

.rule-v {
  width: 1px;
  background: var(--rule);
  transition: background var(--t);
}

.accent-line {
  display: block;
  width: 32px;
  height: 1px;
  background: var(--accent);
  transition: background var(--t);
}


/* ── 8. BPM ANIMATIONS ────────────────────────────────────────────
   Durations resolve to --bpm / --bpm-half, so every animation syncs
   to the current track's tempo. Deliberately limited to brightness
   and scale — never colour or layout. */
.bpm-breathe {
  animation: breathe var(--bpm) ease-in-out infinite;
}

@keyframes breathe {
  0%, 100% { filter: brightness(1);   }
  50%      { filter: brightness(1.2); }
}

/* Pulsing dot in the nav and footer — confirms Spotify is live */
.bpm-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent);
  flex-shrink: 0;
  animation: breathe var(--bpm) ease-in-out infinite;
  transition: background var(--t);
}

@keyframes cursorBlink {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0; }
}


/* ── 9. SCROLL REVEAL ─────────────────────────────────────────────
   Elements start faded and offset. The IntersectionObserver in
   assets/js/reveal.js adds .in-view as they enter the viewport. */
.reveal {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 700ms ease, transform 700ms ease;
}

.reveal.in-view {
  opacity: 1;
  transform: translateY(0);
}

.reveal-delay-1 { transition-delay: 100ms; }
.reveal-delay-2 { transition-delay: 200ms; }
.reveal-delay-3 { transition-delay: 300ms; }
.reveal-delay-4 { transition-delay: 400ms; }


/* ── 10. HIDDEN CANVAS (album-art colour extraction) ─────────────── */
#color-canvas {
  position: absolute;
  left: -9999px;
  width: 50px;
  height: 50px;
  pointer-events: none;
}


/* ── 11. REDUCED MOTION ───────────────────────────────────────────
   The site animates continuously — breathing panels, equaliser bars,
   signal traces down the skill tree. All of it is decorative, so it
   all comes off for anyone who has asked for less motion.
   WCAG 2.2.2 (Pause, Stop, Hide). */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  .reveal {
    opacity: 1;
    transform: none;
  }

  /* The blanket rule above freezes animations rather than removing them,
     which would park the blobs at their final keyframe rather than their
     neutral start. Pin them explicitly. */
  .hero-blob {
    animation: none !important;
    transform: none;
  }

  /* Same trap: a frozen dash-offset animation leaves a stray segment
     sitting on every connector, so the trace has to go entirely. */
  .sd-trace { display: none; }
}

/* Anyone asking for more contrast gets much less album colour lifting the
   hero background. */
@media (prefers-contrast: more) {
  .hero-bg { opacity: 0.1; }
}
