/* ============================================================
   THE JAM BOX — design system for the landing and chart pages
   COLORS(R) 12-color office-equipment palette, ported from the
   brand kit. The wizard, player and admin pages are still on
   app.css; these two share nothing with it.
   ============================================================ */

@font-face {
  font-family: "bookmania";
  src: url("fonts/bookmania-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "bookmania";
  src: url("fonts/bookmania-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fragment Mono";
  src: url("fonts/fragment-mono.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fedra Mono";
  src: url("fonts/fedra-mono-400.otf") format("opentype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Fedra Mono";
  src: url("fonts/fedra-mono-700.otf") format("opentype");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Source Sans Variable";
  src: url("fonts/source-sans.otf") format("opentype");
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* COLORS(R) */
  --crt-cobalt: #1455d9;
  --electric-azure: #0184e0;
  --terminal-cyan: #01bcdb;
  --spreadsheet-mint: #5bda9a;
  --highlighter-lime: #bddb1e;
  --postit-yellow: #ffcc33;
  --tangerine-fax: #f55c1f;
  --powerpoint-red: #cc3333;
  --hotline-magenta: #da2897;
  --laser-violet: #653fc0;
  --warm-office-grey: #c1b9aa;
  --graphite-plastic: #23262f;
  --paper-cream: #f2efe6;

  --surface-screen: var(--paper-cream);
  --surface-card-screen: #e8e4d8;
  --ink-on-screen: var(--graphite-plastic);
  --ink-on-screen-muted: #6d6a61;

  --radius: 8px;
  --radius-cassette: 22px;
  --background: var(--paper-cream);
  --foreground: var(--graphite-plastic);
  --card: var(--surface-card-screen);
  --primary: var(--highlighter-lime);
  --primary-foreground: var(--graphite-plastic);
  --title-accent: var(--tangerine-fax);
  /* AA-safe burnt tangerine for small mono labels: 5.16:1 on paper cream */
  --title-accent-strong: #b23c08;
  --muted-foreground: var(--ink-on-screen-muted);
  --border: var(--warm-office-grey);

  --font-display: "bookmania", serif;
  --font-body: "Source Sans Variable", -apple-system, "Segoe UI", sans-serif;
  --font-mono: "Fedra Mono", ui-monospace, "SFMono-Regular", monospace;
  --font-terminal: "Fragment Mono", ui-monospace, monospace;

  --tracking-tight: -0.01em;
  --tracking-wide: 0.08em;
  --tracking-widest: 0.16em;

  --ease-out-strong: cubic-bezier(0.23, 1, 0.32, 1);
}

*,
*::before,
*::after { box-sizing: border-box; border-color: var(--border); }

html { line-height: 1.5; -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  min-height: 100dvh;
  background-color: var(--background);
  color: var(--foreground);
  font-family: var(--font-body);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
  scrollbar-color: var(--graphite-plastic) var(--paper-cream);
  scrollbar-width: thin;
}

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

/* The UA's [hidden] rule loses to any class that sets display — .countdown is
   a flex container, so without this an empty chart still renders its header. */
[hidden] { display: none !important; }

h1, h2, h3, p { margin: 0; }

::selection {
  background-color: var(--highlighter-lime);
  color: var(--graphite-plastic);
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--paper-cream); }
::-webkit-scrollbar-thumb {
  background: var(--graphite-plastic);
  border: 2px solid var(--paper-cream);
  border-radius: 999px;
}

.page { position: relative; min-height: 100dvh; }

.wrap { max-width: 1200px; margin-inline: auto; padding-inline: 32px; }

.label-mono {
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
}

/* CRT scanlines + film grain overlay */
.crt-overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  opacity: 0.35;
  background-image:
    repeating-linear-gradient(
      to bottom,
      rgb(0 0 0 / 0.16) 0px,
      rgb(0 0 0 / 0.16) 1px,
      transparent 1px,
      transparent 3px
    ),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.32'/%3E%3C/svg%3E");
  background-size: 100% 3px, 160px 160px;
  mix-blend-mode: multiply;
}

/* Brand logo lockup rendered as graphite ink on paper */
.logo-ink { filter: brightness(0) saturate(100%) opacity(0.9); }

/* ── Header ────────────────────────────────────────────────────── */
.topbar {
  position: relative;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-block: 28px;
}
.topbar-brand {
  display: flex;
  align-items: center;
  gap: 14px;
  text-decoration: none;
  color: inherit;
}
.topbar-nav { display: flex; flex-wrap: wrap; align-items: center; gap: 24px; }
/* The chart page's nav hovers warm, the landing page's single link hovers lime
   — that difference is in the source design, not an oversight. */
.topbar-nav .topbar-link:hover { color: var(--title-accent-strong); }
.topbar-logo { height: 40px; width: auto; }
.topbar-tag {
  border-left: 1.5px solid var(--muted-foreground);
  padding-left: 14px;
  color: var(--muted-foreground);
}
.topbar-link {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  letter-spacing: var(--tracking-wide);
  color: var(--foreground);
  text-decoration: none;
  transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.topbar-link:hover { color: var(--primary); }

/* The landing page's single header link is a destination, not a footnote. As
   plain mono text it read as fine print next to the hero CTA, so it gets
   button weight — graphite rather than lime, so the hero keeps the accent. */
.topbar-link--chip {
  display: inline-flex;
  align-items: center;
  height: 40px;
  padding-inline: 20px;
  border-radius: var(--radius);
  background-color: var(--foreground);
  color: var(--background);
  font-weight: 700;
  text-transform: uppercase;
  transition-property: background-color, color, box-shadow;
}
.topbar-link--chip:hover {
  background-color: var(--highlighter-lime);
  color: var(--graphite-plastic);
}
.topbar-link--chip:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--highlighter-lime);
}

/* ── Buttons ───────────────────────────────────────────────────── */
.cta {
  display: inline-flex;
  height: 52px;
  align-items: center;
  justify-content: center;
  padding-inline: 28px;
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  text-decoration: none;
  cursor: pointer;
  transition-duration: 150ms;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
}
.cta:active { transform: scale(0.98); }
.cta:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--highlighter-lime);
}

.cta--primary {
  border: none;
  background-color: var(--primary);
  color: var(--primary-foreground);
  transition-property: transform, background-color, box-shadow;
}
.cta--primary:hover { background-color: var(--foreground); color: var(--background); }

.cta--secondary {
  border: 1.5px solid var(--foreground);
  background-color: transparent;
  color: var(--foreground);
  transition-property: transform, background-color, border-color, color;
}
.cta--secondary:hover {
  border-color: var(--highlighter-lime);
  background-color: var(--highlighter-lime);
  color: var(--graphite-plastic);
}

/* Molded keys. The chart page uses these rather than the flat pair above —
   that's the source design's own split, and it's load-bearing: a
   transparent outline button is invisible on the dark CRT card. */
.cta--molded,
.cta--key {
  border: none;
  border-radius: 14px;
  transition-property: transform, box-shadow, color, background-image;
}
.cta--molded:active,
.cta--key:active { transform: translateY(1px); }

.cta--molded {
  background-image: linear-gradient(
    180deg,
    color-mix(in srgb, var(--highlighter-lime) 82%, #fff) 0%,
    var(--highlighter-lime) 100%
  );
  color: var(--graphite-plastic);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.6),
    inset 0 -2px 5px -2px rgb(35 38 47 / 0.3),
    0 2px 5px rgb(35 38 47 / 0.18),
    0 10px 20px -14px rgb(35 38 47 / 0.5);
}
.cta--molded:hover {
  background-image: linear-gradient(
    180deg,
    color-mix(in srgb, var(--tangerine-fax) 26%, var(--highlighter-lime)) 0%,
    color-mix(in srgb, var(--tangerine-fax) 44%, var(--highlighter-lime)) 100%
  );
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.55),
    0 3px 8px rgb(245 92 31 / 0.32),
    0 12px 24px -14px rgb(245 92 31 / 0.5);
}
.cta--molded:active {
  box-shadow: inset 0 2px 6px rgb(35 38 47 / 0.3), 0 1px 2px rgb(35 38 47 / 0.12);
}
.cta--molded:focus-visible {
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.6),
    0 0 0 3px rgb(245 92 31 / 0.5),
    0 2px 6px rgb(35 38 47 / 0.18);
}

.cta--key {
  background-image: linear-gradient(
    180deg,
    color-mix(in srgb, var(--paper-cream) 60%, #fff) 0%,
    color-mix(in srgb, var(--paper-cream) 92%, var(--graphite-plastic)) 100%
  );
  color: var(--foreground);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.7),
    inset 0 -2px 4px -2px rgb(35 38 47 / 0.28),
    0 2px 5px rgb(35 38 47 / 0.18),
    0 6px 14px -10px rgb(35 38 47 / 0.5);
}
.cta--key:hover {
  color: var(--title-accent-strong);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.75),
    inset 0 -2px 4px -2px rgb(35 38 47 / 0.24),
    0 2px 6px rgb(245 92 31 / 0.28),
    0 8px 18px -10px rgb(245 92 31 / 0.45);
}
.cta--key:active {
  box-shadow:
    inset 0 2px 5px rgb(35 38 47 / 0.32),
    inset 0 -1px 0 rgb(255 255 255 / 0.4),
    0 1px 2px rgb(35 38 47 / 0.12);
}
.cta--key:focus-visible {
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.7),
    0 0 0 3px rgb(245 92 31 / 0.45),
    0 2px 6px rgb(35 38 47 / 0.18);
}

/* ── Hero ──────────────────────────────────────────────────────── */
/* The hero fills the screen and the photo fills the hero, and the crop that
   costs is a **budget**, not a side effect. A 1600x905 photo is 1.768; a laptop
   screen is 1.6 (every 16:10 MacBook) to 1.9, so at full width the photo is
   sometimes shorter than the screen and sometimes taller, and only one of those
   can be fixed for free.

   --hero-zoom is how far past its own height the photo may be scaled to reach
   the bottom of the screen: 1.18 spends at most 15% of the width, against the
   60%+ that plain `cover` was taking at narrow widths in the morning. Above that
   the photo simply stops growing and the hero ends under it — better a short
   hero on a 5:4 monitor than a photo with its subject cropped out.

   The 10px is the scrollbar 100vw includes and the element doesn't get. */
.hero {
  position: relative;
  --hero-ratio: 1.768;
  /* Phones spend nothing: the band is already the full width of the screen, the
     copy below it needs every pixel to keep both buttons above the fold, and
     39px of extra band cost 15% of the frame's width. The budget is a desktop
     idea — see the md block. */
  --hero-zoom: 1;
  --hero-natural-h: calc((100vw - 10px) / var(--hero-ratio));
  --hero-photo-h: min(
    calc(100dvh - var(--hero-fold-reserve)),
    calc(var(--hero-natural-h) * var(--hero-zoom))
  );
  /* Phone layout: the header is in flow above the photo, so the frame's height
     is measured from under it. From 768px up the header floats over the photo
     and this drops to 0 — see the md block, where those 96px turn into 170px of
     frame width, which is what made the hero fill a laptop screen. */
  --hero-fold-reserve: 120px;
}
.hero-media { position: relative; overflow: hidden; }

.hero-image {
  position: relative;
  display: block;
  width: 100%;
  height: var(--hero-photo-h);
  object-fit: cover;
  /* The budget is spent off the ceiling, not off her: 32% keeps the subject and
     the console — the half the copy stands over — inside every crop this can
     produce. */
  object-position: 50% 32%;
}
.hero-fade-r,
.hero-fade-b { position: absolute; pointer-events: none; }
/* The source design's stops (1 / 0.7 / 0.05) were tuned against a dark photo,
   where 70% cream still left contrast. Over a bright one they bleach the
   subject, so the ramp is gentler here. Legibility doesn't rely on it — the
   headline sits inside .hero-fade-b, which is still full cream. */
.hero-fade-r {
  inset: 0;
  background-image: linear-gradient(
    to right,
    rgb(242 239 230 / 0.92) 0%,
    rgb(242 239 230 / 0.4) 50%,
    rgb(242 239 230 / 0) 100%
  );
}
/* A phone gets the photo at its own ratio, so the frame is a ~215px band there
   and a 208px wash would have swallowed all of it. Sized to the overlap now. */
.hero-fade-b {
  left: 0;
  right: 0;
  bottom: 0;
  height: 150px;
  background-image: linear-gradient(to top, rgb(242 239 230 / 1), rgb(242 239 230 / 0));
}

/* Rides just above the eyebrow, at the copy's right edge. It used to be pinned
   to a fraction of the photo's height, which only worked while the photo was
   taller than the copy — with the whole frame above the fold it often isn't,
   and 30%/38% printed the stamp straight through the headline. Anchored to the
   copy instead, it clears the text by construction, on a phone band as much as
   on a 1920 desktop. Sized off the photo so it can't outgrow the frame. */
.hero-seal {
  position: absolute;
  right: 24px;
  bottom: calc(100% + 8px);
  /* Capped against the band as well as in absolute terms: at 320px the band is
     176px tall, the copy climbs 100px of it, and a flat 76px stamp cleared the
     top of the photo by 9px. */
  width: min(76px, calc(var(--hero-photo-h) * 0.36));
}
.hero-seal img { width: 100%; }
.seal-ring {
  -webkit-mask-image: radial-gradient(circle at center, transparent 26%, #000 27%);
  mask-image: radial-gradient(circle at center, transparent 26%, #000 27%);
  transform: rotate(-8deg);
}
.seal-core {
  position: absolute;
  inset: 0;
  -webkit-mask-image: radial-gradient(circle at center, #000 26%, transparent 27%);
  mask-image: radial-gradient(circle at center, #000 26%, transparent 27%);
}

/* Below 768px the copy stays in flow after the band and climbs 100px back over
   it — enough to put both CTAs above the fold on a 390x844 phone, which the old
   62vh crop missed by 255px, and on a 360x800 one with 12px to spare. */
.hero-copy { position: relative; z-index: 10; margin-top: -100px; }
.hero-copy-inner {
  display: flex;
  max-width: 780px;
  flex-direction: column;
  gap: 24px;
}
.hero-eyebrow {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--title-accent-strong);
}
.rec-dot {
  display: block;
  height: 8px;
  width: 8px;
  flex-shrink: 0;
  border-radius: 999px;
  background-color: var(--title-accent-strong);
}
.hero-title {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 7vw, 76px);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  text-wrap: pretty;
}
.hero-lede {
  max-width: 580px;
  font-size: 1.25rem;
  line-height: 1.5;
  color: var(--muted-foreground);
  text-wrap: pretty;
}
.hero-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; }

/* ── VU-meter divider ──────────────────────────────────────────── */
.vu-block { padding-top: 80px; }
.vu-divider {
  position: relative;
  height: 10px;
  border-radius: 999px;
  background-image: repeating-linear-gradient(
    to right,
    var(--ink-on-screen-muted) 0px,
    var(--ink-on-screen-muted) 2px,
    transparent 2px,
    transparent 12px
  );
  -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 62%, rgb(0 0 0 / 0.25));
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 62%, rgb(0 0 0 / 0.25));
}
.vu-scale {
  margin-top: 12px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.vu-scale .is-zero { color: var(--title-accent-strong); }

/* ── Steps ─────────────────────────────────────────────────────── */
.steps {
  display: grid;
  grid-template-columns: 1fr;
  gap: 40px;
  padding-top: 64px;
  padding-bottom: 96px;
}
.step { display: flex; flex-direction: column; gap: 18px; }
.step-mask {
  height: 180px;
  width: 180px;
  object-fit: cover;
  -webkit-mask-image: url("img/step-mask.svg");
  mask-image: url("img/step-mask.svg");
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
}
.step-num {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  color: var(--title-accent-strong);
}
.step h3 {
  font-family: var(--font-display);
  font-size: 1.953rem;
  line-height: 1.25;
  font-weight: 700;
}
.step p { line-height: 1.5; color: var(--muted-foreground); text-wrap: pretty; }

/* ── Record card ───────────────────────────────────────────────── */
.record { scroll-margin-top: 32px; padding-bottom: 96px; }
.record-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-cassette);
  background-color: var(--card);
  padding: 56px 32px;
}
.record-card h2 {
  margin-top: 20px;
  margin-bottom: 16px;
  max-width: 16ch;
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  line-height: 1.08;
  font-weight: 700;
}
.record-card p { max-width: 520px; line-height: 1.5; color: var(--muted-foreground); }
.record-actions {
  margin-top: 32px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

/* ── Footer ────────────────────────────────────────────────────── */
.site-footer { position: relative; border-top: 1.5px solid var(--border); }
.footer-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  padding-block: 28px;
}
.palette-bar {
  display: flex;
  height: 20px;
  width: 25%;
  max-width: 25%;
  align-items: stretch;
  gap: 3px;
}
.palette-bar span { display: block; min-width: 0; flex: 1 1 0%; border-radius: 2px; }
.footer-logo { height: 30px; width: auto; }
.footer-note {
  padding-bottom: 28px;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--muted-foreground);
}

/* ── Hardware surfaces (ported verbatim from the brand kit) ────── */

/* Warm amber mono-phosphor tube */
.crt-amber {
  --phosphor: #ff7a29;
  --phosphor-dim: #e08a53;
  background-color: #14161c;
  background-image: radial-gradient(
    120% 100% at 50% 40%,
    rgb(255 122 41 / 0.07) 0%,
    transparent 70%
  );
  box-shadow:
    inset 0 3px 18px rgb(0 0 0 / 0.7),
    inset 0 0 0 1.5px rgb(255 122 41 / 0.14),
    0 0 0 3px rgb(35 38 47 / 0.35);
}

/* A row rendered as a thin strip of the player's CRT screen */
.crt-row { isolation: isolate; }
.crt-row::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  background-image: repeating-linear-gradient(
    to bottom,
    rgb(0 0 0 / 0.26) 0px,
    rgb(0 0 0 / 0.26) 1px,
    transparent 1px,
    transparent 3px
  );
  opacity: 0.45;
}
.crt-row::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  background: radial-gradient(120% 140% at 50% 50%, transparent 55%, rgb(0 0 0 / 0.5) 100%);
}
.crt-row > * { position: relative; z-index: 2; }

.crt-key {
  background-image: linear-gradient(180deg, rgb(255 122 41 / 0.14) 0%, rgb(255 122 41 / 0.05) 100%);
  box-shadow:
    inset 0 1px 0 rgb(255 122 41 / 0.28),
    inset 0 -2px 4px -2px rgb(0 0 0 / 0.6),
    0 0 0 1px rgb(255 122 41 / 0.3);
  transition:
    box-shadow 150ms var(--ease-out-strong),
    background-image 150ms var(--ease-out-strong);
}
.crt-key:hover {
  background-image: linear-gradient(180deg, rgb(255 122 41 / 0.26) 0%, rgb(255 122 41 / 0.12) 100%);
  box-shadow:
    inset 0 1px 0 rgb(255 122 41 / 0.35),
    0 0 0 1px rgb(255 122 41 / 0.5),
    0 0 14px -2px rgb(255 122 41 / 0.5);
}
.crt-key:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgb(255 122 41 / 0.55);
}

.crt-screen {
  position: relative;
  overflow: hidden;
  background-color: var(--graphite-plastic);
  box-shadow:
    inset 0 3px 14px rgb(0 0 0 / 0.55),
    inset 0 0 0 1.5px rgb(255 255 255 / 0.06);
}
.crt-screen::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: repeating-linear-gradient(
    to bottom,
    rgb(0 0 0 / 0.28) 0px,
    rgb(0 0 0 / 0.28) 1px,
    transparent 1px,
    transparent 3px
  );
  opacity: 0.5;
}

.jambox-chassis {
  background-image: linear-gradient(
    180deg,
    color-mix(in srgb, var(--paper-cream) 94%, #fff) 0%,
    color-mix(in srgb, var(--paper-cream) 96%, var(--graphite-plastic)) 100%
  );
  box-shadow:
    inset 0 2px 0 rgb(255 255 255 / 0.85),
    inset 0 -4px 10px -6px rgb(35 38 47 / 0.22),
    inset 0 0 0 1px rgb(255 255 255 / 0.4),
    0 22px 48px -28px rgb(35 38 47 / 0.5),
    0 2px 6px rgb(35 38 47 / 0.08);
}

/* ============================================================
   Chart page
   The rows are amber mono-phosphor CRT, so everything inside one
   is coloured from --phosphor / --phosphor-dim rather than the
   paper-mode ink tokens.
   ============================================================ */

/* The countdown is what this page is for, so it can't open below the fold. The
   hero keeps its full type; what gives is the padding stack between it and the
   first row — 40 + 80 + 48 of section air was starting the deck at y=666 on a
   13" laptop, one clipped row. */
.chart-hero { padding-top: 24px; padding-bottom: 16px; }
.chart-hero-inner { display: flex; max-width: 820px; flex-direction: column; gap: 16px; }
/* Scoped to the chart page — the landing page's VU divider is a section break
   between hero and steps and keeps its wider gap. */
.chart-hero + .vu-block { padding-top: 32px; }
.chart-hero h1 {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 6.5vw, 68px);
  line-height: 1.05;
  font-weight: 700;
  letter-spacing: var(--tracking-tight);
  text-wrap: pretty;
}
.chart-meta {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--muted-foreground);
}

.countdown {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding-top: 24px;
  padding-bottom: 80px;
}
.countdown-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 8px;
}
.countdown-head h2 { color: var(--title-accent-strong); }
.countdown-count {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--muted-foreground);
  font-variant-numeric: tabular-nums;
}

.chart-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 16px;
  row-gap: 12px;
  overflow: hidden;
  border-radius: 14px;
  padding: 16px 16px 16px 24px;
}
.chart-row-spine {
  position: absolute;
  top: 6px;
  bottom: 6px;
  left: 0;
  width: 6px;
  border-radius: 0 999px 999px 0;
}
.chart-rank {
  width: 2.4ch;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 1.953rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--phosphor);
  text-shadow: 0 0 10px rgb(255 122 41 / 0.45);
}
.chart-move { width: 3.5ch; flex-shrink: 0; }
.mv-amber {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--phosphor-dim);
}
.mv-amber--up { color: var(--phosphor); }
.mv-amber--new {
  display: inline-flex;
  align-items: center;
  border: 1px solid color-mix(in srgb, var(--phosphor) 55%, transparent);
  border-radius: 3px;
  padding: 2px 6px;
  font-size: 0.6rem;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--phosphor);
}

.chart-logo {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 7px;
  border: 1px solid color-mix(in srgb, var(--phosphor) 30%, transparent);
  background-color: rgb(255 122 41 / 0.06);
  /* contain, not cover: founders upload wide wordmarks as often as square
     marks, and a wordmark cropped to a 34px square is unreadable. */
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--phosphor-dim);
}

.chart-body { min-width: 0; flex: 1 1 100%; }
.chart-title-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.chart-company {
  margin: 0;
  min-width: 0;
  font-family: var(--font-mono);
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}
.chart-company a,
.chart-company span {
  color: var(--phosphor);
  text-shadow: 0 0 8px rgb(255 122 41 / 0.35);
  text-decoration: none;
  transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.chart-company a:hover { opacity: 0.8; }
.chart-tag {
  display: inline-flex;
  align-items: center;
  border: 1px solid color-mix(in srgb, var(--phosphor) 35%, transparent);
  border-radius: 4px;
  padding: 2px 8px;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--phosphor-dim);
  text-decoration: none;
}
.chart-tag--no1 {
  border-color: color-mix(in srgb, var(--phosphor) 60%, transparent);
  color: var(--phosphor);
  padding: 2px 6px;
}
a.chart-tag:hover { color: var(--phosphor); border-color: var(--phosphor); }
.chart-line {
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--phosphor-dim);
}

.chart-wave {
  display: none;
  width: 140px;
  flex-shrink: 0;
  color: var(--phosphor);
  filter: drop-shadow(0 0 4px rgb(255 122 41 / 0.5));
}
/* Hidden at every width until the votes open in September: the row order still reflects
   plays, but the numbers themselves aren't meant to be readable before then. chart.js
   still renders the element and the chart JSON still carries plays_this_week, so turning
   the counts back on is a `@media (min-width: 640px) { display: inline }` away rather
   than a backend change. */
.chart-plays {
  display: none;
  flex-shrink: 0;
  font-family: var(--font-mono);
  font-size: 0.875rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--phosphor);
}

.chart-play {
  display: inline-flex;
  height: 44px;
  width: 44px;
  flex-shrink: 0;
  align-items: center;
  justify-content: center;
  border-radius: 999px;
  color: var(--phosphor);
  font-size: 0.875rem;
  line-height: 1;
  text-decoration: none;
  transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.chart-play:active { transform: scale(0.98); }

.chart-vote { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.chart-vote-btn {
  display: inline-flex;
  align-items: center;
  min-height: 34px;
  border: none;
  background-color: transparent;
  border-radius: 8px;
  padding: 0 12px;
  font-family: var(--font-mono);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--phosphor);
  cursor: pointer;
}
.chart-vote-btn.voted { pointer-events: none; opacity: 0.55; }
.chart-vote-count {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  color: var(--phosphor-dim);
  white-space: nowrap;
}

/* Arriving from a fresh submission (?landed=<id>) */
@keyframes chart-land {
  0% { opacity: 0; transform: translateY(-46px) scale(1.01); }
  65% { opacity: 1; transform: translateY(4px); }
  100% { opacity: 1; transform: none; }
}
@keyframes chart-glow {
  0%, 55% { box-shadow: 0 0 0 2px var(--phosphor), 0 0 30px -4px rgb(255 122 41 / 0.6); }
  100% { box-shadow: 0 0 0 0 rgb(255 122 41 / 0); }
}
.chart-row.landing {
  animation:
    chart-land 0.9s cubic-bezier(0.2, 0.8, 0.3, 1.15) both,
    chart-glow 3.6s ease-out both;
}
@media (prefers-reduced-motion: reduce) {
  .chart-row.landing { animation: none; box-shadow: 0 0 0 2px var(--phosphor); }
}

/* Amber CRT call-to-action card, and the empty state's paper chassis */
.chart-cta { padding-bottom: 96px; }
.chart-cta-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-cassette);
  padding: 56px 32px;
}
.chart-cta-vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(120% 120% at 50% 50%, transparent 55%, rgb(0 0 0 / 0.5) 100%);
}
.chart-cta-inner { position: relative; z-index: 10; }
.chart-cta-rule { display: flex; align-items: center; gap: 16px; margin-bottom: 24px; }
.chart-cta-rule span:first-child {
  display: block;
  height: 8px;
  width: 8px;
  flex-shrink: 0;
  border-radius: 999px;
  background-color: var(--phosphor);
}
.chart-cta-rule span:last-child {
  height: 1px;
  flex: 1;
  background-color: var(--phosphor);
  opacity: 0.3;
}
.chart-cta-eyebrow {
  font-family: var(--font-terminal);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: var(--tracking-widest);
  text-transform: uppercase;
  color: var(--phosphor);
}
.chart-cta-card h2 {
  margin-top: 20px;
  margin-bottom: 16px;
  max-width: 18ch;
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  line-height: 1.08;
  font-weight: 700;
  color: var(--phosphor);
}
.chart-cta-card p {
  max-width: 560px;
  font-family: var(--font-terminal);
  line-height: 1.5;
  color: var(--phosphor-dim);
}
.chart-cta-actions {
  margin-top: 32px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
}

.chart-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  border-radius: var(--radius-cassette);
  padding: 64px 32px;
  text-align: center;
}
.chart-empty h2 {
  font-family: var(--font-display);
  font-size: 2.4rem;
  line-height: 1.1;
  font-weight: 700;
}
.chart-empty p { max-width: 42ch; line-height: 1.5; color: var(--muted-foreground); }
.chart-empty svg { color: var(--foreground); }

@media (min-width: 768px) {
  .chart-row { flex-wrap: nowrap; column-gap: 24px; padding-right: 24px; }
  .chart-body { flex-basis: auto; }
  .chart-cta-card { padding-inline: 56px; }
}
@media (min-width: 1024px) {
  .chart-wave { display: block; }
}

/* ── Motion system ─────────────────────────────────────────────── */
@keyframes seal-spin {
  from { transform: rotate(-8deg); }
  to { transform: rotate(352deg); }
}
@keyframes fade-up {
  from { opacity: 0; transform: translateY(14px); }
  to { opacity: 1; transform: none; }
}
@keyframes hero-settle {
  from { opacity: 0; transform: scale(1.04); }
  to { opacity: 1; transform: none; }
}
@keyframes rec-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}

@media (prefers-reduced-motion: no-preference) {
  .seal-ring { animation: seal-spin 24s linear infinite; }

  .hero-enter { animation: fade-up 450ms var(--ease-out-strong) both; }
  .hero-enter-1 { animation-delay: 0ms; }
  .hero-enter-2 { animation-delay: 70ms; }
  .hero-enter-3 { animation-delay: 140ms; }
  .hero-enter-4 { animation-delay: 210ms; }

  .hero-image { animation: hero-settle 700ms var(--ease-out-strong) both; }

  .rec-dot { animation: rec-pulse 1.3s ease-in-out infinite; }

  .js-motion .reveal {
    opacity: 0;
    transform: translateY(20px);
    transition:
      opacity 500ms var(--ease-out-strong),
      transform 500ms var(--ease-out-strong);
  }
  .js-motion .reveal.is-visible { opacity: 1; transform: none; }
}

/* ── md breakpoint ─────────────────────────────────────────────── */
@media (min-width: 768px) {
  /* From here up the hero owns the first screen: the header floats over the
     photo, the photo starts at y=0 and runs to the fold, and the copy leaves the
     flow to stand at the bottom of it. The buttons cannot fall under the fold —
     the frame they sit in already ends at it — so there is no reserve/overlap
     pair to keep in step.

     Floating the header is what made the hero fill a laptop: those 96px are
     170px of frame width at 1.768, so at 1440x775 the photo goes 1158 -> 1370
     wide against a 1430 page, and at 1512x866, 1366x768 and 1920x1080 it comes
     out full-bleed and uncropped. min-height keeps the same layout honest on a
     tall window, where the photo is limited by width and can't reach the fold:
     the hero then stops just past the copy instead of stranding it 300px down a
     screen of cream. 380px is the copy block plus its air — an estimate, and a
     safe one, since it only decides how much empty space to fill. */
  .topbar--overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    z-index: 20;
  }
  .hero {
    --hero-fold-reserve: 0px;
    /* 1.18 reaches the bottom of every common laptop shape — 16:10 needs 1.11,
       3:2 needs 1.19 and lands 5px short, which nobody will see. Raising it buys
       nothing on those screens and costs width on all of them. */
    --hero-zoom: 1.18;
    --hero-h: min(100dvh, calc(var(--hero-photo-h) + 380px));
    min-height: var(--hero-h);
  }
  /* The header now sits on photograph rather than on cream. The wash is the same
     idea as .hero-fade-b, shorter and gentler — the top of this frame is a pale
     ceiling, so the wordmark only needs a little help. */
  .hero-media::before {
    content: '';
    position: absolute;
    inset: 0 0 auto 0;
    z-index: 3;
    height: 168px;
    background-image: linear-gradient(
      to bottom,
      rgb(242 239 230 / 0.82) 0%,
      rgb(242 239 230 / 0.45) 45%,
      rgb(242 239 230 / 0) 100%
    );
  }
  .hero-seal {
    right: 32px;
    bottom: calc(100% + 12px);
    width: min(160px, calc(var(--hero-photo-h) * 0.18));
  }
  .hero-copy {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 48px;
    margin-top: 0;
    /* Back to the page measure now that the photo is full-bleed at every width
       — there is no inset frame left for the headline to start out beside. */
    max-width: 1200px;
    margin-inline: auto;
  }
  /* Only as tall as the copy actually standing on the photo. On a laptop that is
     the whole block (420px, capped) and the wash is broad and flat rather than
     opaque — the point of all this is to see the photograph, and dark ink on a
     pale console does most of the legibility work. On a tall window the frame is
     limited by width, the hero stops just past the copy, and the copy lands
     below the photo rather than on it: there the same expression collapses to
     ~60px, where a fixed 420 bleached three quarters of a frame nothing was
     standing on. The 456 is the copy's height plus its 48px pin plus a little
     ramp above it. */
  .hero-fade-b {
    height: clamp(
      0px,
      calc(var(--hero-photo-h) - var(--hero-h) + 456px),
      420px
    );
    /* Steep for 48px, flat above it, because those are two different jobs.

       The bottom 48px is the seam: the photo stops dead at the fold and has to
       dissolve into the cream page under it, so that strip still reaches 0.97.
       It is a length rather than a percentage because the band collapses to
       ~150px on a tall window and a percentage would take the seam down with
       it. It is 48 because the copy now pins at 48 — the seam sits entirely
       below the buttons, which is the whole point of having moved the pin.

       Everything above the seam is only legibility, and legibility here is
       cheap: measured per glyph pixel, graphite on this photo runs 6.3-7.4:1
       median at 0.32-0.44, because the bottom third of the frame is a pale
       console. This used to run 0.9-0.97 across the whole button band, which
       is what erased the console keys behind them.

       0.26 at the top is not enough to carry the eyebrow — that was the first
       guess and it measured 3.0:1. The eyebrow was living on .hero-fade-r's
       0.92, not on this, and the fix is on the ink; see below. */
    background-image: linear-gradient(
      to top,
      rgb(242 239 230 / 0.97) 0%,
      rgb(242 239 230 / 0.44) 48px,
      rgb(242 239 230 / 0.36) 32%,
      rgb(242 239 230 / 0.32) 62%,
      rgb(242 239 230 / 0.26) 90%,
      rgb(242 239 230 / 0) 100%
    );
  }
  /* The right-fade is inherited from the source design, where the copy was
     inset in the left third and needed its own cream bed. The copy runs the
     full 1000px measure along the bottom now and takes its contrast from
     .hero-fade-b, so at 0.92 this was bleaching the reel-to-reel deck and the
     hatch for nothing. Kept, gentler, because a little wash off the left edge
     still reads as paper rather than as a photo pasted onto the page. */
  .hero-fade-r {
    background-image: linear-gradient(
      to right,
      rgb(242 239 230 / 0.38) 0%,
      rgb(242 239 230 / 0.1) 45%,
      rgb(242 239 230 / 0) 100%
    );
  }
  /* Wider measure buys the headline back its two-line setting at full size —
     cheaper than shrinking the type to win the same vertical space. */
  .hero-copy-inner { max-width: 1000px; gap: 20px; }
  /* Scoped to the landing hero — the chart page shares .hero-lede, on cream,
     where muted grey is right. Here it is 20px of text standing on her orange
     flight suit, and muted grey measures 2.9:1 median against that at the new
     wash (4.8:1 at the old one, which already failed a third of its pixels).
     Graphite measures 7.4:1 median and 4.1:1 at its worst, so the wash no
     longer has to carry this line and can come off the console. Size and face
     still separate it from the headline. */
  .hero-copy-inner .hero-lede { max-width: 700px; color: var(--foreground); }
  /* Same problem, same fix. --title-accent-strong is tuned to 5.16:1 on paper
     cream, and on the photo it was living on the wash rather than on the
     colour: behind 0.92 of .hero-fade-r it measured 4.48:1 median over the
     reel deck and already failed on half its own pixels. Clear of the wash it
     measures 3.0:1. This is the same burnt tangerine taken down until it holds
     5.9:1 median there on its own. The rec-dot stays at full accent, which is
     where the accent was doing the work anyway. */
  .hero-copy-inner .hero-eyebrow { color: #5e2004; }
  .steps { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .record-card { padding-inline: 56px; }
}

/* Under ~720px of viewport the desktop copy block fills the whole frame — at
   568px it is taller than the photo — and the stamp, which rides above the
   headline, would climb off the top of the photo and print over the topbar's
   chart chip. A squeezed frame keeps the buttons, not the decoration. */
@media (min-width: 768px) and (max-height: 719px) {
  .hero-seal { display: none; }
}

/* Type floor. The source design's 0.6-0.7rem mono labels land at 9.6-11.2px,
   under the 12px minimum TESTING_GUIDE asserts for narrow viewports — same
   rule app.css already applies to its own eyebrows. */
@media (max-width: 900px) {
  .vu-scale,
  .countdown-count,
  .chart-vote-btn,
  .chart-vote-count { font-size: 0.8rem; }

  .chart-tag,
  .mv-amber--new { font-size: 0.75rem; }
}

/* The source design keeps the header on one non-wrapping row; under ~640px the
   chart link runs out of room and prints over the wordmark. Let it drop to its
   own line instead. */
@media (max-width: 640px) {
  .topbar { flex-wrap: wrap; gap: 14px; padding-bottom: 22px; }
  .topbar-brand { flex: 1 0 100%; }
}

/* Touch targets: the mono CTAs and the round play key are already >=44px, but
   the header links and the vote button sit under a finger. */
@media (hover: none) {
  .topbar-link { display: inline-flex; align-items: center; min-height: 44px; }
  .chart-vote-btn { min-height: 44px; }
}
