/* NovaFusion Media — the site's stylesheet, built on the NovaFusion
   design system (tokens v3). Shared by every page; it was inline in
   index.html until there was a second page to keep in step with it. */
/* ===================================================================
   NovaFusion Media — built on the NovaFusion design system (tokens v3).
   White paper, black bands, neutral hairlines, one red-to-orange flame.
   Everything is square: the system has no radii but chips and the REC
   pill, and no shadow token at all -- separation is by hairline and
   surface step. Token names below are the system's own.
   =================================================================== */
:root {
  --paper: #ffffff;
  --paper-raised: #ffffff;
  --bone: #f4f4f4;
  --line: #e2e2e2;
  --line-strong: #8a8a8a;
  --ink: #0d0d0d;
  --ink-muted: #5c5c5c;

  --flame-0: #9e0a1c; --flame-1: #c8121f; --flame-2: #e5231b; --flame-3: #ff2d00;
  --flame-4: #ff5a1a; --flame-5: #ff7a33; --flame-6: #ffa04f;

  --signal: var(--flame-4);
  --on-signal: #ffffff;
  --signal-deep: #c41f12;
  /* Red-orange as TEXT, the same sweep as the buttons but stopped short of
     flame-5: the hero word is large text, where 3:1 is the bar, and flame-5
     on paper is only 2.60:1. flame-4 holds 3.12:1. */
  --flame-text: linear-gradient(100deg, var(--flame-1) 0%, var(--flame-3) 55%, var(--flame-4) 100%);
  /* Small accents on paper, warmer than the crimson signal-deep while still
     clearing 4.5:1 for small text. Anything oranger fails: flame-3 is 3.73:1. */
  --accent-text: var(--flame-2);
  --signal-tint: #ffe4d9;

  --band: #0d0d0d;
  --on-band: #ffffff;
  --on-band-muted: #a3a3a3;
  --on-band-accent: #ff5a2a;

  --rec: #d7262e;
  --focus: var(--signal-deep);

  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-6: 24px; --space-8: 32px; --space-12: 48px; --space-16: 64px;
  --space-24: 96px;

  --radius-0: 0px; --radius-xs: 2px; --radius-pill: 999px;
  --stroke-hairline: 1px; --stroke-mark: 2px;
  /* The header is fixed, so it covers the top of whatever is under it. The
     hero has to pad past it rather than under it. */
  --header-h: 76px;

  --font-sans: "Instrument Sans", "Helvetica Neue", Arial, sans-serif;
  --font-serif: "Instrument Serif", Georgia, serif;
  --font-mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
}

/* The flame, exactly as the system's Flame component defines it. Two
   recipes: --flame is the decorative field (one large surface per page,
   never behind text); --flame-ui is scarlet-to-amber for the primary CTA,
   step index blocks and the featured tier. The grain is fractal noise at
   low alpha, which breaks up 8-bit banding so the field reads as lit
   material rather than a flat CSS gradient. */
:root {
  --flame-grain: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .22 0'/></filter><rect width='100%' height='100%' filter='url(%23g)'/></svg>");
  --flame-linear: linear-gradient(118deg, var(--flame-0) 0%, var(--flame-1) 13%, var(--flame-2) 28%, var(--flame-3) 44%, var(--flame-4) 61%, var(--flame-5) 79%, var(--flame-6) 100%);
  --flame-ui-linear: linear-gradient(100deg, var(--flame-3) 0%, var(--flame-4) 38%, var(--flame-5) 74%, var(--flame-6) 100%);
}
@supports (background: linear-gradient(in oklch, red, red)) {
  :root {
    --flame-linear: linear-gradient(118deg in oklch, var(--flame-0) 0%, var(--flame-1) 13%, var(--flame-2) 28%, var(--flame-3) 44%, var(--flame-4) 61%, var(--flame-5) 79%, var(--flame-6) 100%);
    --flame-ui-linear: linear-gradient(100deg in oklch, var(--flame-3) 0%, var(--flame-4) 38%, var(--flame-5) 74%, var(--flame-6) 100%);
  }
}
:root {
  --flame: var(--flame-grain),
    radial-gradient(85% 110% at 100% 0%, rgb(255 184 112 / .55) 0%, rgb(255 184 112 / .18) 32%, rgb(255 184 112 / 0) 58%),
    radial-gradient(70% 95% at 0% 100%, rgb(90 0 12 / .55) 0%, rgb(90 0 12 / .2) 34%, rgb(90 0 12 / 0) 62%),
    var(--flame-linear);
  --flame-ui: var(--flame-grain), radial-gradient(60% 140% at 100% 0%, rgb(255 184 112 / .35) 0%, rgb(255 184 112 / 0) 60%), var(--flame-ui-linear);
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
  background: var(--paper); color: var(--ink);
  font-family: var(--font-sans); font-size: 15px; line-height: 24px;
  -webkit-font-smoothing: antialiased;
}
a { color: inherit; text-decoration: none; }
section { position: relative; }
img { max-width: 100%; }

h1, h2, h3 { font-weight: 500; letter-spacing: -0.025em; line-height: 1.04; }
h2 { font-size: 48px; }
h3 { font-size: 21px; line-height: 1.25; font-weight: 600; letter-spacing: -0.01em; }

/* One italic idea per headline: the serif accent carries the emotional half
   of a line of sans. Never a whole headline, never body copy. */
.script, .morph-word, .morph-sizer, .morph-first {
  font-family: var(--font-serif); font-style: italic; font-weight: 400;
  letter-spacing: 0; color: inherit;
}

:focus-visible { outline: var(--stroke-mark) solid var(--focus); outline-offset: 2px; }

/* The frame: a centred 1200px column, gutter space-16 desktop / space-4
   mobile, as the system's layout rules set out. */
.wrap { max-width: 1200px; margin: 0 auto; padding: 0 var(--space-16); }

/* The machine layer. Every section opens with [ 03 / 05 ] HOW IT WORKS:
   the index and its label are separate elements in the markup, so both go
   inline and read as one line. */
.eyebrow, .sec-index {
  display: inline; font-family: var(--font-mono); font-size: 11px;
  line-height: 16px; font-weight: 500; letter-spacing: 0.1em;
  text-transform: uppercase;
}
.sec-index { color: var(--accent-text); margin-right: var(--space-3); font-variant-numeric: tabular-nums; }
.eyebrow { color: var(--ink-muted); }
.muted { color: var(--ink-muted); }
.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;
}

/* Band sections flip the page to black. The system names the portfolio,
   final CTA and footer; the client-logo strip joins them because every
   uploaded logo is white on transparent and would be invisible on paper. */
.trusted, .portfolio, .book-cta, footer {
  background: var(--band); color: var(--on-band);
}
.trusted .muted, .portfolio .muted, .book-cta .muted, footer .muted { color: var(--on-band-muted); }
.trusted .eyebrow, .portfolio .eyebrow, .book-cta .eyebrow, footer .eyebrow { color: var(--on-band-muted); }
.trusted .sec-index, .portfolio .sec-index, .book-cta .sec-index, footer .sec-index { color: var(--on-band-accent); }

/* ===== BUTTONS ===== */
.btn, .nav-cta {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2); padding: var(--space-3) var(--space-4);
  border: var(--stroke-hairline) solid transparent; border-radius: var(--radius-0);
  font-family: var(--font-mono); font-size: 12px; line-height: 16px;
  font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  cursor: pointer; white-space: nowrap;
  transition: background-color .16s ease-out, background-position .4s ease-out, transform .16s ease-out, border-color .16s ease-out;
}
/* The label's arrow is the hover, not a lift: the system moves it 4px right
   and presses the button 1px down. No glow, no shadow. */
.btn::after, .nav-cta::after { content: "→"; display: inline-block; transition: transform .2s ease-out; }
.btn.glass::after { content: "↗"; }
.btn:active, .nav-cta:active { transform: translateY(1px); }

.btn.solid, .nav-cta {
  background-color: var(--signal); background-image: var(--flame-ui);
  background-size: 100% 100%, 180% 100%, 180% 100%;
  background-position: 0 0, 0% 50%, 0% 50%;
  color: var(--on-signal);
}
.btn.glass {
  background: transparent; color: var(--ink);
  border-color: var(--line-strong);
}
.portfolio .btn.glass, .book-cta .btn.glass, footer .btn.glass {
  color: var(--on-band); border-color: var(--on-band-muted);
}

@media (hover: hover) and (pointer: fine) {
  .btn:hover::after, .nav-cta:hover::after { transform: translateX(4px); }
  .btn.solid:hover, .nav-cta:hover { background-position: 0 0, 100% 50%, 100% 50%; }
  .btn.glass:hover { background: var(--bone); }
  .portfolio .btn.glass:hover, .book-cta .btn.glass:hover { background: rgb(128 128 128 / .14); }
  .vid-card:hover, .design-card:hover, .test-card:hover, .step:hover { background: var(--bone); }
  .client-logo:hover { opacity: 1; }
  .scroll-row:hover .scroll-track, .test-scroll:hover .test-track { animation-play-state: paused; }
  .nav-links a:hover { color: var(--ink); }
  .faq summary:hover { background: var(--bone); }
}

/* ===== HEADER ===== */
/* Glass, so the photograph behind it blurs through rather than being hidden
   by a bar. Over the sky it reads as frosted; once the page has scrolled
   past the hero it is frosted white over white, which is just the header. */
header {
  position: fixed; top: 0; left: 0; right: 0; z-index: 50;
  background: rgb(255 255 255 / .62);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  backdrop-filter: blur(20px) saturate(150%);
  border-bottom: var(--stroke-hairline) solid rgb(255 255 255 / .45);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  header { background: var(--paper); border-bottom-color: var(--line); }
}
.scroll-bar { position: absolute; left: 0; bottom: -1px; height: var(--stroke-mark); width: 0%; background-image: var(--flame-ui-linear); z-index: 51; }
.nav { display: flex; align-items: center; justify-content: space-between; gap: var(--space-6); padding: var(--space-4) var(--space-16); max-width: 1200px; margin: 0 auto; }
/* The mark is drawn in cream and orange, so it only works on black. On paper
   the system calls for the name set as a wordmark until a dark-ink mark
   exists, which is what .logo-mark is. */
.logo { display: flex; align-items: center; gap: var(--space-2); }
.logo img { display: none; }
.logo-mark { font-size: 17px; font-weight: 600; letter-spacing: -0.02em; color: var(--ink); white-space: nowrap; }
footer .logo img { display: block; height: 32px; width: auto; }
footer .logo .logo-mark { display: none; }
.beta-badge {
  font-family: var(--font-mono); font-size: 11px; line-height: 16px; font-weight: 500;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-text);
  border: var(--stroke-hairline) solid var(--line-strong);
  border-radius: var(--radius-xs); padding: 1px var(--space-2); white-space: nowrap;
}
footer .beta-badge { color: var(--on-band-accent); border-color: var(--on-band-muted); }
.nav-links { display: flex; align-items: center; gap: var(--space-6); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-muted); }
.nav-actions { display: flex; align-items: center; gap: var(--space-3); }

/* ===== HERO ===== */
.hero {
  position: relative; overflow: hidden;
  /* A fixed height, not a share of the viewport: the copy sits at the foot of
     the hero, so a viewport-relative height made the gap under the header
     change with the window. At 780 that gap is about 90px and stays there. */
  min-height: 780px;
  display: flex; align-items: flex-end;
  padding: calc(var(--header-h) + var(--space-8)) 0 var(--space-16);
  /* -1.5 EV. A true 1.5-stop pull is x0.3536 in LINEAR light, but CSS
     composites in sRGB, where that works out at about x0.59 across the
     tonal range -- so it is a flat black multiply at alpha 0.41. Done in
     the background shorthand rather than as a filter, so it stays part of
     the hero's own background, which is what the glass above it blurs.
     Measured bands: sky 152->94, mountains 124->75, forest 55->31. */
  background-image:
    linear-gradient(rgb(0 0 0 / .41), rgb(0 0 0 / .41)),
    url('hero-bg.webp');
  background-size: cover; background-position: center 42%;
  background-color: var(--band);
  /* No bottom border. It is a light grey hairline, and what follows the hero
     on both pages is a black band, so it read as a white line across the foot
     of the picture. The seam is already drawn by the next section's own
     border-top, which is set to a colour that suits a dark ground. */
}
/* The glass. backdrop-filter applies evenly across whatever box it is on, so
   the gradient is in the mask, not the filter: the blur is painted over the
   lower half and then masked out towards the top, which is what makes it
   rise out of the photograph instead of sitting on it as a band. The tint
   rides along to carry the text, since the forest below is only 55/255. */
.hero::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  -webkit-backdrop-filter: blur(9px) saturate(112%);
  backdrop-filter: blur(9px) saturate(112%);
  -webkit-mask-image: linear-gradient(to top, #000 0%, #000 42%, rgb(0 0 0 / .4) 74%, transparent 96%);
  mask-image: linear-gradient(to top, #000 0%, #000 42%, rgb(0 0 0 / .4) 74%, transparent 96%);
  /* Lighter again: the exposure pull already darkens the picture, so the
     veil is here for the glass and a little lift under the copy, not to
     carry the text on its own. */
  background: linear-gradient(to top, rgb(13 13 13 / .52) 0%, rgb(13 13 13 / .40) 42%, rgb(13 13 13 / .26) 72%, rgb(13 13 13 / .06) 90%, transparent 100%);
}
.glow, .cursor-glow { display: none; }   /* the system has no glows */
.hero-inner { text-align: left; position: relative; z-index: 1; width: 100%; }
.hero h1 {
  font-size: 112px; line-height: 0.94; letter-spacing: -0.045em;
  max-width: 14ch; margin-bottom: var(--space-8); color: var(--on-band);
  text-shadow: 0 1px 2px rgb(0 0 0 / .45), 0 6px 28px rgb(0 0 0 / .5);
}
.hero p.sub {
  font-size: 18px; line-height: 28px; color: #e8e8e8;
  max-width: 56ch; margin-bottom: var(--space-8);
  text-shadow: 0 1px 2px rgb(0 0 0 / .5), 0 2px 16px rgb(0 0 0 / .45);
}
.hero .btn.glass {
  color: var(--on-band); border-color: rgb(255 255 255 / .5);
  background: rgb(255 255 255 / .1);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
@media (hover: hover) and (pointer: fine) {
  .hero .btn.glass:hover { background: rgb(255 255 255 / .2); }
}
.hero-cta { display: flex; gap: var(--space-3); flex-wrap: wrap; margin-bottom: var(--space-12); }
.tag-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.tag-row span {
  font-family: var(--font-mono); font-size: 11px; line-height: 16px; font-weight: 500;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--on-band);
  border: var(--stroke-hairline) solid rgb(255 255 255 / .45);
  background: rgb(255 255 255 / .1);
  -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
  border-radius: var(--radius-xs); padding: 3px var(--space-2);
}

/* The cycling word is the headline's one italic idea. It cuts between words
   rather than sliding: the system's motion is editorial, cuts not bounces. */
.morph { position: relative; display: inline-grid; vertical-align: baseline; }
.morph-sizer { visibility: hidden; white-space: nowrap; grid-area: 1 / 1; }
/* The cycling words are drawn from their data-w attribute rather than held as
   text, so the headline a crawler reads -- served or rendered -- is one
   sentence. Generated content still takes part in layout, which is what the
   sizer measures. */
.morph-word::before, .morph-sizer::before { content: attr(data-w); }
/* The one word the markup ships with. It holds the headline together before the
   script runs, and is all that is left in the accessibility tree after: the
   script swaps this class for .sr-only once the stack exists. */
.morph-first { grid-area: 1 / 1; white-space: nowrap; color: var(--on-band); }
.morph-stack { grid-area: 1 / 1; position: relative; }
.morph-word {
  position: absolute; left: 0; top: 0; white-space: nowrap;
  /* Plain white, like the rest of the headline. Orange could not hold here:
     the word crosses the snow peak, where measured against it every stop of
     the flame lands at 1.7:1 or under. The shadow is inherited from the h1,
     so the italic word and the roman line around it carry the same weight. */
  color: var(--on-band);
  opacity: 0; transform: translateY(8px); transition: none;
}
.morph-word.is-active {
  opacity: 1; transform: none;
  transition: opacity .18s ease-out, transform .18s ease-out;
}

/* The liquid metal button. The frame is 4.49x the button's height, because
   the shader pads its stage to clear the bloom, so it overhangs the button
   on every side. It is transparent and pointer-events:none, and the real <a>
   sits on top of it keeping its href, focus ring and label -- the shader
   never becomes the control. */
.lm-cta { position: relative; display: inline-block; vertical-align: middle; }
/* Hidden until the shader is actually up. The frame goes into the document
   before its content is fetched, and an iframe with no srcdoc yet is
   about:blank -- which paints white. Since the frame is 4.49x the button's
   height, that showed as a white box around it for as long as the fetch took.
   background:transparent on the element does not help: the white is the inner
   document's own canvas, so the element has to be transparent instead.

   Tying it to is-lit, which is also what takes the glass off, makes the two a
   single cross-fade: the painted button leaves as the metal arrives. */
.lm-cta__frame {
  position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  border: 0; display: block; background: transparent;
  pointer-events: none; z-index: 0;
  opacity: 0; transition: opacity .28s ease-out;
}
.lm-cta.is-lit > .lm-cta__frame { opacity: 1; }
.lm-cta > .lm-cta__link { position: relative; z-index: 1; }
/* The painted chrome comes off only while a frame is actually live, so a
   button whose shader never loads keeps the glass styling it had.

   Longhands, not the `background` shorthand: written as `background: none`
   this only ever took the image off and left the hero's translucent fill in
   place, so the glass button stayed painted and the metal plate arrived
   behind it -- two buttons stacked, which is what looked like one spawning.

   The handover is faded rather than cut, because the frame cannot be ready on
   the same tick the page paints: it has a fetch and a shader compile ahead of
   it, so something has to be in that space and it may as well arrive softly. */
.lm-cta > .lm-cta__link {
  transition: background-color .28s ease-out, border-color .28s ease-out;
}
.lm-cta.is-lit > .lm-cta__link {
  background-color: transparent !important;
  background-image: none !important;
  border-color: transparent !important;
  -webkit-backdrop-filter: none !important;
  backdrop-filter: none !important;
  color: var(--on-band);
}

/* ===== TRUSTED BY (band) ===== */
.trusted { padding: var(--space-12) 0; }
.trusted .label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--on-band-muted); margin-bottom: var(--space-6); }
.logo-row { display: flex; align-items: center; gap: var(--space-12); flex-wrap: wrap; }
.client-logo { height: 36px; width: 128px; object-fit: contain; opacity: .78; transition: opacity .16s ease-out; }

/* ===== PORTFOLIO (band) ===== */
.portfolio { padding: var(--space-24) 0; }
.portfolio-head, .how-head, .pricing-head, .testimonials-head, .faq-head { margin-bottom: var(--space-12); }
.portfolio-head h2, .book-cta h2 { color: var(--on-band); margin-top: var(--space-6); }
/* The rows carry themselves. The edge mask fades cards in and out at the
   frame's edge rather than letting them clip against a hard line. */
.scroll-row {
  display: flex; gap: var(--space-4); overflow: hidden; padding: 2px 0;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.scroll-track {
  display: flex; gap: var(--space-4); width: max-content;
  animation: scrollRight 32s linear infinite;
}
.scroll-track.left { animation-name: scrollLeft; }
/* The track holds the cards twice, so a 50% shift lands on an identical
   frame and the loop has no seam. */
@keyframes scrollRight { from { transform: translateX(-50%); } to { transform: translateX(0); } }
@keyframes scrollLeft  { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* Frames, not cards: square corners, a hairline, nothing else around them. */
/* Sized per format rather than one width for both. At a single 300px width
   a 9:16 reel stands 533px tall and swamps the row, while a 16:9 clip is
   only 169px. These widths put the two formats on a comparable height, with
   the reels the shorter of the two. */
.vid-card {
  flex: 0 0 168px; position: relative; overflow: hidden;
  border: var(--stroke-hairline) solid var(--on-band-muted);
  background: #000; transition: background-color .16s ease-out;
}
.vid-card video, .vid-card img { display: block; width: 100%; height: 100%; object-fit: cover; }
.play { display: none; }   /* a timecode chip does this job in the system */
.ph { aspect-ratio: 9 / 16; background-color: #1c1c1c; background-image: repeating-linear-gradient(to bottom, rgb(255 255 255 / .08) 0 1px, transparent 1px 4px); }
.vid-card.wide { flex: 0 0 440px; }
.vid-card.wide .ph { aspect-ratio: 16 / 9; }
/* Long- and short-form read at a glance, the way the system labels frames.
   The markup already separates them with .wide, so no data attribute is
   needed. */
.vid-card::after {
  position: absolute; top: var(--space-2); right: var(--space-2);
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.08em;
  color: var(--on-band); background: rgb(13 13 13 / .72);
  border-radius: var(--radius-xs); padding: 2px 6px; pointer-events: none;
}
.vid-card::after { content: "9:16"; }
.vid-card.wide::after { content: "16:9"; }
.progress-rail { height: var(--stroke-hairline); background: var(--on-band-muted); margin-top: var(--space-6); }
.progress-rail-fill { height: 100%; background-image: var(--flame-ui-linear); width: 0; }
.rail-dot { display: none; }

/* ===== DESIGN PORTFOLIO (paper) ===== */
.design-portfolio { padding: var(--space-24) 0; border-bottom: var(--stroke-hairline) solid var(--line); }
.design-card {
  flex: 0 0 300px; overflow: hidden;
  border: var(--stroke-hairline) solid var(--line);
  background: var(--paper-raised); transition: background-color .16s ease-out;
}
.design-card img { display: block; width: 100%; height: auto; }

/* ===== HOW IT WORKS (paper) ===== */
.how { padding: var(--space-24) 0; border-bottom: var(--stroke-hairline) solid var(--line); }
.steps { display: grid; grid-template-columns: repeat(4, 1fr); border-top: var(--stroke-hairline) solid var(--line); border-left: var(--stroke-hairline) solid var(--line); }
.step {
  padding: var(--space-8); background: var(--paper-raised);
  border-right: var(--stroke-hairline) solid var(--line);
  border-bottom: var(--stroke-hairline) solid var(--line);
  display: flex; flex-direction: column; gap: var(--space-3);
  transition: background-color .16s ease-out;
}
/* The index is a flame block, one of the system's three uses of --flame-ui. */
.step .num {
  font-family: var(--font-mono); font-size: 13px; line-height: 16px; font-weight: 600;
  display: inline-grid; place-items: center; min-width: 28px; height: 28px;
  background-color: var(--signal); background-image: var(--flame-ui);
  color: var(--on-signal); margin-bottom: var(--space-2);
}
.step p { color: var(--ink-muted); }

/* ===== PRICING (paper) ===== */
.pricing { padding: var(--space-24) 0; border-bottom: var(--stroke-hairline) solid var(--line); }
.billing-toggle { display: inline-flex; border: var(--stroke-hairline) solid var(--line-strong); margin-bottom: var(--space-8); }
.billing-toggle button {
  font-family: var(--font-mono); font-size: 12px; font-weight: 600; letter-spacing: 0.06em;
  text-transform: uppercase; padding: var(--space-3) var(--space-4);
  background: transparent; color: var(--ink-muted); border: 0; cursor: pointer;
}
.billing-toggle button.active { background: var(--ink); color: var(--paper); }
/* Seven named rows the cards share, so the price, the blurb and the first
   ✓ all sit on the same line across every tier. The cards do not hold the
   same children -- AI has no add-on tag and uses a config block where the
   others have a list, and Growth's blurb is a line shorter -- so each child
   is placed in its row explicitly rather than auto-flowing, which would
   shift a whole card up when one row is missing. */
.price-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-3);
  align-items: stretch;
  grid-template-rows: auto auto auto auto auto 1fr auto;
}
@supports (grid-template-rows: subgrid) {
  .price-grid > .price-card {
    display: grid; grid-template-rows: subgrid; grid-row: span 7;
    gap: var(--space-3);
  }
  .price-grid > .price-card .addon-tag { grid-row: 1; }
  .price-grid > .price-card h3 { grid-row: 2; }
  .price-grid > .price-card .amount { grid-row: 3; }
  .price-grid > .price-card .amount-sub { grid-row: 4; }
  .price-grid > .price-card .desc { grid-row: 5; }
  .price-grid > .price-card ul,
  .price-grid > .price-card .ai-config { grid-row: 6; }
  .price-grid > .price-card .btn { grid-row: 7; align-self: end; }
}
.price-card {
  display: flex; flex-direction: column; gap: var(--space-4);
  padding: var(--space-8); background: var(--paper-raised);
  border: var(--stroke-hairline) solid var(--line); position: relative;
}
/* The featured tier takes a flame border, drawn as two backgrounds so the
   gradient can live in the border box without a wrapper element. */
.price-card.featured {
  border: var(--stroke-mark) solid transparent;
  background: linear-gradient(var(--paper-raised), var(--paper-raised)) padding-box, var(--flame-ui-linear) border-box;
}
.price-card .badge {
  position: absolute; top: -1px; right: -1px;
  background-color: var(--signal); background-image: var(--flame-ui); color: var(--on-signal);
  font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; padding: 4px var(--space-2);
}
.price-card h3 { margin: 0; }
.price-card .desc { color: var(--ink-muted); font-size: 15px; }
.amount, .amount-value { font-size: 32px; line-height: 1.1; font-weight: 500; letter-spacing: -0.02em; }
.amount-sub, .amount small { font-family: var(--font-mono); font-size: 12px; color: var(--ink-muted); letter-spacing: 0; }
.price-card ul { list-style: none; margin: 0; padding: var(--space-4) 0 0; border-top: var(--stroke-hairline) solid var(--line); display: grid; gap: var(--space-2); flex: 1; }
.price-card li { display: flex; gap: var(--space-2); font-size: 15px; }
/* Success is ink plus a red-orange check and a word. There is no green. */
.price-card li::before { content: "✓"; font-family: var(--font-mono); color: var(--accent-text); }
.price-card .btn { margin-top: var(--space-4); }
.ai-config { display: grid; gap: var(--space-3); flex: 1; }
.ai-field { display: grid; gap: var(--space-1); }
.ai-field label, .addon-label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-muted); }
.ai-field select, .qual-input {
  font-family: var(--font-sans); font-size: 15px; color: var(--ink);
  background: var(--paper-raised); border: var(--stroke-hairline) solid var(--line-strong);
  border-radius: var(--radius-0); padding: var(--space-3); width: 100%;
}
.qual-input { background: transparent; color: var(--on-band); border-color: var(--on-band-muted); }
.qual-input::placeholder { color: var(--on-band-muted); }
/* Two full cards, so they get the grid's gap and stretch to a common
   height instead of being centred against each other at uneven widths. */
.addon-row {
  display: grid; grid-template-columns: 2fr 1fr; gap: var(--space-4);
  align-items: stretch; margin-top: var(--space-4);
}
.strategy-card { display: flex; flex-direction: column; gap: var(--space-3); }
/* The price sits on the headline's line, hard right, so the two read as one
   row rather than the price trailing the list at the foot of the card. */
.strategy-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-6); }
.strategy-head h3 { margin: 0; }
.strategy-head .amount { white-space: nowrap; text-align: right; }
.strategy-copy { display: grid; gap: var(--space-2); }
/* No margin-top:auto: the button follows the list instead of being pushed to
   the bottom of whatever height the neighbouring card sets. */
.strategy-action { margin-top: var(--space-2); display: grid; gap: var(--space-3); justify-items: start; }
.strategy-card.added { border-color: var(--accent-text); }
.addon-tag { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--accent-text); }
.helper-card { padding: var(--space-8); background: var(--bone); border: var(--stroke-hairline) solid var(--line); display: flex; flex-direction: column; gap: var(--space-4); align-items: flex-start; justify-content: center; }

/* The whole pricing section has to fit one screen. It is sticky, so anything
   below the fold is covered by the next section before it can be scrolled to,
   which is why the add-on and helper cards could not be reached at all. Every
   number below is here to buy vertical room. */
.pricing { padding: var(--space-8) 0; }
.pricing .pricing-head { margin-bottom: var(--space-6); }
.pricing .billing-toggle { margin-bottom: var(--space-6); }
.price-grid > .price-card { padding: var(--space-6); gap: var(--space-2); }
.price-card .desc { font-size: 14px; line-height: 21px; }
.price-card ul { padding-top: var(--space-2); gap: 3px; }
.price-card li { font-size: 13px; line-height: 19px; }
.price-card .btn { margin-top: var(--space-3); }
.addon-row { margin-top: var(--space-3); }
.strategy-card, .helper-card { padding: var(--space-4) var(--space-6); }
.strategy-card { gap: var(--space-2); }
.strategy-list li { font-size: 14px; line-height: 20px; }
.strategy-action { margin-top: var(--space-1); }

/* ===== TESTIMONIALS (paper) ===== */
.testimonials { padding: var(--space-24) 0; border-bottom: var(--stroke-hairline) solid var(--line); overflow: hidden; }
.test-scroll {
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.test-track {
  display: flex; gap: var(--space-4); width: max-content;
  animation: testScroll 38s linear infinite;
}
@keyframes testScroll { from { transform: translateX(0); } to { transform: translateX(-50%); } }
.test-card {
  flex: 0 0 360px; padding: var(--space-8); background: var(--paper-raised);
  border: var(--stroke-hairline) solid var(--line);
  display: flex; flex-direction: column; gap: var(--space-4);
  transition: background-color .16s ease-out;
}
.test-who { display: flex; align-items: center; gap: var(--space-3); }
.avatar { width: 36px; height: 36px; object-fit: cover; border: var(--stroke-hairline) solid var(--line); }
.test-who .name { font-weight: 600; }
.test-who .role { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-muted); }

/* ===== FAQ (paper) ===== */
/* No horizontal padding of its own and no centring on the grid: the section
   now uses the same .wrap as every other one, which is what puts its heading
   on the same line as the rest. It used to sit 120px to their left, and the
   grid 64px, because each was aligning itself a different way. */
.faq { padding: var(--space-24) 0; border-bottom: var(--stroke-hairline) solid var(--line); }
.faq-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 var(--space-12); }
.faq-col { display: flex; flex-direction: column; }
.faq details { border-bottom: var(--stroke-hairline) solid var(--line); }
.faq summary { list-style: none; cursor: pointer; display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); padding: var(--space-4) 0; font-size: 17px; font-weight: 600; letter-spacing: -0.01em; transition: background-color .16s ease-out; }
.faq summary::-webkit-details-marker { display: none; }
.faq-icon { font-family: var(--font-mono); font-size: 18px; color: var(--accent-text); flex: none; line-height: 1.2; }
.faq p { color: var(--ink-muted); padding-bottom: var(--space-4); max-width: 56ch; }

/* ===== BOOK CALL (band) ===== */
.book-cta { padding: var(--space-24) 0; }
.book-cta h2 { font-size: 72px; line-height: 0.98; letter-spacing: -0.035em; margin-bottom: var(--space-6); }
.book-cta p { color: var(--on-band-muted); max-width: 56ch; margin-bottom: var(--space-8); }

/* ===== FOOTER (band) ===== */
footer { padding: var(--space-12) 0; border-top: var(--stroke-hairline) solid rgb(255 255 255 / .14); }
.footer-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-6); flex-wrap: wrap; }
.footer-links { display: flex; gap: var(--space-6); font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--on-band-muted); }

/* ===== QUALIFICATION FLOW ===== */
/* The form sits inside the band, so a transparent ground drops it straight
   onto black: every control below is set for that, not for paper. */
.qual { background: transparent; border: var(--stroke-hairline) solid var(--on-band-muted); padding: var(--space-8); }
/* One step on screen at a time: the flow is six questions, and without this
   every one of them renders at once as a single long list. */
.qual-step { display: none; }
.qual-step.active { display: block; }
.qual-progress { height: var(--stroke-hairline); background: rgb(255 255 255 / .22); margin-bottom: var(--space-6); }
.qual-progress > div { height: 100%; background-image: var(--flame-ui-linear); }
.qual-count { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--on-band-muted); margin-bottom: var(--space-2); }
.qual-q { font-size: 21px; font-weight: 600; letter-spacing: -0.01em; margin-bottom: var(--space-6); color: var(--on-band); }
.qual-options { display: grid; gap: var(--space-2); }
.qual-option {
  text-align: left; font-family: var(--font-sans); font-size: 15px; color: var(--on-band);
  background: transparent; border: var(--stroke-hairline) solid var(--on-band-muted);
  border-radius: var(--radius-0); padding: var(--space-3) var(--space-4); cursor: pointer;
  transition: background-color .16s ease-out, border-color .16s ease-out;
}
.qual-option:hover { background: rgb(255 255 255 / .08); }
/* A tint of the flame rather than signal-tint, which is a near-white panel
   made for paper and would read as a lit block on the band. */
.qual-option.active { background: rgb(255 90 42 / .16); border-color: var(--on-band-accent); }
.qual-fields { display: grid; gap: var(--space-4); }
.qual-field { display: grid; gap: var(--space-1); }
.qual-field label { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--on-band-muted); }
.qual-nav { display: flex; gap: var(--space-3); margin-top: var(--space-6); }
.qual-back { background: transparent; border: var(--stroke-hairline) solid var(--on-band-muted); color: var(--on-band); font-family: var(--font-mono); font-size: 12px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; padding: var(--space-3) var(--space-4); cursor: pointer; }
/* rec as the dark theme sets it: #d7262e is only 3.0:1 on the band. */
.qual-error { color: #ff5c5c; font-size: 13px; margin-top: var(--space-2); }
.qual-alt { font-size: 13px; color: var(--on-band-muted); margin-top: var(--space-4); }
.qual-done { text-align: center; padding: var(--space-8) 0; }
.qual-tick { font-family: var(--font-mono); font-size: 32px; color: var(--on-band-accent); }
.qual-done h3 { color: var(--on-band); }
.qual-done p { color: var(--on-band-muted); }
.qual-trap { position: absolute; left: -9999px; }
.strategy-card { padding: var(--space-8); background: var(--bone); border: var(--stroke-hairline) solid var(--line); }
.strategy-copy { color: var(--ink-muted); }
/* Six items over three columns is two rows, not three -- the section has to
   fit one screen, and this is the cheapest row to lose. */
.strategy-list { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-1) var(--space-6); margin: 0; }
.strategy-list li::before { content: "✓"; font-family: var(--font-mono); color: var(--accent-text); margin-right: var(--space-2); }

.mobile-menu-btn { display: none; background: transparent; border: var(--stroke-hairline) solid var(--line-strong); color: var(--ink); font-size: 18px; padding: 6px 10px; cursor: pointer; }
.mobile-nav-panel { display: none; }

/* ===================================================================
   Responsive. These sit at the end of the sheet on purpose: a media
   query adds no specificity, so they only beat the base rules above by
   coming after them.
   =================================================================== */
@media (max-width: 1024px) {
  .wrap, .nav { padding-left: var(--space-8); padding-right: var(--space-8); }
  .hero h1 { font-size: 72px; line-height: 0.98; letter-spacing: -0.035em; }
  .price-grid { grid-template-columns: repeat(2, 1fr); }
  .addon-row { grid-template-columns: 1fr; }
  .steps { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 760px) {
  .wrap, .nav { padding-left: var(--space-4); padding-right: var(--space-4); }
  .nav-links { display: none; }
  .logo-mark { font-size: 15px; }
  .mobile-menu-btn { display: block; }
  .hero {
    /* The copy sits at the foot of the hero, so on a short phone it fills the
       box and the top padding is the only thing between it and the header.
       At 48px against a 71px header the headline went behind it; clearing the
       header's own height is what fixes it at every screen height. */
    min-height: 86vh; padding: calc(var(--header-h) + var(--space-6)) 0 var(--space-8);
    background-image:
      linear-gradient(rgb(0 0 0 / .41), rgb(0 0 0 / .41)),
      url('hero-bg-small.webp');
  }
  .hero h1 { font-size: 44px; line-height: 1.02; letter-spacing: -0.03em; max-width: none; }
  .hero p.sub { font-size: 16px; line-height: 26px; }
  .book-cta h2 { font-size: 40px; }
  h2 { font-size: 32px; }
  .trusted, .portfolio, .design-portfolio, .how, .pricing, .testimonials, .faq, .book-cta { padding-top: var(--space-12); padding-bottom: var(--space-12); }
  /* Stacked, so there is nothing left to align across and the shared rows
     would only strand each card's button. Back to a plain column. */
  .price-grid, .steps { grid-template-columns: 1fr; }
  .price-grid { grid-template-rows: none; }
  .price-grid > .price-card { display: flex; flex-direction: column; grid-row: auto; }
  .addon-row { grid-template-columns: 1fr; }
  .strategy-list { grid-template-columns: 1fr; }
  .faq-grid { grid-template-columns: 1fr; gap: 0; }
  .step, .price-card, .test-card, .helper-card, .qual { padding: var(--space-6); }
  .vid-card { flex-basis: 132px; }
  .vid-card.wide { flex-basis: 280px; }
  .design-card { flex-basis: 240px; }
  .test-card { flex-basis: 280px; }
  .logo-row { gap: var(--space-8); justify-content: center; }
  .footer-row { flex-direction: column; align-items: flex-start; }
  /* 16px or larger, or iOS Safari zooms the page when the field is focused. */
  .qual-input, .ai-field select, .qual-option { font-size: 16px; }
}
/* ===================================================================
   Stacked scroll. Every section sticks to the top of the viewport and
   the next one rides up over it, while the fixed header stays put
   above the whole stack.

   Two things this needs. Each section must have an opaque ground, or
   the one it is covering shows straight through -- the paper sections
   were transparent and inherited the body's white. And the footer has
   to be positioned: sticky sections paint in the positioned layer, so
   a static footer would slide under them instead of over.
   =================================================================== */
.hero, .trusted, .portfolio, .design-portfolio, .how,
.testimonials, .faq, .book-cta {
  position: sticky; top: 0;
}
/* Pricing is the one section taller than its own content budget, and pinning
   something taller than the viewport makes the overflow unreachable rather
   than merely below the fold -- that is what hid the add-on and helper cards.
   So it only pins where it fits whole; on a shorter screen it scrolls like an
   ordinary section and everything can still be reached. */
@media (min-height: 980px) {
  .pricing { position: sticky; top: 0; }
}
.design-portfolio, .how, .pricing, .testimonials, .faq {
  background: var(--paper);
}
/* The seam you see as a section arrives. */
.trusted, .portfolio, .design-portfolio, .how, .pricing,
.testimonials, .faq, .book-cta {
  border-top: var(--stroke-hairline) solid var(--line);
}
.portfolio, .book-cta, .trusted { border-top-color: rgb(255 255 255 / .18); }
footer { position: relative; z-index: 1; }

/* Phones get no stacking. A pinned section taller than the window puts its
   overflow out of reach -- the next section covers it before it can be
   scrolled to -- and on a phone almost every section is taller than the
   window: How it works measures 972px and the FAQ 1031px against about 836px.
   This sits after the sticky rules above because a media query adds no
   specificity; placed earlier it would simply lose to them. */
@media (max-width: 760px) {
  .hero, .trusted, .portfolio, .design-portfolio, .how,
  .pricing, .testimonials, .faq, .book-cta { position: static; }

  /* The nav ran past the right edge and cut the menu button off: wordmark
     plus badge plus a full-size CTA came to 408px inside 386px. */
  /* A little shorter once the badge is hidden below. */
  :root { --header-h: 72px; }
  .nav { gap: var(--space-3); }
  header .beta-badge { display: none; }
  .logo-mark { font-size: 15px; }
  .nav-cta { padding: 10px 12px; font-size: 11px; letter-spacing: 0.04em; }
  .nav-cta::after { display: none; }

  /* The button toggles .open, and nothing was listening for it, so the menu
     could never appear. */
  .mobile-nav-panel.open {
    display: grid;
    padding: var(--space-2) var(--space-4) var(--space-4);
    background: var(--paper);
    border-top: var(--stroke-hairline) solid var(--line);
  }
  .mobile-nav-panel.open a {
    font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--ink);
    padding: var(--space-3) 0;
    border-bottom: var(--stroke-hairline) solid var(--line);
  }
  .mobile-nav-panel.open a:last-child { border-bottom: 0; }
}

/* On a touchscreen the rows are swiped, not animated. The marquee has no
   manual equivalent, so a visitor who tried to drag it got nothing back and
   the row read as frozen. Native horizontal scrolling replaces it, and the
   browser handles the momentum. The duplicate cards exist only to make the
   marquee loop seamlessly, so they are dropped here rather than showing
   every clip twice.

   This sits at the end of the sheet on purpose: a media query adds no
   specificity, so it only beats .scroll-row's overflow and the track's
   animation above by coming after them. */
@media (hover: none), (pointer: coarse) {
  .scroll-row, .test-scroll {
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* the edge fade hides content that cannot be scrolled past by hand */
    -webkit-mask-image: none; mask-image: none;
  }
  .scroll-row::-webkit-scrollbar, .test-scroll::-webkit-scrollbar { display: none; }
  /* padding gives the first and last card room to clear the screen edge */
  .scroll-track, .test-track { animation: none; width: max-content; padding: 0 var(--space-4); }
  .vid-card, .design-card, .test-card { scroll-snap-align: center; }
  .vid-card[data-clone], .design-card[data-clone], .test-card[data-clone] { display: none; }
}

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