/* ==========================================================================
   Underscore — underscore.music
   Palette, CRT treatment and type carried over verbatim from theunder.net so
   the product page reads as the same house. The CRT sweep is not decoration
   here: the app itself ships native scanlines (UNDERSCORE_NATIVE_CRT_SCANLINES),
   so the site and the product share one treatment.
   ========================================================================== */

/* --- Fonts -----------------------------------------------------------------
   GT-Haptik, self-hosted under your Grilli Type webfont licence (order 55425).
   The licence permits @font-face embedding and self-hosting across any number
   of domains, subject to the monthly unique-visitor tier on your invoice.
   Do not commit /fonts/ to a public repository — the licence prohibits
   distributing the files outside your organisation.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "GT-Haptik";
  src: url("/fonts/GT-Haptik-Medium.woff2") format("woff2"),
       url("/fonts/GT-Haptik-Medium.woff") format("woff");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "GT-Haptik";
  src: url("/fonts/GT-Haptik-Bold.woff2") format("woff2"),
       url("/fonts/GT-Haptik-Bold.woff") format("woff");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #000000;
  --bg-raise: #0b0c0e;
  --surface: rgba(255, 255, 255, 0.035);
  --surface-hi: rgba(255, 255, 255, 0.06);
  --line: rgba(255, 255, 255, 0.13);
  --line-hi: rgba(255, 255, 255, 0.26);

  --ink: hsl(0, 0%, 86%);      /* 15.2:1 on black */
  --ink-hi: #f4f6f7;           /* 19.4:1 */
  --ink-dim: #969ba1;          /*  7.5:1 */
  --ink-faint: #838a91;        /*  6.0:1 — was #6a6f75 at 4.1:1, below AA */

  /* Interface accent. Monochrome — the only colour on the site comes from the
     chromatic aberration fringing, which is an effect rather than a palette. */
  --accent: #f4f6f7;
  --accent-soft: rgba(255, 255, 255, 0.1);

  /* Retained for the aberration recipes only, not used as UI colour. */
  --cyan: #78ffff;
  --magenta: #ff00c8;

  --maxw: 1120px;
  --gut: clamp(1.25rem, 4vw, 2.5rem);
  --radius: 12px;

  --font-display: "GT-Haptik", "Helvetica Neue", Inter, -apple-system,
    BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  --font-body: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
    Inter, Roboto, Helvetica, Arial, system-ui, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.1;
  margin: 0;
  color: var(--ink-hi);
  letter-spacing: -0.015em;
  /* The original site set text-transform:lowercase on <body>. Kept here on
     display type only, so body copy and the legal entity name stay readable
     and properly capitalised. */
  text-transform: lowercase;
  /* Evens out line lengths so headings don't leave a single word stranded.
     Ignored by browsers that don't support it. */
  text-wrap: balance;
}

p { margin: 0 0 1.15em; }
p:last-child { margin-bottom: 0; }

/* Links are the same white as body copy, so they carry a permanent underline
   rather than relying on colour alone to be identifiable. */
a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
  text-decoration-color: rgba(255, 255, 255, 0.4);
}
a:hover { text-decoration-color: currentColor; }

/* Navigation is identifiable by position, so it opts out of the underline. */
.nav a,
.brand,
.footer__links a,
.footer__bottom a,
.btn {
  text-decoration: none;
}
.nav a:hover,
.footer__links a:hover {
  text-decoration: underline;
  text-decoration-color: currentColor;
}

ul, ol { margin: 0 0 1.15em; padding-left: 1.25em; }
li { margin-bottom: 0.5em; }

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

hr {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 0;
}

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 3px;
}

::selection {
  background: var(--accent);
  color: #000;
}
::-moz-selection {
  background: var(--accent);
  color: #000;
}

/* --- CRT overlay ----------------------------------------------------------
   Verbatim from the original default.css (html:before / html:after), with one
   change: position is `fixed` rather than `absolute`, so the scanlines track
   the viewport on a long scrolling page instead of stopping after the first
   screen. Gradient stops, alphas, background-size, 24px travel and the 2400ms
   timing are unchanged. The vignette has been eased off — see below.

   To dial the sweep back without touching the values, lower --crt-opacity.
   -------------------------------------------------------------------------- */
:root { --crt-opacity: 1; }

/* Stacking order, bottom to top:
     page content (1) → vignette (700) → scanlines (900) → hero icon (901)
     → site header (902)

   theunder.net orders these ...vignette (700) → header (800) → scanlines (900),
   so the sweep passes over the header. Underscore's hero icon has to clear the
   sweep (see .hero__mark), and .hero creates no stacking context, so the icon's
   z-index competes at the root. Left at 800 the header would be BELOW the icon
   and the icon would ride over the header bar as the page scrolls. The header
   therefore goes above both.

   The visible consequence: scanlines no longer sweep across the header bar.
   The header's backdrop-filter still blurs them behind its 78% black, so the
   texture reads through — it just no longer animates there. */
.crt-scan,
.crt-vignette {
  position: fixed;
  inset: 0;
  pointer-events: none;
}

.crt-scan {
  background: linear-gradient(
    to bottom,
    rgba(129, 197, 214, 0.25) 10%,
    rgba(0, 40, 0, 0.25) 20%,
    rgba(70, 40, 50, 0.36) 60%,
    rgba(70, 90, 20, 0.26) 100%
  );
  background-size: 100% 4px;
  height: calc(100% + 24px);
  animation: gradientMove 2400ms linear infinite;
  opacity: var(--crt-opacity);
  transform: translateZ(0);
  will-change: transform;
  z-index: 900;
}

/* Vignette strength. The original was spread 120px at alpha 0.8; this sits
   at the midpoint. Blur radius is unchanged from the original at 250px.
     lighter ......... 80px  / 0.60
     here ............ 100px / 0.72
     original ........ 120px / 0.80
   Nudge these two values; nothing else needs to change. */
:root {
  --vignette-spread: 100px;
  --vignette-alpha: 0.72;
}

.crt-vignette {
  box-shadow: inset 0 0 250px var(--vignette-spread)
    rgba(0, 0, 0, var(--vignette-alpha));
  z-index: 700;
}

@keyframes gradientMove {
  0%   { transform: translateY(0); }
  100% { transform: translateY(-24px); }
}

/* --- Chromatic aberration --------------------------------------------------
   The RGB-fringing recipes from the original stylesheet, lifted verbatim and
   made reusable.

   .ca-text   — the original h2 treatment: blur(0.066rem) plus the red/cyan
                offset text-shadow stack.
   .ca-glow   — the #svg-sine treatment.
   .ca-shift  — the `aberration` keyframe animation from the mobile numpad,
                every stop unchanged. Applied to graphics.

   Deliberate deviation: these are applied to display type and graphics only,
   not to body paragraphs. The original page carried eight words; blurring
   2,000 words of services copy and legal text would undo the reason we are
   rebuilding. Add `.ca-text` to .prose p if you want it everywhere.
   -------------------------------------------------------------------------- */
/* NOTE ON UNITS: the original stylesheet set html{font-size:62.5%}, so its
   rem values resolve against a 10px root — blur(0.066rem) rendered as 0.66px,
   blur(0.08rem) as 0.8px, blur(0.01rem) as 0.1px, blur(0.1rem) as 1px. This
   file uses a 16px root, so those are written in px here to reproduce the
   original appearance exactly rather than 1.6x heavier. */
.ca-text {
  filter: blur(0.66px);
  text-shadow: 2px 0px 2px rgba(251, 12, 12, 0.26),
    -4px 0px 1px rgba(192, 243, 252, 0.15),
    0 0 18px rgba(255, 255, 255, 0.36),
    0 0 0.5px rgba(255, 255, 255, 0.6);
}

/* Display type and interface graphics carry the fringing. */
h1, h2, h3,
.eyebrow,
.card__num,
.step__num,
.footer__entity,
.brand__word {
  filter: blur(0.66px);
  text-shadow: 2px 0px 2px rgba(251, 12, 12, 0.26),
    -4px 0px 1px rgba(192, 243, 252, 0.15),
    0 0 18px rgba(255, 255, 255, 0.36),
    0 0 0.5px rgba(255, 255, 255, 0.6);
}

/* The one place it is dropped: mono labels in the legal pages and the
   definition rows, where 0.66px of blur turns 8pt uppercase into mush. */
.row dt,
.footer h4,
.footer__bottom {
  filter: none;
  text-shadow: none;
}

/* Original #contact-link treatment. */
.btn { filter: blur(0.8px); }
.btn:hover, .btn:focus-visible { filter: blur(0); }

.ca-glow {
  filter: blur(1.32px);
  text-shadow: 0 0 18px rgba(255, 255, 255, 0.36);
}

.ca-shift {
  animation: aberration 2400ms linear infinite;
}

@keyframes aberration {
  0% {
    filter: blur(0.1px) drop-shadow(3px 0px 2px rgba(120, 255, 255, 0.8))
      drop-shadow(-4px 0px 3px rgba(255, 0, 200, 0.46));
  }
  10% {
    filter: blur(0) drop-shadow(-6px 0px 2px rgba(120, 255, 255, 0.8))
      drop-shadow(7px 0px 3px rgba(255, 0, 200, 0.6));
  }
  20% {
    filter: blur(0.1px) drop-shadow(3px 0px 2px rgba(120, 255, 255, 0.2))
      drop-shadow(-4px 0px 3px rgba(255, 0, 200, 0.46));
  }
  30% {
    filter: blur(0.1px) drop-shadow(-3px 0px 2px rgba(120, 255, 255, 0.2))
      drop-shadow(4px 0px 1px rgba(255, 0, 100, 0.6));
  }
  40% {
    filter: blur(0.1px) drop-shadow(3px 0px 2px rgba(120, 255, 255, 0.6))
      drop-shadow(-4px 0px 3px rgba(255, 0, 200, 0.46));
  }
  50% {
    filter: blur(0) drop-shadow(-5px 0px 2px rgba(120, 255, 255, 0.5))
      drop-shadow(4px 0px 1px rgba(255, 0, 200, 0.46));
  }
  60% {
    filter: blur(0.1px) drop-shadow(6px 0px 2px rgba(120, 255, 255, 0.2))
      drop-shadow(-4px 0px 3px rgba(255, 0, 200, 0.8));
  }
  90% {
    filter: blur(1px) drop-shadow(3px 0px 2px rgba(120, 255, 255, 0.5))
      drop-shadow(-4px 0px 3px rgba(255, 0, 200, 0.46));
  }
}

/* --- Layout --------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gut);
}

.section {
  padding-block: clamp(3.5rem, 9vw, 6.5rem);
  position: relative;
  z-index: 1;
}

.section--tight { padding-block: clamp(2.5rem, 6vw, 4rem); }

.section + .section { border-top: 1px solid var(--line); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--accent);
  color: #000;
  padding: 0.7rem 1.1rem;
  z-index: 1000;
  font-weight: 700;
}
.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
}

/* --- Header --------------------------------------------------------------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 902;   /* above the scanlines AND the hero icon — see stacking note */
  background: rgba(0, 0, 0, 0.78);
  backdrop-filter: saturate(140%) blur(12px);
  -webkit-backdrop-filter: saturate(140%) blur(12px);
  border-bottom: 1px solid var(--line);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  min-height: 68px;
}

.brand {
  display: inline-flex;
  align-items: baseline;
  color: var(--ink-hi);
  text-decoration: none;
  white-space: nowrap;
}
.brand:hover { text-decoration: none; color: #fff; }

/* "theundernet" set as "_net". See the WORDMARK note in build.py for why the
   substitution lives in markup rather than in the font's GSUB table. */
.brand__word {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  letter-spacing: -0.01em;
  text-transform: lowercase;
}

/* The underscore stays at its natural depth below the baseline — that is the
   whole joke, it sits *under* — and is stretched slightly so it reads as a
   deliberate mark rather than a stray character. Raising it toward the
   x-height makes it look like a hyphen instead. */
.brand__lig {
  display: inline-block;
  transform: scaleX(1.35);
  transform-origin: left bottom;
  margin-right: 0.05em;
}

.nav {
  display: flex;
  align-items: center;
  gap: 1.6rem;
}

.nav a {
  color: var(--ink-dim);
  font-size: 0.9375rem;
  text-decoration: none;
  padding-block: 0.4rem;
  border-bottom: 1px solid transparent;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.nav a:hover { color: var(--ink-hi); text-decoration: none; }
.nav a[aria-current="page"] {
  color: var(--ink-hi);
  border-bottom-color: var(--accent);
}

.nav__cta {
  color: #000 !important;
  background: var(--accent);
  padding: 0.1em 0.35em !important;
  border-radius: 0;
  font-weight: 700;
  border-bottom: 0 !important;
}
.nav__cta:hover { background: #ffffff; text-decoration: none; }

.nav-toggle {
  display: none;
  background: transparent;
  border: 1px solid var(--line-hi);
  border-radius: 8px;
  color: var(--ink-hi);
  padding: 0.5rem 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  cursor: pointer;
}

@media (max-width: 820px) {
  .nav-toggle { display: block; }
  .nav {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: #060708;
    border-bottom: 1px solid var(--line);
    padding: 0.5rem var(--gut) 1.25rem;
  }
  .nav[hidden] { display: none; }
  .nav a {
    padding-block: 0.85rem;
    border-bottom: 1px solid var(--line);
  }
  .nav__cta {
    margin-top: 0.9rem;
    text-align: center;
    border-radius: 999px;
  }
  .brand__legal { display: none; }
}

/* --- Hero ----------------------------------------------------------------- */
.hero {
  position: relative;
  padding-block: clamp(3.5rem, 11vw, 7.5rem);
  border-bottom: 1px solid var(--line);
  overflow: hidden;
  text-align: center;
}

/* --- Hero mark --------------------------------------------------------------
   theunder.net puts its inline SVG monogram here. Underscore's mark is the
   shipping app icon (app/resources/icons/Underscore.png), so this is a raster
   <img> instead — hence the explicit radius and the absence of `fill`. The icon
   already carries its own scanlines, so it deliberately does NOT get the
   blur/aberration the SVG mark had: doubling the treatment turned it to mush. */
.hero__mark {
  width: 108px;
  max-width: 28vw;
  height: auto;
  margin: 0 auto clamp(1.75rem, 4vw, 2.5rem);
  border-radius: 23%;   /* macOS squircle, near enough at this size */
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.09),
    0 18px 48px rgba(0, 0, 0, 0.7);

  /* ABOVE the CRT sweep, not under it. The icon has its own scanlines baked in,
     and the page's animated 4px sweep over the top of them beat against each
     other and turned the pad grid to mud.

     .hero is position:relative with z-index:auto, so it creates NO stacking
     context and this value competes at the root — which is why .site-header
     had to move too. See the stacking order note in the CRT overlay section. */
  position: relative;
  z-index: 901;
}

/* --- Hero wordmark --------------------------------------------------------
   theunder.net runs its wordmark along an animated sine path (an SVG <textPath>
   fed by drawSine() in main.js). Here it is ordinary text. The CRT treatment is
   unchanged — .ca-glow in the markup carries the same blur(1.32px) and glow the
   sine carried — so only the baseline is different.

   The sine version got its responsive sizing for free, because the SVG scaled
   with its own width. Plain text does not, hence the clamp. Letter-spacing is
   in em rather than the sine's 2pt so it tracks the size down to mobile.
   -------------------------------------------------------------------------- */
.hero__wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(2.75rem, 9.5vw, 5rem);
  line-height: 1;
  letter-spacing: 0.033em;
  text-transform: lowercase;
  color: hsl(0, 0%, 86%);
  text-align: center;
  margin: 0 auto clamp(1rem, 2.5vw, 1.75rem);
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  margin: 0 0 1rem;
}

.hero h1 {
  font-size: 42px;
  max-width: 22ch;
  margin-inline: auto;
  margin-bottom: 1rem;
  /* No local text-shadow — inherits the full aberration recipe. */
}

/* Currently unused - the hero lede was removed. Kept so restoring the
   paragraph is a markup-only change. */
.hero__lede {
  font-size: clamp(1.0625rem, 2.1vw, 1.3125rem);
  color: var(--ink);
  max-width: 58ch;
  margin-inline: auto;
  margin-bottom: 2rem;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  align-items: center;
  justify-content: center;
}

/* The eyebrow reads as a byline under the headline, so it carries the gap
   down to the buttons. */
.hero .eyebrow {
  margin: 0 0 2rem;
}

/* --- Buttons ----------------------------------------------------------------
   No pill, no border. A button is a run of highlighted text — the same
   inverted block you get from ::selection.

   On hover the character under the pointer flashes as a solid block cursor,
   the way the Apple II inverted a character cell at roughly 1.9Hz. Per-letter
   spans are injected by main.js; the accessible name is preserved with
   aria-label so nothing is read out character by character.
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg: transparent;
  --btn-fg: var(--ink-hi);

  display: inline-block;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.125rem;
  line-height: 1.3;
  padding: 0.08em 0.3em;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  text-decoration: none;
  text-transform: lowercase;
  white-space: pre;
  background: var(--btn-bg);
  color: var(--btn-fg);
  transition: background 0.12s ease, color 0.12s ease;
}
.btn:hover { text-decoration: none; }

/* Highlighted at rest. */
.btn--primary {
  --btn-bg: var(--accent);
  --btn-fg: #000;
}

/* Reads as plain text until you touch it, then highlights to match. */
.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink-hi);
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, 0.28);
}
.btn--ghost:hover,
.btn--ghost:focus-visible {
  --btn-bg: var(--accent);
  --btn-fg: #000;
  box-shadow: none;
}

/* Per-character cells.

   GT-Haptik is proportional, so a narrow glyph like "i" would invert to a
   sliver rather than a cell. The box-shadow spread paints the block outward
   past the glyph box — full cell height, a hair of extra width — without
   changing layout, so nothing reflows as the cursor moves along the word.

   535ms is the Apple II's flash period (~1.87Hz); steps(1) keeps the edge
   hard rather than fading. */
.btn__char {
  white-space: pre;
}

.btn__char.is-cursor {
  animation: blockCursor 535ms steps(1, end) infinite;
}

@keyframes blockCursor {
  0%, 49.99% {
    background: var(--btn-fg);
    color: var(--btn-bg);
    box-shadow: 0 0 0 0.075em var(--btn-fg);
  }
  50%, 100% {
    background: transparent;
    color: inherit;
    box-shadow: none;
  }
}

/* A ghost button at rest has a transparent background, so inverting a cell
   would render invisible text. Give the cursor something solid to sit on. */
.btn--ghost:not(:hover) .btn__char.is-cursor {
  animation: blockCursorGhost 535ms steps(1, end) infinite;
}
@keyframes blockCursorGhost {
  0%, 49.99% {
    background: var(--ink-hi);
    color: #000;
    box-shadow: 0 0 0 0.075em var(--ink-hi);
  }
  50%, 100% {
    background: transparent;
    color: inherit;
    box-shadow: none;
  }
}

/* --- Typographic blocks ---------------------------------------------------- */
.section__head { max-width: 68ch; margin-bottom: clamp(2rem, 4vw, 3rem); }

.section__head h2 {
  font-size: clamp(1.75rem, 3.8vw, 2.5rem);
  margin-bottom: 0.9rem;
}

.section__head p {
  color: var(--ink-dim);
  font-size: 1.0625rem;
  margin-bottom: 0;
}

/* .lede inside a section head would otherwise lose to `.section__head p`
   on specificity and render at body size. */
.section__head p.lede {
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  color: var(--ink);
}

.lede {
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  color: var(--ink);
  max-width: 68ch;
}

.prose { max-width: 72ch; }
.prose h2 {
  font-size: clamp(1.5rem, 3vw, 2rem);
  margin-top: 2.75rem;
  margin-bottom: 0.9rem;
}
.prose h3 {
  font-size: 1.1875rem;
  margin-top: 2rem;
  margin-bottom: 0.6rem;
}
.prose h2:first-child, .prose h3:first-child { margin-top: 0; }
.prose ul { color: var(--ink); }
.prose li { margin-bottom: 0.55em; }
.prose--dim p, .prose--dim li { color: var(--ink-dim); }

/* --- Card grid ------------------------------------------------------------- */
.grid {
  display: grid;
  gap: 1.15rem;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }

/* Explicit 2x2 — auto-fit would give three columns at container width and
   orphan the fourth card onto its own row. */
.grid--2up { grid-template-columns: repeat(2, 1fr); }
@media (max-width: 720px) {
  .grid--2up { grid-template-columns: 1fr; }
}

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.6rem 1.5rem;
  transition: border-color 0.18s ease, background 0.18s ease,
    transform 0.18s ease;
}
.card:hover {
  border-color: var(--line-hi);
  background: var(--surface-hi);
}

.card__num {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  color: var(--ink);
  display: block;
  margin-bottom: 0.85rem;
}

.card h3 {
  font-size: 1.1875rem;
  margin-bottom: 0.6rem;
}

.card p {
  color: var(--ink-dim);
  font-size: 0.9688rem;
  margin-bottom: 0;
}

.card ul {
  color: var(--ink-dim);
  font-size: 0.9375rem;
  margin: 0.9rem 0 0;
  padding-left: 1.1em;
}

/* Numbered process steps */
.steps { counter-reset: step; }
.step {
  display: grid;
  grid-template-columns: 4.5rem 1fr;
  gap: 1.25rem;
  padding-block: 1.75rem;
  border-top: 1px solid var(--line);
  align-items: start;
}
.step:last-child { border-bottom: 1px solid var(--line); }
.step__num {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.12em;
  color: var(--ink);
  padding-top: 0.3rem;
}
.step h3 { font-size: 1.25rem; margin-bottom: 0.55rem; }
.step p { color: var(--ink-dim); margin-bottom: 0; }

@media (max-width: 620px) {
  .step { grid-template-columns: 1fr; gap: 0.5rem; }
}

/* --- Definition rows (stack, engagements, contact details) ----------------- */
.rows { border-top: 1px solid var(--line); }
.row {
  display: grid;
  grid-template-columns: minmax(9rem, 15rem) 1fr;
  gap: 1.25rem;
  padding-block: 1.15rem;
  border-bottom: 1px solid var(--line);
  align-items: baseline;
}
.row dt {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0;
}
.row dd { margin: 0; color: var(--ink); }

@media (max-width: 620px) {
  .row { grid-template-columns: 1fr; gap: 0.3rem; }
}

/* --- Tag list -------------------------------------------------------------- */
.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  padding: 0;
  margin: 0;
}
.tags li {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--ink-dim);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 0.35rem 0.8rem;
  margin: 0;
}

/* --- Closing statement ------------------------------------------------------ */
.statement {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.5rem, 3.4vw, 2.125rem);
  line-height: 1.2;
  letter-spacing: -0.015em;
  color: var(--ink-hi);
  text-align: center;
  margin: clamp(3rem, 7vw, 4.5rem) auto 0;
  max-width: 20ch;
}

/* The fringing sits here rather than on .statement. `filter` on a parent
   rasterises its whole subtree into one filtered layer, so a child cannot drop
   it the way `.row dt` drops it — and 0.66px of blur over 17px of aside copy is
   the same mush the sheet already avoids on the mono labels. */
.statement__lead {
  filter: blur(0.66px);
  text-shadow: 2px 0px 2px rgba(251, 12, 12, 0.26),
    -4px 0px 1px rgba(192, 243, 252, 0.15),
    0 0 18px rgba(255, 255, 255, 0.36),
    0 0 0.5px rgba(255, 255, 255, 0.6);
}

/* Wider than the default 20ch, which was cut for three stacked fragments and
   turns a running sentence into a column. Equal specificity to .statement, so
   this MUST stay below it to win on source order. */
.statement--wide { max-width: 34ch; }

/* The punchline runs at a second, quieter register. "Make music. Easy" is the
   claim; the rest is the aside that undercuts it, and setting both at 34px
   flattens the joke into one long shout. em-based so it tracks the clamp. */
.statement__aside {
  display: inline-block;
  font-size: 0.5em;
  line-height: 1.45;
  color: var(--ink-dim);
  letter-spacing: 0;
  margin-top: 0.9em;
  max-width: 30ch;
}

/* --- Screen-reader-only ----------------------------------------------------
   Keeps the document outline intact where a visible heading was removed. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Callout --------------------------------------------------------------- */
.callout {
  border: 1px solid var(--line);
  border-left: 2px solid var(--accent);
  background: var(--surface);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 1.35rem 1.5rem;
  color: var(--ink-dim);
  font-size: 0.9688rem;
}
.callout strong { color: var(--ink-hi); }

/* --- CTA band -------------------------------------------------------------- */
.cta-band {
  border-top: 1px solid var(--line);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.045), transparent 70%);
  text-align: center;
}
.cta-band h2 {
  font-size: clamp(1.6rem, 3.6vw, 2.25rem);
  margin-bottom: 0.9rem;
}
.cta-band p {
  color: var(--ink-dim);
  max-width: 52ch;
  margin-inline: auto;
  margin-bottom: 1.75rem;
}

/* --- Footer ---------------------------------------------------------------- */
.site-footer {
  border-top: 1px solid var(--line);
  background: var(--bg-raise);
  padding-block: clamp(2.5rem, 6vw, 4rem) 2rem;
  position: relative;
  z-index: 1;
  font-size: 0.9375rem;
}

.footer__grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 2.5rem;
  margin-bottom: 2.5rem;
}
@media (max-width: 760px) {
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 2rem; }
  .footer__about { grid-column: 1 / -1; }
}

.footer__about p {
  color: var(--ink-dim);
  max-width: 42ch;
  font-size: 0.9375rem;
}

.footer__entity {
  color: var(--ink-hi);
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.0625rem;
  margin-bottom: 0.5rem;
  display: block;
}

.footer h4 {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin: 0 0 1rem;
}

.footer__links {
  list-style: none;
  padding: 0;
  margin: 0;
}
.footer__links li { margin-bottom: 0.6rem; }
.footer__links a { color: var(--ink-dim); }
.footer__links a:hover { color: var(--ink-hi); }

.footer__bottom {
  border-top: 1px solid var(--line);
  padding-top: 1.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  justify-content: space-between;
  align-items: center;
  color: var(--ink-faint);
  font-size: 0.875rem;
  font-family: var(--font-mono);
}
.footer__bottom a { color: var(--ink-faint); }
.footer__bottom a:hover { color: var(--ink); }

/* --- Contact page ---------------------------------------------------------- */
.contact-grid {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}
@media (max-width: 860px) {
  .contact-grid { grid-template-columns: 1fr; }
}

.field { margin-bottom: 1.15rem; }
.field label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.45rem;
}
.field input,
.field textarea,
.field select {
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--line-hi);
  border-radius: 8px;
  color: var(--ink-hi);
  font-family: var(--font-body);
  font-size: 1rem;
  padding: 0.75rem 0.9rem;
}
.field textarea { min-height: 150px; resize: vertical; }
.field input:focus,
.field textarea:focus,
.field select:focus {
  border-color: var(--accent);
  outline: none;
  box-shadow: 0 0 0 3px var(--accent-soft);
}

.form-note {
  font-size: 0.875rem;
  color: var(--ink-faint);
  margin-top: 1rem;
}

/* --- Case control ----------------------------------------------------------
   .proper opts an element out of the lowercase display treatment. Applied to
   the legal entity name, legal pages, and body copy. Deliberately NOT a
   descendant selector: children with their own text-transform (mono labels,
   eyebrows) keep it, everything else inherits `none`.
   Must stay after .eyebrow / heading rules to win on source order.
   -------------------------------------------------------------------------- */
.proper { text-transform: none; }

.nav a { text-transform: lowercase; }
.brand span:first-of-type { text-transform: lowercase; }

/* --- Placeholder marker (fails check.sh if it ships) ----------------------- */
.todo {
  background: var(--magenta);
  color: #fff;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  padding: 0.1rem 0.4rem;
  border-radius: 4px;
  font-weight: 700;
}

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

/* --- Print ----------------------------------------------------------------- */
@media print {
  .crt-scan, .crt-vignette, .site-header, .cta-band { display: none; }
  body { background: #fff; color: #000; }
  a { color: #000; }
}

/* ==========================================================================
   Underscore additions
   Everything above this line is theunder.net's stylesheet. Everything below is
   specific to the product page and has no counterpart on the studio site.
   ========================================================================== */

/* --- Status badge -----------------------------------------------------------
   The store CTA before there is a store link. Deliberately NOT a .btn: it is
   not actionable, and a disabled-looking button invites clicks that do nothing.
   This reads as the inverted ::selection block the buttons are built from, but
   as a statement rather than a control.

   WHEN THE APP SHIPS: delete the badge from index.html and restore the
   <a class="btn btn--primary"> that sits commented out beside it. Nothing here
   needs to change. */
.badge {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-hi);
  border: 1px solid var(--line-hi);
  border-radius: 999px;
  padding: 0.5rem 1rem;
  white-space: nowrap;
}

/* --- Screenshot -------------------------------------------------------------
   The app window sits on the page rather than in a fake browser chrome or a
   tilted 3D mock. It is a real 1280x832 window; showing it at its own aspect
   ratio and letting it scroll horizontally on narrow screens is more honest
   than cropping it to something that fits.

   aspect-ratio holds the box open before the image decodes, so the hero does
   not jump when it lands. */
.shot {
  margin-top: clamp(2.5rem, 6vw, 4rem);
  position: relative;
}

.shot__frame {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg-raise);
  overflow: hidden;
  aspect-ratio: 1280 / 832;
  box-shadow: 0 24px 90px rgba(0, 0, 0, 0.85);
}

.shot__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
  display: block;
}

.shot__caption {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-align: center;
  margin: 1rem 0 0;
}

/* Shown until assets/img/underscore-ui.png exists. The <img> sits on top; if it
   fails to load, its alt text and this placeholder are what remain, so the page
   never renders a broken-image icon on a black field. */
.shot__frame--empty {
  display: grid;
  place-items: center;
  padding: 2rem;
  text-align: center;
  /* Loud on purpose. At --bg-raise on a black page under the vignette, an
     empty 1280x832 box is indistinguishable from a rendering failure — the
     first preview of this page read as a broken screenshot. A placeholder has
     to announce that it is one. */
  background: repeating-linear-gradient(
    45deg,
    rgba(255, 255, 255, 0.022) 0 14px,
    transparent 14px 28px
  ), var(--surface);
  border: 1px dashed var(--line-hi);
}
.shot__frame--empty p {
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  line-height: 1.9;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
  max-width: 44ch;
  margin: 0;
}
.shot__frame--empty code {
  color: var(--ink-hi);
  background: rgba(255, 255, 255, 0.08);
  padding: 0.15em 0.45em;
  border-radius: 4px;
}

/* --- Feature grid -----------------------------------------------------------
   Seven cards into a three-column grid leaves the seventh stranded beside two
   empty cells, so it spans the full row instead and reads as a deliberate
   closer. At two columns it spans two; at one it spans one. `1 / -1` does the
   right thing at every count without a media query per breakpoint. */
.card--wide { grid-column: 1 / -1; }

/* The wide card has room for two columns of prose at desktop width. */
@media (min-width: 900px) {
  .card--wide p { columns: 2; column-gap: 2.5rem; }
}

/* theunder.net sets this inline where a section head is followed by a table
   rather than by cards. A class instead, so the site can ship a Content
   Security Policy with no 'unsafe-inline' in style-src. */
.section__head--tight { margin-bottom: 1.75rem; }

/* --- The pitch -------------------------------------------------------------
   Replaces a whole "what it is" section with one line. Display type, but a step
   below .statement so the two do not compete — this one opens the page and the
   statement closes it. */
.pitch {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.25rem, 2.6vw, 1.75rem);
  line-height: 1.35;
  letter-spacing: -0.015em;
  color: var(--ink-hi);
  text-align: center;
  max-width: 34ch;
  margin: 0 auto;
  filter: blur(0.66px);
  text-shadow: 2px 0px 2px rgba(251, 12, 12, 0.26),
    -4px 0px 1px rgba(192, 243, 252, 0.15),
    0 0 18px rgba(255, 255, 255, 0.36),
    0 0 0.5px rgba(255, 255, 255, 0.6);
}

/* --- Feature bullets --------------------------------------------------------
   A plain list, not cards. The marker is an underscore rather than a disc: it
   is the product's own mark, and at this size it reads as a baseline rule
   leading into each line.

   The marker is generated content and therefore invisible to assistive tech,
   which is correct — the <ul><li> structure already announces the list, and a
   spoken "underscore" before every item would be noise. */
.bullets {
  list-style: none;
  padding: 0;
  margin: 0;
  max-width: 60ch;
}

.bullets li {
  position: relative;
  padding-left: 2.25rem;
  margin-bottom: 1.15rem;
  font-size: clamp(1.0625rem, 2vw, 1.25rem);
  line-height: 1.5;
  color: var(--ink);
}
.bullets li:last-child { margin-bottom: 0; }

.bullets li::before {
  content: "_";
  position: absolute;
  left: 0;
  top: 0;
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--ink-faint);
}

/* --- Requirements rows ------------------------------------------------------
   .rows from the studio site, but the labels are shorter here, so the label
   column is narrower than the 9-15rem the studio contact block wanted. */
.rows--spec .row { grid-template-columns: minmax(7rem, 11rem) 1fr; }
@media (max-width: 620px) {
  .rows--spec .row { grid-template-columns: 1fr; }
}

/* --- Legal page header ------------------------------------------------------
   theunder.net sets these inline on privacy.html/terms.html. Hoisted into the
   sheet here because three pages share them and inline style cannot be
   overridden from a stylesheet later. */
.legal-title {
  font-size: clamp(1.9rem, 4.5vw, 2.75rem);
  margin-bottom: 1rem;
}

.legal-date {
  color: var(--ink-faint);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  margin: 0;
}

/* The summary box at the top of the privacy policy. Same construction as
   .callout, sized up, because it is the only part most readers will read. */
.summary {
  border: 1px solid var(--line-hi);
  border-left: 2px solid var(--accent);
  background: var(--surface-hi);
  border-radius: 0 var(--radius) var(--radius) 0;
  padding: 1.5rem 1.6rem;
  margin-bottom: 2.5rem;
  max-width: 72ch;
}
.summary p { color: var(--ink); margin-bottom: 0.75rem; }
.summary p:last-child { margin-bottom: 0; }
.summary strong { color: var(--ink-hi); }

/* --- Hero, product variant --------------------------------------------------
   The studio hero centres a headline over two buttons. This one carries a
   wordmark, a headline, a byline and a status badge, so the vertical rhythm is
   tighter and the actions row needs to survive wrapping onto three lines on a
   narrow phone. */
.hero--product h1 {
  font-size: clamp(1.75rem, 4.6vw, 2.5rem);
  max-width: 26ch;
}

.hero--product .eyebrow { margin: 0 0 2.25rem; }

.hero__actions { row-gap: 1.15rem; }

/* --- Manual pointer ---------------------------------------------------------
   The manual is a separate site on its own subdomain. This makes that a
   destination rather than a footnote. */
.cta-band .badge { margin-top: 0.5rem; }
