/* ==================================================================
   Notchix — design system

   Swiss poster. Three flat fields, red, ink and paper, and one family,
   Archivo, used from its widest, heaviest end (the wordmark and the
   headlines) to its plain text width (everything you read). Big type does
   the work; almost nothing is decorated.

   Every section declares its field (.field-red / .field-ink / .field-paper),
   and the field sets the tokens: --bg, --text, --muted, --faint, --rule,
   --accent, --on-accent. Components only ever read tokens, so the same
   button or list is correct on any field without a per-field override.

   Contrast was worked out, not eyeballed:
     ink on red      4.62:1   passes body text, just. So on red, ALL text is
                              solid ink. Any transparency drops it under 4.5.
                              Hierarchy there comes from size and weight.
     red on paper    3.66:1   large text and UI only, never small copy.
     faint on paper  needs >= 0.62 ink to clear 4.5:1; it is 0.62.
   Scripts/contrast.py checks all of it.

   The turntable, the MacBook and the notch UI keep their own dark,
   rounded, shaded look. They depict physical things and real app UI, and
   are exempt from the flat, square page chrome around them.
   ================================================================== */

/* ---------------------------------------------------------- fonts */
/* One self-hosted variable file, latin subset: Archivo, weight 100-900 and
   width 62-125%. The wordmark and headlines use it at 125% / 900, body text
   at 100% / 400. One family from poster to paragraph is how Swiss work is
   set; the earlier grotesk-plus-italic-serif pairing is what the page was
   told looked generated. No third-party request, no visitor IP sent to a
   font CDN. */
@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url(assets/fonts/archivo-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* the three fields */
  --red:   #E8340A;
  --ink:   #0B0A09;
  --paper: #F2EDE6;

  /* default field is ink; sections override with .field-* below */
  --bg:        var(--ink);
  --text:      var(--paper);
  --muted:     rgba(242, 237, 230, 0.66);
  --faint:     rgba(242, 237, 230, 0.52);
  --ghost:     rgba(242, 237, 230, 0.18);
  --rule:      rgba(242, 237, 230, 0.14);
  --rule-lift: rgba(242, 237, 230, 0.26);
  --accent:    var(--red);
  --on-accent: var(--ink);
  --accent-wash: rgba(232, 52, 10, 0.12);
  --accent-lift: #ff4a1c;
  --accent-deep: #b92703;

  /* dark material, for the things that are depicted rather than laid out:
     the turntable, the MacBook, the app's own panels */
  --void:   #060505;
  --core:   #121110;
  --core-2: #1a1816;
  --shell:  rgba(250, 248, 242, 0.035);

  --live: #8fae8b;   /* the one second signal; muted sage */

  --sans:    "Archivo", ui-sans-serif, -apple-system, system-ui, sans-serif;
  --mono:    "Archivo", ui-sans-serif, -apple-system, system-ui, sans-serif;
  --display: "Archivo", ui-sans-serif, -apple-system, system-ui, sans-serif;

  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px; --s5: 24px;
  --s6: 32px; --s7: 48px; --s8: 72px; --s9: 112px; --s10: 160px;

  /* Page chrome is square. Rounded corners are kept for what they depict:
     the app's panels (its own 14pt) and the hardware. */
  --r-shell: 18px;
  --r-core:  14px;
  --r-md:    10px;
  --r-sm:    6px;
  --r-pill:  999px;
  --r-chrome: 0px;

  --ease:     cubic-bezier(0.32, 0.72, 0, 1);
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  --page: 1320px;
}

/* Fields. A section is one of these, full bleed, edge to edge. */
.field-ink, .field-paper, .field-red { background: var(--bg); color: var(--text); }
.field-ink { /* the :root defaults */ }
.field-paper {
  --bg: var(--paper); --text: var(--ink);
  --muted: rgba(11, 10, 9, 0.72);
  --faint: rgba(11, 10, 9, 0.62);
  --ghost: rgba(11, 10, 9, 0.22);
  --rule:  rgba(11, 10, 9, 0.16);
  --rule-lift: rgba(11, 10, 9, 0.30);
  --accent: var(--red); --on-accent: var(--ink);
}
.field-red {
  --bg: var(--red); --text: var(--ink);
  /* no faded ink on red: see the contrast note at the top */
  --muted: var(--ink); --faint: var(--ink);
  --ghost: rgba(11, 10, 9, 0.35);
  --rule:  rgba(11, 10, 9, 0.30);
  --rule-lift: rgba(11, 10, 9, 0.55);
  --accent: var(--ink); --on-accent: var(--paper);
  --accent-wash: rgba(11, 10, 9, 0.08);
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; overflow-x: clip; }

body {
  margin: 0;
  background: var(--ink);
  color: var(--text);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.55;
  font-weight: 400;
  letter-spacing: 0;
  text-rendering: optimizeLegibility;
  font-synthesis-weight: none;   /* fail loudly rather than fake a weight */
  overflow-x: clip;
  position: relative;
}

/* Film grain. Fixed and pointer-events-none so it never repaints on scroll —
   attaching this to a scrolling container is what tanks mobile framerate. */
body::after {
  content: "";
  position: fixed; inset: 0; z-index: 90;
  pointer-events: none;
  opacity: 0.035;
  background-image:
    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.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='520' height='520'%3E%3Cfilter id='c'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.012' numOctaves='2'/%3E%3C/filter%3E%3Crect width='520' height='520' filter='url(%23c)' opacity='0.55'/%3E%3C/svg%3E");
  background-size: 160px 160px, 520px 520px;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
  }
}

img { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
/* Links inside running text have to look like links. The privacy links added
   to the pricing list and the FAQ were the same colour as the words around
   them and had no underline, which made them invisible. */
p a, li a { text-decoration: underline; text-decoration-color: var(--ghost); text-underline-offset: 3px; }
p a:hover, li a:hover { text-decoration-color: currentColor; }

::selection { background: var(--ink); color: var(--paper); }
.field-ink ::selection { background: var(--red); color: var(--ink); }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }

/* ------------------------------------------------------------- type */
/* Headlines are the widest, heaviest cut, uppercase, set tight. At 125%
   width the counters are open enough that tracking can go slightly negative
   without the letters colliding. Body stays at normal width: the wide cut is
   for being seen, the normal cut is for being read. */
h1, h2, h3 { margin: 0; }
h1, h2, .wordmark {
  font-stretch: 125%; font-weight: 900; text-transform: uppercase;
  line-height: .9; letter-spacing: -0.018em;
  text-wrap: balance;
}
h1 { font-size: clamp(2.8rem, 7.4vw, 7.2rem); }
h2 { font-size: clamp(2.1rem, 5.2vw, 4.9rem); }
h3 { font-stretch: 110%; font-weight: 700; font-size: 1.05rem; line-height: 1.3; letter-spacing: -0.005em; }
p { text-wrap: pretty; }

/* Emphasis inside a headline: the accent, same cut. The italic serif that
   used to do this is gone with the rest of the old voice. */
.display { color: var(--accent); }

/* Labels: small, wide, tracked, with a square rather than a dot. */
.tag {
  display: inline-flex; align-items: center; gap: 10px;
  font-stretch: 125%; font-size: 11px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text);
}
.tag::before { content: ""; width: 8px; height: 8px; background: var(--accent); }
.tag.is-live::before {
  background: var(--live);
  animation: pulse 2.4s var(--ease) infinite;
}
@keyframes pulse { 50% { opacity: .35; } }

.lede {
  font-size: clamp(1.04rem, 1.4vw, 1.2rem);
  line-height: 1.62; letter-spacing: -0.008em;
  color: var(--muted); max-width: 50ch;
}
.tnum { font-variant-numeric: tabular-nums; }

/* ----------------------------------------------------------- layout */
.wrap { width: 100%; max-width: var(--page); margin-inline: auto; padding-inline: var(--s5); position: relative; z-index: 1; }
section { padding-block: var(--s10); position: relative; }
.rule-top { border-top: 1px solid var(--rule); }
.head { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s4); margin-bottom: var(--s8); max-width: 56ch; }

/* --------------------------------------------- double-bezel enclosure */
.bezel { padding: 6px; border-radius: var(--r-shell); background: var(--shell); border: 1px solid var(--rule); }
.bezel > .core {
  border-radius: var(--r-core);
  background: var(--core);
  border: 1px solid var(--rule);
  overflow: hidden;
}

/* ---------------------------------------------------------- buttons */
.btn {
  position: relative; display: inline-flex; align-items: center; gap: .32em;
  padding: 15px 24px; border-radius: var(--r-chrome);
  font-stretch: 112%; font-size: 14px; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
  border: 2px solid transparent; cursor: pointer;
  white-space: nowrap;
}
/* Colours come from the field. The primary used to hard-code its text colour,
   which went ink-on-ink the moment it sat on the red field: an invisible
   buy button. --on-accent is whatever reads on this field's accent. */
.btn-primary { background: var(--accent); color: var(--on-accent); }
.btn-primary:hover { background: var(--text); color: var(--bg); }
.btn-primary:active { background: var(--accent-deep); color: var(--on-accent); }
.field-red .btn-primary:active { background: #2a2622; }

.btn-quiet { background: transparent; color: var(--text); border-color: var(--text); }
.btn-quiet:hover { background: var(--text); color: var(--bg); }

.btn-sm { padding: 9px 14px; font-size: 12px; }

/* -------------------------------------------------------------- nav */
/* An ink bar, square, the same on every field it floats over. Still centred
   by translateX(-50%), which motion-layer.js's hide-on-scroll carries. */
.nav {
  position: fixed; top: 16px; left: 50%; transform: translateX(-50%); z-index: 80;
  display: flex; align-items: center; gap: var(--s6);
  padding: 6px 6px 6px 16px; border-radius: var(--r-chrome);
  background: var(--ink); color: var(--paper);
  --text: var(--paper); --muted: rgba(242, 237, 230, 0.66);
  --accent: var(--red); --on-accent: var(--ink); --bg: var(--ink);
}
.brand { display: flex; align-items: center; gap: 9px; font-stretch: 125%; font-weight: 900; letter-spacing: 0.02em; text-transform: uppercase; font-size: 13px; }
.brand-mark { width: 10px; height: 10px; background: var(--red); }
.brand-mark::after { content: none; }
.nav-links { display: flex; gap: var(--s5); font-size: 12px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.nav-links a:hover { color: var(--text); }

/* ------------------------------------------------------------- hero */
/* ------------------------------------------------------------- hero */
/* The LAM layout: the wordmark fills the width at the top, and the
   statement and the buy button sit on the bottom edge, deliberately
   unbalanced: copy wide on the left, the action narrow on the right. */
.hero { padding: 0; min-height: 100dvh; display: flex; }
.hero-in {
  display: grid; grid-template-rows: auto 1fr;
  padding-top: 96px; padding-bottom: var(--s7);
}

/* The wordmark's font-size is the wrap's content width over its measured
   em-width, so it spans the page exactly with no script. Re-measure whenever
   the word changes: "PLATTER" was 5.95em; "NOTCHIX" is 5.986em (measured in
   the browser as built, letters as separate spans, which switches kerning
   off). 6.01 leaves a hair of slack; the old 5.97 overflowed NOTCHIX by 3px. */
.wordmark {
  --wm-width: min(100vw, var(--page)) - 2 * var(--s5);
  font-size: calc((var(--wm-width)) / 6.01);
  letter-spacing: -0.03em; line-height: .78;
  white-space: nowrap; overflow: hidden;
  display: flex; padding-bottom: .04em;
}
.wordmark span { display: inline-block; }

.hero-foot {
  align-self: end;
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: var(--s7); align-items: end;
  padding-top: var(--s8);
  border-top: 2px solid var(--text);
  margin-top: var(--s7);
}
.hero-copy h1 { font-size: clamp(2.4rem, 5.4vw, 5.2rem); margin-bottom: var(--s5); }
.hero-copy .lede { max-width: 46ch; color: var(--text); }
.hero-buy { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s3); }
.hero-buy .tag { margin-top: var(--s4); }

.anchor-note { margin-top: var(--s5); font-size: 13px; color: var(--faint); display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
.anchor-note s { color: var(--ghost); }
.anchor-note b { color: var(--muted); font-weight: 500; }

/* ---------------------------------------------------- vinyl player */
/*
   Built against MD Vinyl's own product shots. Four things carry the illusion,
   and the first version got all four wrong:
     1. The label is SMALL — about a third of the diameter. A big label reads
        as a coloured disc, not a record.
     2. The grooves must be visible across the whole black face.
     3. The tonearm is a chrome tube with a black headshell angled at the end,
        mounted in a recessed well — not a line with a dot on it.
     4. There is a metal spindle through the centre hole.
*/

.player { display: grid; grid-template-columns: minmax(0, 1fr); place-items: center; position: relative; padding: var(--s7) var(--s6) var(--s6); }
.deck { position: relative; width: min(100%, 390px); aspect-ratio: 1 / 1; }

/* The plinth the record sits on. */
.deck::before {
  content: ""; position: absolute; inset: -4%; border-radius: 50%;
  background: radial-gradient(circle at 36% 26%, #1e1b19, #0b0a09 72%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.06), 0 50px 90px -42px rgba(0,0,0,1);
}

.record {
  position: absolute; inset: 3%; border-radius: 50%;
  background: radial-gradient(circle at 34% 26%, #232020 0%, #131111 40%, #060505 100%);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.09), inset 0 0 70px rgba(0,0,0,.95);
  animation: spin 3.4s linear infinite;
  animation-play-state: paused;
  will-change: transform;
}
.is-playing .record { animation-play-state: running; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Grooves, edge to label. Fine and many — this is most of what says "vinyl". */
.grooves {
  position: absolute; inset: 0; border-radius: 50%;
  background: repeating-radial-gradient(circle at 50% 50%,
    rgba(255,255,255,.085) 0 1px, rgba(0,0,0,.42) 1px 2.2px, transparent 2.2px 3.4px);
  /* The run-out: a smooth band just outside the label. */
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 0 20.5%, #000 22.5% 97%, transparent 99%);
          mask: radial-gradient(circle at 50% 50%, transparent 0 20.5%, #000 22.5% 97%, transparent 99%);
  opacity: 1;
}

/* Album art, cropped to a circle — a THIRD of the diameter, as on a real record. */
.label {
  position: absolute; inset: 30%; border-radius: 50%; overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.7), 0 0 34px -8px var(--art, transparent);
  transition: box-shadow .9s var(--ease);
  background-size: cover; background-position: center;
}
/* The spindle through the centre hole. Does not turn. */
.spindle {
  position: absolute; top: 50%; left: 50%; width: 3.4%; aspect-ratio: 1;
  transform: translate(-50%, -50%); border-radius: 50%;
  background: linear-gradient(150deg, #cfcac3, #6d6863 60%, #2b2825);
  box-shadow: 0 0 0 2px rgba(0,0,0,.75), 0 2px 5px rgba(0,0,0,.9);
  z-index: 4;
}

/* Fixed light source over a turning disc. */
.sheen {
  position: absolute; inset: 3%; border-radius: 50%; pointer-events: none; z-index: 3;
  background: linear-gradient(118deg, rgba(255,255,255,.10) 0%, transparent 28%, transparent 72%, rgba(255,255,255,.05) 100%);
}

/* ---- tonearm ---- */

/* The recessed circle the pivot sits in. Static — the arm turns, the well does not. */
.well {
  position: absolute; top: -2.5%; right: -4.5%;
  width: 22%; aspect-ratio: 1; border-radius: 50%; z-index: 5;
  background: rgba(255,255,255,.02);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
}

.tonearm {
  position: absolute; top: -1%; right: -3%;
  width: 19%; height: 82%; z-index: 6;
  transform-origin: 50% 11.5%;          /* the pivot's own centre */
  /* Positive = clockwise. The arm points DOWN from its pivot, so clockwise
     swings the headshell LEFT, across the record. Negative threw it off
     the right-hand edge entirely. */
  transform: rotate(14deg);             /* parked, off the rim */
  transition: transform 1.2s var(--ease);
  pointer-events: none;
}
.is-playing .tonearm { transform: rotate(var(--arm, 30deg)); }

/* Pivot housing: a rounded block, not a dot. */
.tonearm .pivot {
  position: absolute; top: 4%; left: 50%; transform: translateX(-50%);
  width: 86%; height: 21%; border-radius: 5px;
  background: linear-gradient(155deg, #4a4540, #191714);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.24), 0 4px 12px -4px rgba(0,0,0,1);
}
.tonearm .pivot::after {
  content: ""; position: absolute; top: 50%; left: 50%;
  width: 52%; height: 54%; transform: translate(-50%, -50%);
  border-radius: 2px; background: #100e0d;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14);
}

/* Chrome tube — long enough to carry the headshell over the grooves. */
.tonearm .tube {
  position: absolute; top: 18%; left: 50%;
  width: 4px; height: 70%; transform: translateX(-50%);
  border-radius: 99px;
  background: linear-gradient(90deg, #615c57 0%, #d6d1ca 36%, #f4f0ea 50%, #b2ada6 64%, #4e4a47 100%);
  box-shadow: 0 2px 7px rgba(0,0,0,.85);
}

/* Headshell: a black block angled off the end of the tube. */
.tonearm .headshell {
  position: absolute; bottom: 6%; left: 50%;
  width: 40%; height: 10%;
  transform: translateX(-50%) rotate(26deg);
  transform-origin: 50% 0;
  border-radius: 2px 2px 3px 3px;
  background: linear-gradient(155deg, #3f3a36, #131110);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.18), 0 3px 9px rgba(0,0,0,.95);
}
.tonearm .headshell::after {
  content: ""; position: absolute; bottom: -26%; right: 24%;
  width: 2px; height: 30%; border-radius: 99px;
  background: var(--accent);
}

.readout { margin-top: var(--s7); }
.readout-title { font-size: 17px; font-weight: 580; letter-spacing: -0.02em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.readout-artist { font-size: 13.5px; color: var(--faint); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.scrub { margin-top: var(--s4); }
.scrub-track { height: 3px; border-radius: 99px; background: var(--ghost); overflow: hidden; }
.scrub-fill { height: 100%; border-radius: 99px; background: var(--accent); width: 0%; transition: width 1s linear; }
.scrub-time { display: flex; justify-content: space-between; margin-top: 7px; font-size: 11px; color: var(--faint); font-variant-numeric: tabular-nums; }

/* ---- transport, MD Vinyl style ---- */
/* The distinctive part is that the glyph is a LABEL under the control, not an
   icon inside it. The button itself is a blank embossed pill — light catching
   the top edge, shadow under the bottom — so it reads as a physical key. */
.transport { display: flex; align-items: flex-start; gap: var(--s5); margin-top: var(--s6); }
.tctl { display: flex; flex-direction: column; align-items: center; gap: 9px; }

.tctl button {
  width: 74px; height: 38px; border-radius: 19px;
  border: 1px solid rgba(255,255,255,.07);
  background: linear-gradient(180deg, #3a3531 0%, #24211f 58%, #1b1917 100%);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.16),
    inset 0 -1px 0 rgba(0,0,0,.6),
    0 4px 10px -3px rgba(0,0,0,.85);
  cursor: pointer;
}
.tctl button:hover { background: linear-gradient(180deg, #443e39 0%, #2a2724 58%, #201d1b 100%); }
.tctl button:active {
  transform: translateY(1px);
  box-shadow: inset 0 2px 5px rgba(0,0,0,.75), inset 0 1px 0 rgba(255,255,255,.05);
}

.tctl.is-main button {
  width: 96px;
  background: linear-gradient(180deg, #4a423c 0%, #2c2724 58%, #201d1a 100%);
}

.tctl span {
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--faint);
  display: flex; align-items: center; justify-content: center;
}
.tctl:hover span { color: var(--muted); }
.tctl.is-main span { color: var(--muted); letter-spacing: .2em; }

.picks-note { margin: var(--s3) 0 0; font-size: 11.5px; color: var(--faint); }

.picks { display: flex; flex-wrap: wrap; gap: 6px; margin-top: var(--s5); }
.pick {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px 5px 6px; border-radius: var(--r-pill);
  background: var(--shell); border: 1px solid var(--rule);
  font-size: 12px; color: var(--muted); cursor: pointer;
}
.pick i {
  width: 16px; height: 16px; border-radius: 4px; display: block;
  background-size: cover; background-position: center;
}
.pick:hover { color: var(--text); border-color: var(--rule-lift); }
.pick[aria-pressed="true"] { background: var(--accent-wash); border-color: rgba(217,120,87,.35); color: var(--text); }

/* -------------------------------------------------------- the demo */
/* Cropped to the top of the frame: the source renders are 1760x600 with the
   notch in the upper half and empty desktop below, so showing the whole frame
   letterboxes a lot of nothing. */
.demo-screen { position: relative; aspect-ratio: 1760 / 430; background: var(--void); }
.demo-screen img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 50% 0%;
  opacity: 0; transform: scale(.985);
  transition: opacity .9s var(--ease), transform .9s var(--ease);
}
.demo-screen img[data-on] { opacity: 1; transform: scale(1); }

.demo-steps { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1px; background: var(--rule); margin-top: 6px; border-radius: var(--r-md); overflow: hidden; }
.demo-step {
  background: var(--core); padding: var(--s4) var(--s5); text-align: left; cursor: pointer;
  border: 0; color: inherit; font: inherit; position: relative;
}
.demo-step:hover, .demo-step[aria-selected="true"] { background: var(--core-2); }
.demo-step-k { display: block; font-family: var(--mono); font-size: 10px; letter-spacing: .16em; text-transform: uppercase; color: var(--faint); }
.demo-step-t { display: block; font-size: 13.5px; margin-top: 4px; color: var(--muted); }
.demo-step[aria-selected="true"] .demo-step-t { color: var(--text); }
.demo-step::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px; width: 100%;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform .5s var(--ease);
}
.demo-step[aria-selected="true"]::after { transform: scaleX(1); }

/* -------------------------------------------------------- the bento */
.bento { display: grid; grid-template-columns: repeat(6, 1fr); gap: var(--s4); }
.cell { padding: var(--s6); display: flex; flex-direction: column; gap: var(--s3); }
.cell-wide { grid-column: span 4; }
.cell-third { grid-column: span 2; }
.cell-half { grid-column: span 3; }
.cell figure { margin: var(--s4) 0 0; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--rule); }
.cell p { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.6; }
.cell-icon { color: var(--accent); }

/* ------------------------------------------------------ tools list */
.toolgrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); gap: 0;
  /* Same fill as a card, so a row that does not divide evenly ends in empty
     space rather than in a visible panel of a slightly different colour. */
  background: var(--core);
}
/* The hairlines belong to the CARDS, not to the grid. Painting the container
   and letting it show through 1px gaps is the usual trick, but it also paints
   the cells that have no card: at 16 tools in 3 columns the last row's two
   empty cells became one solid light block. A per-card rule leaves a gap a
   gap, whatever the tool count does next. */
.toolcard, .featurecard {
  background: var(--core); padding: var(--s5);
  box-shadow: 1px 0 0 var(--rule), 0 1px 0 var(--rule);
}
.toolcard:hover, .featurecard:hover { background: var(--core-2); }
.toolcard svg, .featurecard svg { color: var(--accent); margin-bottom: var(--s3); }
.toolcard h3, .featurecard h3 { margin-bottom: 3px; }
.toolcard p, .featurecard p { margin: 0; font-size: 13px; color: var(--faint); line-height: 1.55; }

/* --------------------------------------------------------- pricing */

/* Rows divided by hairlines rather than marked with checkmarks: the list is
   a spec, and a column of ticks is the most generic way to present one. */
.includes { list-style: none; margin: var(--s7) 0 0; padding: 0; }
.includes li { padding: var(--s3) 0; font-size: 14.5px; color: var(--muted); }
.includes li + li { border-top: 1px solid var(--rule); }
.includes b { color: var(--text); font-weight: 520; }


/* ------------------------------------------------------------- faq */
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  cursor: pointer; list-style: none; padding-block: var(--s5);
  font-size: 16.5px; font-weight: 500; letter-spacing: -0.015em;
  display: flex; align-items: center; justify-content: space-between; gap: var(--s5);
}
.faq summary:hover { color: var(--accent); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary span.pm { flex: none; position: relative; width: 14px; height: 14px; }
.faq summary span.pm::before, .faq summary span.pm::after {
  content: ""; position: absolute; inset: 0; margin: auto;
  background: var(--muted); border-radius: 99px;
  transition: transform .5s var(--ease), opacity .4s var(--ease);
}
.faq summary span.pm::before { width: 14px; height: 1.5px; }
.faq summary span.pm::after  { width: 1.5px; height: 14px; }
.faq details[open] summary span.pm::after { transform: scaleY(0); opacity: 0; }
.faq p { margin: 0 0 var(--s6); color: var(--muted); font-size: 14.5px; max-width: 72ch; }

/* ---------------------------------------------------------- footer */
.footer { padding-block: var(--s8); border-top: 1px solid var(--rule); }
.footer-in { display: flex; flex-wrap: wrap; gap: var(--s6); align-items: flex-start; justify-content: space-between; }
.footer small { color: var(--faint); font-size: 12.5px; line-height: 1.65; display: block; max-width: 46ch; }
.footer a { color: var(--muted); border-bottom: 1px solid var(--rule-lift); }
.footer a:hover { color: var(--text); }

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

/* Numbers count up when they arrive, so a spec reads as a measurement. */
.count-up { font-variant-numeric: tabular-nums; }

/* -------------------------------------------------- scroll reveals */
/* Deliberately none. Reveals are Motion's job (motion-layer.js) and it hides
   elements from JS, so a script that fails to load leaves a plain readable
   page instead of a blank one. Hiding [data-reveal] here would undo that: the
   safety net clears the inline opacity and it would fall back to this rule. */

/* -------------------------------------------------------- responsive */
@media (max-width: 1000px) {
  /* minmax(0, 1fr), not 1fr. A bare 1fr keeps an implicit min-content floor,
     and the player below it has the same floor on its own column, so the
     deck's min(100%, 390px) could never shrink under what it already measured.
     On a 375px phone that held the hero 13px wider than the screen, clipped
     by the body's overflow: clip, with the lede and the player's right edge
     cut off. Nothing inside looked wide; the floor was circular. */
  .hero-foot { grid-template-columns: minmax(0, 1fr); gap: var(--s6); }
  .bento { grid-template-columns: repeat(2, 1fr); }
  .cell-wide, .cell-third, .cell-half { grid-column: span 1; }
  .nav-links { display: none; }
}
@media (max-width: 768px) {
  section { padding-block: var(--s9); }
  .hero { padding-top: 132px; }
  .bento { grid-template-columns: 1fr; }
  .demo-steps { grid-template-columns: repeat(2, 1fr); }
  .price-core { padding: var(--s6) var(--s5); }
  .wrap { padding-inline: var(--s4); }
  .cell { padding: var(--s5); }
  .deck { width: min(100%, 320px); }
}
@media (max-width: 520px) {
  .player { padding: var(--s6) var(--s4) var(--s5); }
  .price-amount { font-size: 3rem; }
  .demo-steps { grid-template-columns: 1fr; }
  .nav { gap: var(--s3); padding-left: 14px; }
}

/* Reading progress, driven by Motion's scroll() binding. */
.read-progress {
  position: fixed; top: 0; left: 0; z-index: 95;
  height: 2px; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: var(--accent);
  pointer-events: none;
}


/* ==================================================================
   Surface effects

   What is left after the vibe-coding audit. The border beam and the
   cursor spotlight were cut with the rest of the decorative hover motion;
   hover now changes colour or border instantly and does not move. What
   remains is motion that carries information: headings arriving as the
   section scrolls in, and the demo's own state changes.
   ================================================================== */

/* -------------------------------------------------- per-word headings */
/* Words are wrapped in JS and animated by Motion. The overflow clip is what
   makes them rise out of the line rather than fade in place. */
.word { display: inline-block; overflow: hidden; vertical-align: bottom; }
.word > i { display: inline-block; font-style: inherit; will-change: transform; }

/* Digits that roll need a fixed advance or the line reflows on every frame. */
[data-price], .count-up { font-variant-numeric: tabular-nums; }

@media (prefers-reduced-motion: reduce) {
}

/* ------------------------------------------------------------- legal */
/* privacy.html and terms.html. A reading measure, not a layout: 68ch, body
   text at full contrast, nothing decorative. */
.legal { max-width: 68ch; margin: 0 auto; padding: 150px var(--s5) var(--s9); position: relative; z-index: 1; }
.legal h1 { font-size: clamp(2.2rem, 4vw, 3rem); margin: var(--s4) 0 var(--s2); }
.legal h2 { font-size: 1.2rem; letter-spacing: -0.015em; margin: var(--s7) 0 var(--s3); }
.legal p, .legal li { color: var(--muted); font-size: 15.5px; line-height: 1.7; }
.legal ul { padding-left: 1.1em; margin: 0; }
.legal li + li { margin-top: var(--s2); }
.legal b { color: var(--text); font-weight: 520; }
.legal code { font-family: var(--mono); font-size: .9em; color: var(--text); }
.legal .updated { font-family: var(--mono); font-size: 11px; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); margin: 0; }

/* Unfilled fields are loud on purpose: a page that ships with one of these
   visible is wrong, and it should look wrong. */
mark.todo { background: var(--accent-wash); color: var(--accent-lift); border: 1px dashed var(--accent); border-radius: 4px; padding: 0 4px; font-family: var(--mono); font-size: .86em; }
.template-note { border: 1px dashed var(--accent); border-radius: var(--r-md); padding: var(--s3) var(--s4); color: var(--muted); font-size: 14px; }

/* --------------------------------------------------- loading states */
/* A screenshot that has not arrived used to be a hole in the page. The <img>
   already reserves its box from its width/height attributes, so the element
   itself is the placeholder: a slow brightness pulse while loading, removed
   the moment it decodes. On failure the browser draws the alt text inside
   the box, so styling it turns a broken-image icon into a sentence saying
   what should have been there. site.js adds and removes the classes. */
img.is-loading { background-color: var(--core); animation: img-wait 1.3s ease-in-out infinite alternate; }
@keyframes img-wait { to { background-color: #1f1b18; } }
/* A failed image stops being a replaced element, and its width="1640"
   attribute becomes a hard 1640px box. The first version of this state blew
   the bento column out to that width and crushed the neighbouring card to a
   word per line. width:100% overrides the attribute; the attribute-mapped
   aspect-ratio still gives the box the right height. */
img.is-failed {
  width: 100%; height: auto;
  background: var(--core-2); color: var(--faint);
  font: 13px/1.5 var(--sans); text-align: center;
  outline: 1px dashed var(--rule-lift); outline-offset: -1px;
}

/* Grid children may not grow past their track because of one wide descendant.
   The default min-width of a grid item is its min-content size, which is how
   a single 1640px broken image resized a whole row. */
.bento > *, .toolgrid > * { min-width: 0; }

/* Present for screen readers, absent on screen. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* The transport keys are 74 + 96 + 74px with two 24px gaps: 292px, which is
   exactly the width that was holding the whole hero open on a phone. Below
   400px they scale down, keeping the MD Vinyl ratio of a wider centre key:
   60 + 80 + 60 + two 12px gaps = 224px, inside the 240px a 320px screen has. */
@media (max-width: 400px) {
  .transport { gap: var(--s3); }
  .tctl button { width: 60px; }
  .tctl.is-main button { width: 80px; }
}

/* Drawn by motion-layer.js on load; replaces the static border-top. */
.hero-foot { position: relative; }
.hero-rule { position: absolute; left: 0; right: 0; top: -2px; height: 2px; background: var(--text); transform-origin: 0 50%; }

/* ============================================================ MacBook */
/* A 14" MacBook Pro, front-on, built from its real proportions so the app's
   renders sit in it at their true size:

     display   1512 x 982 pt            (aspect 1.54)
     notch     ~185 x 32 pt, centred    (12.2% x 3.3% of the display)
     render    880 x 300 pt strip       (58.2% wide, 30.55% tall), centred

   Everything inside the display is sized in cqw, container-query units of
   the display's own width, so the menu bar and notch scale with the laptop
   rather than with the page. The wallpaper gradient is sampled from the
   renders' edge pixels (#212432 at the top, #12141f at 30.55%), so the
   strip's edges vanish into it.

   The aluminium and the glass are gradients and shadows: this is a drawing
   of hardware, exempt from the flat page chrome by the note at the top. */

.demo { padding-bottom: 0; }
.demo .head { margin-bottom: 0; }

.mac-track { position: relative; height: 380vh; }
.mac-stage {
  position: sticky; top: 0; height: 100dvh; overflow: hidden;
  display: grid; grid-template-rows: 1fr auto; justify-items: center; align-items: center;
  padding: 72px var(--s5) 0;
}

.mac {
  /* as wide as fits both the viewport width and its height */
  width: min(92vw, 1000px, calc((100dvh - 260px) * 1.47));
  transform-origin: 50% 2.6%;          /* the notch; motion-layer.js zooms from here */
  will-change: transform;
}

.mac-lid {
  padding: 1.55% 1.55% 2.4%;
  border-radius: 3.2% / 4.8%;
  background: #0a0a0b;
  box-shadow: 0 0 0 1.5px #56575b, inset 0 0 0 1px #1d1d20;   /* the aluminium rim */
}
.mac-display {
  container-type: inline-size;
  position: relative; overflow: hidden;
  aspect-ratio: 1512 / 982;
  border-radius: 1.1% / 1.7%;
  background: linear-gradient(180deg, #212432 0%, #12141f 30.55%, #0b0c12 100%);
}

.mac-menubar {
  position: absolute; inset: 0 0 auto; height: 2.45cqw;
  display: flex; justify-content: space-between; align-items: center;
  padding: 0 1.6cqw; color: rgba(255, 255, 255, .9);
  font: 500 .86cqw/1 -apple-system, "SF Pro Text", var(--sans);
  z-index: 2;
}
.mb-left, .mb-right { display: flex; align-items: center; gap: 1.35cqw; }
.mb-left b { font-weight: 700; }
.mb-ico { height: .8cqw; width: auto; }
.mb-batt { height: .78cqw; }

.mac-frames {
  position: absolute; top: 0; left: 50%; width: 58.2%;
  transform: translateX(-50%);
  aspect-ratio: 1760 / 600;
}
.mac-frames img {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; transition: opacity .45s var(--ease);
}
.mac-frames img[data-on] { opacity: 1; }

.mac-notch {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 12.2cqw; height: 2.1cqw; background: #000;
  border-radius: 0 0 .75cqw .75cqw;
  z-index: 3;
}

.mac-base {
  position: relative; margin: 0 -3.1%;
  height: clamp(10px, 1.9vw, 20px);
  border-radius: 0 0 44% 44% / 0 0 100% 100%;
  background: linear-gradient(180deg, #d6d7da 0%, #b3b4b8 38%, #8c8d91 100%);
  box-shadow: inset 0 1px 0 #f2f2f4;
}
.mac-scoop {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 15%; height: 42%;
  border-radius: 0 0 40% 40% / 0 0 100% 100%;
  background: linear-gradient(180deg, #9a9b9f, #bfc0c4);
}

/* Steps: a Swiss strip along the bottom of the stage. */
.mac-steps {
  list-style: none; margin: 0; padding: var(--s4) 0 var(--s3);
  width: min(100%, var(--page) - 2 * var(--s5));
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s4);
  border-top: 1px solid var(--rule-lift);
  background: var(--bg); position: relative; z-index: 2;
}
.mac-steps li { display: grid; gap: 2px; color: var(--faint); transition: color .3s var(--ease); }
.mac-steps .k { font-stretch: 125%; font-weight: 900; font-size: 12px; letter-spacing: .1em; }
.mac-steps .t { font-stretch: 125%; font-weight: 900; font-size: clamp(14px, 1.6vw, 20px); text-transform: uppercase; }
.mac-steps .d { font-size: 13px; }
.mac-steps li[aria-current="step"] { color: var(--text); }
.mac-steps li[aria-current="step"] .k { color: var(--accent); }
.mac-progress { width: min(100%, var(--page) - 2 * var(--s5)); height: 2px; background: var(--rule); margin-bottom: var(--s4); position: relative; z-index: 2; }
.mac-progress i { display: block; height: 100%; background: var(--accent); transform: scaleX(0); transform-origin: 0 50%; }

@media (max-width: 768px) {
  .mac-track { height: 300vh; }
  .mac-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s3); }
  .mac-steps .d { display: none; }
}

/* ---------------------------------------------------------- pricing */
.price-grid { display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); gap: var(--s8); align-items: start; }
.price-figure {
  margin: 0; display: flex; align-items: baseline; gap: .12em;
  font-stretch: 125%; font-weight: 900; line-height: .8; letter-spacing: -0.03em;
  font-size: clamp(6rem, 17vw, 15rem);
}
.price-once { font-size: .22em; letter-spacing: 0; text-transform: uppercase; }
.country { display: grid; gap: 6px; margin-top: var(--s6); max-width: 360px; }
.country[hidden] { display: none; }
.country-k { font-stretch: 125%; font-weight: 700; font-size: 11px; letter-spacing: .14em; text-transform: uppercase; }
.country select {
  appearance: none; -webkit-appearance: none;
  font: 600 16px/1.2 var(--sans); color: var(--text);
  background: transparent; border: 2px solid var(--text); border-radius: var(--r-chrome);
  padding: 12px 40px 12px 14px; cursor: pointer;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 20px) 55%, calc(100% - 14px) 55%;
  background-size: 6px 6px; background-repeat: no-repeat;
}
.country select:hover { background-color: var(--accent-wash); }
.price-note { margin: var(--s3) 0 0; font-weight: 700; font-size: 15px; }
.price-buy { margin-top: var(--s6); }
.price-fine { margin: var(--s4) 0 0; font-size: 13.5px; max-width: 48ch; }
.price-sub { margin: 0 0 var(--s4); font-size: 1.15rem; font-weight: 600; max-width: 32ch; }
.pricing .includes { margin-top: 0; }
.pricing .includes li { color: var(--text); border-color: var(--rule-lift); }
.pricing .includes li + li { border-top-color: var(--rule-lift); }

@media (max-width: 900px) {
  .price-grid { grid-template-columns: minmax(0, 1fr); gap: var(--s7); }
}

/* ------------------------------------------------ Swiss section chrome */
/* The .head used to cap at 56ch, which is right for a paragraph and wrong
   for a 5rem headline: it stacked them seven lines deep. The measure now
   lives on each part: headlines ~15 wide characters, the lede 56. */
.head { max-width: none; }
.head h2 { max-width: 15ch; }
.head .lede { max-width: 56ch; }

/* Page cards become hairline-framed blocks, square, on the field's own
   ground. The player in #play keeps the dark rounded enclosure: it depicts
   a device, like the MacBook. */
.bezel { border-radius: var(--r-chrome); background: transparent; padding: 0; border: 1px solid var(--rule-lift); }
.bezel > .core { border-radius: var(--r-chrome); background: transparent; border: 0; }
.cell figure { border-radius: var(--r-chrome); }
.toolgrid, .toolcard, .featurecard { background: transparent; }
.toolcard:hover, .featurecard:hover { background: var(--accent-wash); }

#play .bezel { border-radius: var(--r-shell); background: var(--ink); padding: 6px; border-color: transparent; }
#play .bezel > .core { border-radius: var(--r-core); background: var(--core); border: 1px solid rgba(242,237,230,.1); }
#play .bezel { --text: var(--paper); --muted: rgba(242,237,230,.66); --faint: rgba(242,237,230,.52); --rule: rgba(242,237,230,.14); --rule-lift: rgba(242,237,230,.26); --accent: var(--red); color: var(--paper); }

/* FAQ questions at a reading size for this scale; the page type grew and
   13px questions under a 5rem headline read as footnotes. */
.faq summary { font-size: clamp(16px, 1.4vw, 19px); font-weight: 650; padding-block: var(--s4); }
.faq details p { font-size: 16px; max-width: 64ch; color: var(--muted); }
