:root {
  /* GENERATED from the palette in packages/shared/src/brand.ts — the app and this
     page must never carry two copies of a hex. Regenerate after changing a colour:
     node apps/web/scripts/gen-brand-css.mjs
     apps/web/src/brand-css.test.ts fails if these lines drift from the palette. */
  /* @brand-colors:start */
  --bg: #080B0F;
  --ink: #05080C;
  --surface: #111820;
  --chat-panel: #0B1017;
  --card: #202A35;
  --card-deep: #0E141C;
  --line: #2E3946;
  --line-hi: #3B4757;
  --text: #F5F7FA;
  --muted: #A8B3C2;
  --muted-dim: #7C8794;
  --red: #D71920;
  --red-hi: #ED2A33;
  --red-text: #F04448;
  --cyan: #31A7FF;
  --amber: #FFB000;
  --on-red: #FFFFFF;

  /* The same colours as channel triplets, so rgba() can take an alpha without re-typing the hex. */
  --rgb-bg: 8, 11, 15;
  --rgb-ink: 5, 8, 12;
  --rgb-surface: 17, 24, 32;
  --rgb-chat-panel: 11, 16, 23;
  --rgb-card: 32, 42, 53;
  --rgb-card-deep: 14, 20, 28;
  --rgb-line: 46, 57, 70;
  --rgb-line-hi: 59, 71, 87;
  --rgb-text: 245, 247, 250;
  --rgb-muted: 168, 179, 194;
  --rgb-muted-dim: 124, 135, 148;
  --rgb-red: 215, 25, 32;
  --rgb-red-hi: 237, 42, 51;
  --rgb-red-text: 240, 68, 72;
  --rgb-cyan: 49, 167, 255;
  --rgb-amber: 255, 176, 0;
  --rgb-on-red: 255, 255, 255;
  /* @brand-colors:end */

  /* GENERATED from the scale in packages/shared/src/brand-scale.ts. These are not new
     numbers: they are the values this page already used, gathered into a set. The
     stylesheet had 11 distinct border-radius values and 38 distinct sub-2rem spacing
     values, none of them chosen together — each arrived with one component, on one
     afternoon, because .62rem looked right in that one card. A step here is the obvious
     thing for the next component to reach for, so the thirty-ninth padding value never
     gets invented. Every step carries a note in brand-scale.ts saying what it is FOR;
     read those before picking one. Regenerate after changing a step:
     node apps/web/scripts/gen-brand-css.mjs

     Eight steps, because the page genuinely uses that range: --space-1 is the hairline
     inset on the mono pills and badges in the scroll story, and --space-8 is the margin
     that sets a grid off from the heading above it. A scale that cannot express the range
     the page actually works in is not a scale, it is a rounding error waiting to happen.
     2rem and up is deliberately absent: that is section-level vertical rhythm, a separate
     decision. Its own marker pair rather than a tail on @brand-colors, because the two
     regions come from different sources and because a generator rewrites everything
     between a marker pair — one shared region would have swallowed this comment.
     apps/web/src/design-scale.test.ts fails if these lines drift from brand-scale.ts, or
     if a raw value creeps back in anywhere else in the file. */
  /* @brand-scale:start */
  --space-1: .15rem;
  --space-2: .3rem;
  --space-3: .5rem;
  --space-4: .75rem;
  --space-5: 1rem;
  --space-6: 1.25rem;
  --space-7: 1.5rem;
  --space-8: 1.75rem;

  --radius-1: 6px;
  --radius-2: 8px;
  --radius-3: 12px;
  --radius-4: 16px;
  --radius-5: 20px;
  --radius-pill: 999px;
  /* @brand-scale:end */

  /* Depth, in three sizes. Not colours, so they are not generated — but they are themed:
     a shadow is dark-on-dark here and has to become a soft grey lift on a light ground,
     or every card on the light page looks like it is floating in a hole. */
  --shadow-soft: 0 12px 26px rgba(0, 0, 0, .45);
  --shadow-card: 0 18px 40px rgba(0, 0, 0, .5);
  --shadow-float: 0 40px 90px rgba(0, 0, 0, .6);
  --mono: ui-monospace, "SF Mono", Menlo, monospace;
  /* Type roles. Barlow is the DIN-lineage grotesque the whole product now runs on;
     the condensed cut carries display headings, where its narrow caps read as
     motorsport rather than as generic software. Self-hosted — see /fonts/barlow.css. */
  --font-ui: Barlow, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-display: "Barlow Condensed", "Arial Narrow", var(--font-ui);
  font-family: var(--font-ui);
  color: var(--text);
  background: var(--bg);
}

/* The light theme.
   ----------------
   Every property below already exists in :root above; this only re-points it. That is the
   whole return on Days 2-4: because the stylesheet says --card and --muted rather than the
   greys those names used to stand for, re-pointing sixteen names re-paints the page, and
   the 1500 lines of rules underneath do not change at all. Both blocks are written from packages/shared by
   the same function walking the same list, so neither can end up carrying a property the
   other is missing — which is the failure that matters here, since a name defined in dark
   and absent in light does not error, it just leaves one control dark on a white page.

   Opt-in on purpose. This is scoped to [data-theme="light"] and NOT to prefers-color-scheme,
   so it paints only when something sets the attribute. Wiring it to the OS preference would
   hand roughly half of all visitors a light marketing site, and whether a brand this dark by
   nature wants that is a decision for a person to make, not a side effect of the tokens
   existing. Turning it on later is one media query.

   To look at it:  document.documentElement.dataset.theme = 'light'  */
:root[data-theme="light"] {
  /* @brand-colors-light:start */
  --bg: #F4F6F9;
  --ink: #E6EBF1;
  --surface: #FFFFFF;
  --chat-panel: #FAFBFD;
  --card: #FFFFFF;
  --card-deep: #EDF1F6;
  --line: #D5DBE3;
  --line-hi: #B4BECB;
  --text: #0C1218;
  --muted: #48576A;
  --muted-dim: #6E7C8D;
  --red: #B3141A;
  --red-hi: #8E1015;
  --red-text: #B3141A;
  --cyan: #0F6FC4;
  --amber: #9A6800;
  --on-red: #FFFFFF;

  /* The same colours as channel triplets, so rgba() can take an alpha without re-typing the hex. */
  --rgb-bg: 244, 246, 249;
  --rgb-ink: 230, 235, 241;
  --rgb-surface: 255, 255, 255;
  --rgb-chat-panel: 250, 251, 253;
  --rgb-card: 255, 255, 255;
  --rgb-card-deep: 237, 241, 246;
  --rgb-line: 213, 219, 227;
  --rgb-line-hi: 180, 190, 203;
  --rgb-text: 12, 18, 24;
  --rgb-muted: 72, 87, 106;
  --rgb-muted-dim: 110, 124, 141;
  --rgb-red: 179, 20, 26;
  --rgb-red-hi: 142, 16, 21;
  --rgb-red-text: 179, 20, 26;
  --rgb-cyan: 15, 111, 196;
  --rgb-amber: 154, 104, 0;
  --rgb-on-red: 255, 255, 255;
  /* @brand-colors-light:end */

  --shadow-soft: 0 6px 18px rgba(12, 18, 24, .08);
  --shadow-card: 0 1px 3px rgba(12, 18, 24, .08), 0 10px 28px rgba(12, 18, 24, .07);
  --shadow-float: 0 20px 48px rgba(12, 18, 24, .14);
  color: var(--text);
  background: var(--bg);
}

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

html {
  scroll-behavior: smooth;
}

body {
  margin: 0;
  color: var(--text);
  background: var(--bg);
  font: 1rem/1.65 var(--font-ui);
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

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

a {
  color: inherit;
  text-decoration: none;
}

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

ul,
ol {
  margin: 0;
  padding: 0;
}

main > section:not(.hero):not(.trust-strip),
.site-footer {
  padding: clamp(3rem, 8vw, 5.5rem) var(--space-6);
}

main > section:not(.hero):not(.trust-strip) > *,
.trust-strip,
.site-footer {
  max-width: 1080px;
  margin-inline: auto;
}

/* Display headings run in the condensed cut. Condensed sets narrower at the same
   point size, so the clamps step up and the line-heights tighten to keep the
   optical weight the old system font had. */
h1 {
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 5.8vw, 4rem);
  line-height: 1.02;
  font-weight: 700;
  letter-spacing: -.005em;
}

h2 {
  max-width: 760px;
  margin-inline: auto;
  font-family: var(--font-display);
  font-size: clamp(1.9rem, 3.5vw, 2.6rem);
  line-height: 1.08;
  font-weight: 700;
  letter-spacing: -.005em;
  text-align: center;
}

h3 {
  font-size: 1.15rem;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: 0;
}

.eyebrow {
  color: var(--red-text);
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  padding: .8em 1.4em;
  border: 1px solid transparent;
  border-radius: var(--radius-2);
  font-weight: 700;
  line-height: 1.1;
  text-align: center;
  transition: background-color .18s ease, border-color .18s ease, color .18s ease, transform .18s ease;
}

.btn:hover {
  transform: translateY(-1px);
}

.btn-primary {
  background: var(--red);
  color: var(--on-red);
}

.btn-primary:hover {
  background: var(--red-hi);
}

.btn-secondary {
  color: var(--text);
  background: transparent;
  border-color: rgba(var(--rgb-text), .28);
}

.btn-secondary:hover {
  background: rgba(var(--rgb-text), .08);
  border-color: rgba(var(--rgb-text), .5);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
summary:focus-visible {
  outline: 3px solid rgba(var(--rgb-red), .7);
  outline-offset: 3px;
}

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  min-height: 4.25rem;
  padding: var(--space-4) var(--space-6);
  background: rgba(var(--rgb-bg), .82);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(14px);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-4);
}

.brand img {
  height: 30px;
  width: auto;
}

/* "Open beta" chip. Sits beside the logo in the header and beside the pricing
   headline, so the promise on every page is the same one: this is an early
   product we are still fixing. Muted on purpose — red is reserved for CTAs. */
.beta-badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  color: var(--muted);
  font-size: .68rem;
  font-weight: 800;
  letter-spacing: .1em;
  text-transform: uppercase;
  white-space: nowrap;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--muted);
  font-size: .95rem;
  font-weight: 600;
}

.site-nav > a:not(.btn) {
  display: none;
  padding: var(--space-4) var(--space-3);
  transition: color .18s ease;
}

.site-nav > a:not(.btn):hover {
  color: var(--text);
}

.site-nav .btn {
  min-height: 2.45rem;
  padding: .68em 1em;
  white-space: nowrap;
}

.hero {
  display: grid;
  justify-items: center;
  gap: var(--space-6);
  padding: clamp(4.75rem, 11vw, 7.5rem) var(--space-6) clamp(3rem, 8vw, 5.75rem);
  position: relative;
  overflow: hidden;
  isolation: isolate;
  color: var(--text);
  text-align: center;
  background: linear-gradient(180deg, var(--card-deep) 0%, var(--bg) 60%, var(--ink) 100%);
}

/* Ambient glows live on drifting layers so the hero breathes instead of sitting
   static. They slowly wander + swell; killed under reduced-motion (below).
   z-index 1 sits them ABOVE the template canvas (z 0) so they read as light
   falling across the real paint template, and below the content (z 2). */
.hero::before,
.hero::after {
  content: "";
  position: absolute;
  z-index: 1;
  border-radius: 50%;
  filter: blur(20px);
  pointer-events: none;
}

.hero::before {
  width: 48rem;
  height: 48rem;
  top: -16rem;
  left: -10rem;
  background: radial-gradient(circle, rgba(var(--rgb-red), .30), transparent 60%);
  animation: hero-drift-a 26s ease-in-out infinite alternate;
}

.hero::after {
  width: 42rem;
  height: 42rem;
  top: -12rem;
  right: -8rem;
  background: radial-gradient(circle, rgba(var(--rgb-cyan), .20), transparent 60%);
  animation: hero-drift-b 34s ease-in-out infinite alternate;
}

@keyframes hero-drift-a {
  from { transform: translate3d(0, 0, 0) scale(1); }
  to   { transform: translate3d(3.5rem, 2.5rem, 0) scale(1.14); }
}

@keyframes hero-drift-b {
  from { transform: translate3d(0, 0, 0) scale(1.06); }
  to   { transform: translate3d(-3rem, 2rem, 0) scale(.9); }
}

/* Template canvas — the hero opens on the REAL paint template (the blank UV
   sheet), ghosted + lit so it reads as a technical backdrop under the headline.
   Deliberately faint + the BLANK template only: the template→painted reveal is
   the pinned scroll story just below, so this sets the stage without spoiling
   it. Sits at z 0 (under the glows + content); it's the last child of .hero
   so the content load-in stagger (nth-child 1..6) is unaffected. */
.hero > .hero-canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: auto;
  overflow: hidden;
  pointer-events: none;
  animation: none;
  /* The #000 in a mask is not a colour, it is an alpha channel written in hex: black means
     keep this pixel, transparent means drop it. Theming it would delete the fade. */
  -webkit-mask-image: radial-gradient(135% 100% at 50% 34%, #000 34%, transparent 82%);
          mask-image: radial-gradient(135% 100% at 50% 34%, #000 34%, transparent 82%);
}

.hero-canvas-img {
  position: absolute;
  inset: -12%;
  background: url("paint/template-blank.webp") center / cover no-repeat;
  filter: saturate(.44) brightness(.48) contrast(1.03);
  opacity: .6;
  transform: scale(1.06);
  animation: hero-canvas-drift 52s ease-in-out infinite alternate;
  will-change: transform;
}

@keyframes hero-canvas-drift {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to   { transform: scale(1.16) translate3d(-2.5%, -2%, 0); }
}

/* Darkening veil keeps the headline crisp over the busy template. */
.hero-canvas::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(78% 62% at 50% 40%, rgba(var(--rgb-ink), .86), rgba(var(--rgb-ink), .46) 62%, rgba(var(--rgb-ink), .9)),
    linear-gradient(180deg, rgba(var(--rgb-ink), .55), rgba(var(--rgb-ink), .2) 30%, rgba(var(--rgb-ink), .88));
}

/* Soft cyan sweep — a slow "scan" across the template, reading as Ailivery
   reading the real template. Very low contrast; killed under reduced-motion. */
.hero-canvas::before {
  content: "";
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: -30%;
  width: 42%;
  z-index: 2;
  /* The pale blue at the centre of the sweep is light, not paint: it is the hot core of a
     scanning beam passing over the car, and it stays that colour on any ground. Deliberately
     not a token — tokens are for surfaces and text, and this is neither. */
  background: linear-gradient(90deg, transparent, rgba(var(--rgb-cyan), .1) 46%, rgba(150, 210, 255, .17) 50%, rgba(var(--rgb-cyan), .1) 54%, transparent);
  transform: skewX(-14deg);
  filter: blur(6px);
  animation: hero-scan 13s ease-in-out infinite;
}

@keyframes hero-scan {
  0%   { transform: translateX(0) skewX(-14deg); opacity: 0; }
  9%   { opacity: 1; }
  30%  { opacity: 1; }
  44%  { transform: translateX(340%) skewX(-14deg); opacity: 0; }
  100% { transform: translateX(340%) skewX(-14deg); opacity: 0; }
}

/* Content sits above the glows + template and fades up on load, top-to-bottom. */
.hero > * {
  position: relative;
  z-index: 2;
  width: min(100%, 1080px);
  animation: hero-in .7s cubic-bezier(.2, .7, .2, 1) both;
}

.hero > *:nth-child(1) { animation-delay: .05s; }
.hero > *:nth-child(2) { animation-delay: .13s; }
.hero > *:nth-child(3) { animation-delay: .21s; }
.hero > *:nth-child(4) { animation-delay: .29s; }
.hero > *:nth-child(5) { animation-delay: .37s; }
.hero > *:nth-child(6) { animation-delay: .45s; }
.hero > *:nth-child(7) { animation-delay: .53s; }
.hero > *:nth-child(8) { animation-delay: .61s; }
.hero > *:nth-child(9) { animation-delay: .69s; }

@keyframes hero-in {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

.hero h1 {
  max-width: 820px;
}

.hero .subhead {
  max-width: 680px;
  color: rgba(var(--rgb-text), .78);
  font-size: clamp(1.05rem, 2vw, 1.22rem);
  line-height: 1.65;
}

/* Sim names sit above the CTA so a visitor qualifies themselves before they
   click. They were previously only in the FAQ and footer, which is late for the
   single question every sim racer arrives with. */
.hero-sims {
  max-width: 640px;
  color: var(--muted);
  font-family: var(--mono);
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .14em;
  line-height: 1.7;
  text-transform: uppercase;
}

/* Each sim name lights up under the cursor. The size change is a transform, not a
   font-size change, so a name growing never re-wraps the strip — it runs onto two
   lines and a reflow would shuffle every name after the one being hovered.
   inline-block is what makes transform apply at all on inline text; it also keeps
   two-word names like ASSETTO CORSA from breaking across the line.
   Cyan rather than red: red is the button colour on this screen, and these names
   are not clickable, so a red hover would read as a link that goes nowhere. */
.hero-sim-name {
  display: inline-block;
  transition: color .22s ease, text-shadow .22s ease, transform .22s cubic-bezier(.2, .8, .3, 1.4);
}

@media (hover: hover) {
  .hero-sim-name:hover {
    color: var(--text);
    text-shadow: 0 0 10px rgba(var(--rgb-cyan), .6), 0 0 24px rgba(var(--rgb-cyan), .3);
    transform: scale(1.14);
  }
}

.hero-cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-1);
}

/* The free start, right under each big sign-up button. The price cards left this page on
   2026-10-04, so this line tells the visitor what the free account gives before they sign up.
   In the hero row it takes a line of its own under the button. */
.free-note {
  flex-basis: 100%;
  color: var(--muted);
  font-size: .9rem;
  line-height: 1.45;
  text-wrap: balance;
}

.cta-mid .free-note {
  margin-top: var(--space-5);
}

.hero-stat {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-5);
  color: var(--muted);
  font-size: .95rem;
  font-weight: 700;
}

/* The count arrives from /api/public/stats, which under a slow connection answers
   seconds after first paint. Taking the line out of the flow means the reveal
   grows the hero, and the hero sizes .hero-canvas -> .hero-canvas-img, a
   full-bleed layer whose reflow was the landing's entire layout shift
   (measured CLS 0.064, all of it this one element). So keep the box, hide the
   ink: the reveal now changes nothing about the layout. */
.hero-stat[hidden] {
  visibility: hidden;
}

.hero-stat-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--red);
  animation: hero-stat-pulse 1.6s ease-in-out infinite;
}

.hero-stat-count strong {
  color: var(--text);
  font-weight: 800;
  /* Reserve the digits as well as the line. Without a width the empty strong
     wraps the sentence differently from the filled one, so the number landing
     would still move the hero by a line. 4.5ch holds a four-digit count with
     its thousands comma. */
  display: inline-block;
  min-width: 4.5ch;
}

.hero-stat-join {
  color: var(--cyan);
}

@keyframes hero-stat-pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: .3; transform: scale(.78); }
}

/* Hero proof strip — four real liveries sit directly under the CTA so the first
   screen shows the product, not only a claim about it. Deliberately small: the
   full wall is section 03, this is a thumbnail of the promise. Unlike
   .hero-stat (which stays hidden until the live count loads) this always
   renders, so the hero never ships with zero proof. */
.hero-proof {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.hero-proof-strip {
  display: flex;
  justify-content: center;
  gap: var(--space-3);
}

/* Square: the source art is a 640×640 template sheet, so a 16:9 crop slices a
   meaningless band out of the middle. Full square reads as a whole livery. */
.hero-proof-strip img {
  width: clamp(70px, 20vw, 104px);
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  border-radius: var(--radius-2);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-soft);
}

.hero-proof-cap {
  color: var(--muted);
  font-family: var(--mono);
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
}

/* Scroll cue — anchors the empty lower hero and invites the (long) scroll story. */
.hero-scrollcue {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  margin-top: clamp(1.5rem, 4vh, 3rem);
  color: var(--muted);
  text-decoration: none;
  font-family: var(--mono);
  font-size: .64rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  transition: color .2s ease;
}

.hero-scrollcue:hover,
.hero-scrollcue:focus-visible {
  color: var(--text);
}

.hero-scrollcue-chev {
  width: 1.1rem;
  height: 1.1rem;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  animation: hero-bob 1.8s ease-in-out infinite;
}

@keyframes hero-bob {
  0%, 100% { transform: rotate(45deg) translate(0, 0); opacity: .5; }
  50% { transform: rotate(45deg) translate(2px, 2px); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-stat-dot,
  .hero::before,
  .hero::after,
  .hero > *,
  .hero-canvas-img,
  .hero-canvas::before,
  .hero-scrollcue-chev { animation: none; }
  .hero > * { opacity: 1; transform: none; }
  /* Keep the glow — it is a colour change, not motion — but drop the grow and the ease. */
  .hero-sim-name { transition: color .22s ease, text-shadow .22s ease; }
  .hero-sim-name:hover { transform: none; }
}

.hero-media {
  margin-top: var(--space-6);
  padding: var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-4);
  background: rgba(var(--rgb-card), .35);
}

/* A real Studio screenshot under the proof strip, so a visitor sees the actual app before signing up.
   width/height on the <img> reserve its box; height:auto here keeps the 1080x633 ratio at every width.
   The radius also hides the rounded window corners left in the capture. */
.hero-shot {
  display: block;
  width: 100%;
  height: auto;
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-card);
}

.hero-video {
  width: 100%;
  /* Neutralize the height="1080" presentational hint from the <video> tag.
     Without this the element keeps a definite 1080px height, which makes the
     aspect-ratio rule below inert and renders the video at full intrinsic
     height (overflowing the phone screen). height:auto lets aspect-ratio
     drive the box on both phone (4/3) and desktop (16/9). */
  height: auto;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-3);
  background: var(--surface);
  object-fit: cover;
}

.trust-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-5) var(--space-6);
  color: var(--muted);
  font-size: .9rem;
  font-weight: 600;
}

.trust-strip span,
.trust-strip a {
  display: inline-flex;
  align-items: center;
  min-height: 2.2rem;
  padding: var(--space-3) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--card-deep);
}

.trust-strip a {
  transition: border-color .18s ease, color .18s ease, transform .18s ease;
}

.trust-strip a:hover {
  color: var(--text);
  border-color: var(--red);
  transform: translateY(-1px);
}

/* When the supported-sims strip is followed by the feature strip, keep them snug. */
.trust-strip + .trust-strip {
  padding-top: 0;
}

.value-props,
.examples,
.pricing,
.get-started,
.faq {
  background: var(--bg);
}

/* The limits band. Same furniture as the FAQ, one shade lighter, because the two
   sit next to each other and a shared background would read as one long list. */
.limits {
  background: var(--surface);
}

.how-it-works,
.features {
  background: var(--surface);
}

.card-grid,
.gallery,
.steps {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-5);
  margin-top: var(--space-8);
  list-style: none;
}

.gallery {
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--space-6);
}

.card,
.steps li,
.feature-row,
.faq details {
  background: var(--card-deep);
  border: 1px solid var(--line);
  border-radius: var(--radius-3);
  box-shadow: var(--shadow-card);
  transition: border-color .18s ease, transform .18s ease, box-shadow .18s ease;
}

.card,
.steps li {
  padding: var(--space-7);
}

.card:hover,
.steps li:hover,
.feature-row:hover {
  border-color: var(--line-hi);
  transform: translateY(-3px);
  box-shadow: var(--shadow-float);
}

.card p,
.steps p,
.feature-row p {
  margin-top: var(--space-3);
  color: var(--muted);
}

.steps {
  counter-reset: steps;
}

.steps li {
  display: grid;
  gap: var(--space-4);
}

.step-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border-radius: var(--radius-pill);
  color: var(--on-red);
  background: var(--red);
  font-weight: 700;
}

.gallery img {
  width: 100%;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: var(--radius-2);
  background: var(--card);
  border: 1px solid var(--line);
}

.features {
  position: relative;
  overflow: hidden;
}

.features::before {
  content: "";
  position: absolute;
  inset: 0;
  background: url("/landing/ailivery-hero-car-blended.webp") center / cover no-repeat;
  opacity: .14;
  pointer-events: none;
}

.features::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(var(--rgb-surface), .58) 0%, rgba(var(--rgb-surface), .86) 100%);
  pointer-events: none;
}

.features > * {
  position: relative;
  z-index: 1;
}

.features h2 {
  margin-bottom: var(--space-8);
}

.feature-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
}

.feature-row {
  padding: var(--space-6) var(--space-7);
}

.pricing {
  text-align: center;
}

.pricing-sub {
  max-width: 560px;
  margin: 0 auto;
  color: var(--muted);
}

.pricing-sub strong {
  color: var(--text);
}

/* Payment-provider changeover: prices stay up, the purchase action is temporarily withheld. */
.pricing-paused {
  max-width: 560px;
  margin: var(--space-5) auto 0;
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius-2);
  background: rgba(var(--rgb-text), .03);
  color: var(--muted);
  font-size: 0.92rem;
}

.pricing-paused strong {
  color: var(--text);
}

/* Free + three paid cards: one column on phones, two by two on tablets, one row of four on desktop.
   Fixed steps instead of auto-fit, so the row never breaks as three cards and one lonely card. */
.price-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  max-width: 900px;
  margin: var(--space-8) auto 0;
}

.price-card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-7) var(--space-7);
  border: 1px solid var(--line);
  border-radius: var(--radius-4);
  background: var(--card-deep);
  box-shadow: var(--shadow-card);
}

.price-card--featured {
  border: 1px solid var(--red);
  box-shadow: 0 26px 64px rgba(var(--rgb-red), .22), var(--shadow-card);
}

.price-tag {
  position: absolute;
  top: -.7rem;
  left: 50%;
  transform: translateX(-50%);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-1);
  background: var(--red);
  color: var(--on-red);
  font-family: var(--mono);
  font-size: .58rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .16em;
  white-space: nowrap;
  box-shadow: 0 8px 20px rgba(var(--rgb-red), .35);
}

.price-name {
  font-weight: 700;
  font-size: 1.05rem;
}

.price-amount {
  font-family: var(--font-display);
  font-size: 2.5rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* "USD / month" on its own line: "$19.99 USD / month" is wider than a card in the row of four. */
.price-amount small {
  display: block;
  margin-top: var(--space-1);
  font-size: .5em;
}

/* The buyer's unit is a finished livery, not a credit. This is the headline
   number under the price; the credit/render maths drops to a muted footnote
   (.price-credits) so nobody has to convert three units in their head. */
.price-liveries {
  color: var(--muted);
  font-size: .95rem;
  line-height: 1.35;
}

.price-liveries strong {
  display: block;
  color: var(--text);
  font-size: 1.08rem;
  font-weight: 700;
}

.price-credits {
  color: var(--muted);
  font-size: .82rem;
  line-height: 1.4;
}

.price-badge {
  display: inline-flex;
  align-items: center;
  min-height: 1.9rem;
  margin: var(--space-1) 0 var(--space-3);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-pill);
  color: var(--on-red);
  background: var(--red);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .02em;
}

.price-feats {
  width: 100%;
  margin: var(--space-2) 0 var(--space-5);
  padding: var(--space-5) 0 0;
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  text-align: left;
}

.price-feats li {
  position: relative;
  list-style: none;
  padding-left: var(--space-7);
  color: var(--muted);
  font-size: .85rem;
  line-height: 1.4;
}

.price-feats li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: -.02rem;
  color: var(--cyan);
  font-weight: 800;
}

.price-feats strong {
  color: var(--text);
  font-weight: 700;
}

.price-card .btn {
  width: 100%;
  margin-top: auto;
}

@media (min-width: 640px) {
  .price-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 1000px) {
  .price-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* Hold: the reviews hold the scroll for a moment, like the Studio story above them, so they do not
   fly past. Nothing inside moves or hides; every review stays on screen.
   The held block's children get the width the section gives its own children, because they used to be them. */
.hang-stick > * {
  max-width: 1080px;
  margin-inline: auto;
}

/* Only on a big screen, and never with reduced motion. The block sticks below the site header and
   sits in the middle of the screen. The spacer after the block is the hold: the block stays put
   while the page scrolls past it. */
@media (min-width: 1000px) and (min-height: 700px) and (prefers-reduced-motion: no-preference) {
  .hang::after {
    content: "";
    display: block;
    height: 120vh;
  }

  .hang-stick {
    position: sticky;
    top: 0;
    box-sizing: border-box;
    min-height: 100vh;
    padding-top: 5rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

/* Shared inclusions block. Spelling out everything a pack carries makes the
   work visible at the price — the cards alone read thinner than the product
   actually is. Shared rather than repeated per card so the grid stays short
   on phones. */
/* No max-width here on purpose: the section-child rule near the top of this
   file already caps every direct child at 1080px, which is what .price-grid
   actually resolves to. Setting 900px here would leave this box narrower than
   the cards it belongs to. */
.price-included {
  margin: var(--space-7) auto 0;
  padding: var(--space-7) var(--space-7) var(--space-6);
  border: 1px solid var(--line);
  border-radius: var(--radius-4);
  background: var(--card-deep);
  box-shadow: var(--shadow-card);
  text-align: left;
}

.price-included-h {
  margin-bottom: var(--space-5);
  color: var(--text);
  font-family: var(--mono);
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .18em;
  text-transform: uppercase;
  text-align: center;
}

.price-included ul {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: var(--space-3) var(--space-7);
}

.price-included li {
  position: relative;
  list-style: none;
  padding-left: var(--space-7);
  color: var(--muted);
  font-size: .88rem;
  line-height: 1.45;
}

.price-included li::before {
  content: "✓";
  position: absolute;
  left: 0;
  top: -.02rem;
  color: var(--cyan);
  font-weight: 800;
}

.price-included-note {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: .85rem;
  text-align: center;
}

/* Plans only: the facts a buyer asks before paying (currency, when the credits
   arrive, what cancelling keeps, what does not carry over). A readable box of
   its own, not a muted footnote. One column, so each fact reads as a line. */
.price-facts {
  margin-top: var(--space-6);
  padding: var(--space-5) var(--space-6);
  border: 1px solid var(--line-hi);
  border-radius: var(--radius-3);
  background: var(--card);
  text-align: left;
}

.price-facts h3 {
  margin-bottom: var(--space-4);
  color: var(--text);
  font-size: 1rem;
  font-weight: 700;
}

.price-included .price-facts ul {
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

.price-included .price-facts li {
  color: var(--text);
  font-size: .95rem;
}

/* "Cancel any time" stands out wherever the landing page says it. */
.cancel-any-time {
  color: var(--text);
  font-size: 1.15em;
  font-weight: 800;
}

.price-facts-costs {
  margin-top: var(--space-4);
  color: var(--muted);
  font-size: .88rem;
}

.faq-more {
  margin: var(--space-7) auto 0;
  text-align: center;
}

.faq-more a {
  color: var(--cyan);
  font-weight: 600;
  text-decoration: none;
}

.faq-more a:hover {
  text-decoration: underline;
}

/* Inline prose link. The base rule makes every `a` inherit its colour with no
   underline — right for nav and cards, wrong for a link inside a sentence,
   which then reads as plain text. Anything a reader must notice mid-paragraph
   carries this class. */
.txt-link {
  color: var(--cyan);
  font-weight: 600;
}

.txt-link:hover {
  text-decoration: underline;
}

.get-started {
  text-align: center;
}

.get-started > p {
  max-width: 600px;
  margin: var(--space-4) auto 0;
  color: var(--muted);
}

.get-started-cta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-7);
}

/* Reviews: real words from the support chat, kept exactly as written (typos too), and only with
   the writer's yes. A note under the name says when the writer has a tie to us, such as a partner. */
.reviews {
  text-align: center;
  background: var(--bg);
}

.reviews .eyebrow {
  margin-bottom: var(--space-4);
}

.review-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-6);
  margin: var(--space-8) auto 0;
}

/* Four reviews sit two by two once two cards fit side by side, so no card sits alone on a row. */
@media (min-width: 720px) {
  .review-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

.review {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--space-7);
  margin: 0;
  padding: var(--space-8) var(--space-7);
  border: 1px solid var(--line);
  border-radius: var(--radius-4);
  background: var(--card-deep);
  box-shadow: var(--shadow-card);
  text-align: left;
}

.review blockquote {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.4rem, 2.4vw, 1.7rem);
  line-height: 1.15;
  font-weight: 600;
}

.review figcaption {
  display: grid;
  gap: var(--space-1);
  color: var(--muted);
  font-size: .9rem;
}

.review figcaption strong {
  color: var(--text);
  font-weight: 700;
}

.review-note {
  color: var(--muted-dim);
  font-size: .82rem;
}

.reviews-source {
  margin: var(--space-7) auto 0;
  color: var(--muted-dim);
  font-size: .85rem;
}

/* Conversion CTA right after the wall-of-liveries climax — one big button so
   high-intent users act before the page settles into detail. Red-glow band +
   top/bottom rules punctuate it against its neighbours. */
.cta-mid {
  text-align: center;
  background:
    radial-gradient(circle at 50% -20%, rgba(var(--rgb-red), .28), transparent 24rem),
    var(--surface);
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.cta-mid h2 {
  max-width: 20ch;
}

/* Loss line: names what standing still costs, which lands harder than another
   restatement of the gain. */
.cta-mid-lead {
  max-width: 46ch;
  margin: var(--space-5) auto 0;
  color: var(--muted);
  font-size: clamp(1rem, 1.6vw, 1.12rem);
  line-height: 1.55;
}

/* The big button: larger than the site default, with a red glow that lifts on
   hover. box-shadow is in its own transition since the base .btn omits it. */
.cta-mid-btn {
  margin-top: var(--space-8);
  padding: 1.05em 2.5em;
  font-size: 1.15rem;
  border-radius: var(--radius-3);
  box-shadow: 0 18px 44px rgba(var(--rgb-red), .35);
  transition: background-color .18s ease, transform .18s ease, box-shadow .18s ease;
}

.cta-mid-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 22px 54px rgba(var(--rgb-red), .45);
}

.faq-list {
  display: grid;
  gap: var(--space-4);
  max-width: 820px;
  margin: var(--space-8) auto 0;
}

.faq details {
  padding: var(--space-5) var(--space-6);
  text-align: left;
}

.faq summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  cursor: pointer;
  font-weight: 600;
  list-style: none;
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  flex: 0 0 auto;
  color: var(--red);
  font-size: 1.25rem;
  line-height: 1;
  content: "⌄";
  transition: transform .18s ease;
}

.faq details[open] summary::after {
  transform: rotate(180deg);
}

.faq details p {
  padding-top: var(--space-3);
  color: var(--muted);
}

.site-footer {
  display: grid;
  gap: var(--space-7);
  max-width: none;
  color: rgba(var(--rgb-text), .82);
  background: var(--bg);
  border-top: 1px solid var(--line);
}

.site-footer > * {
  width: min(100%, 1080px);
  margin-inline: auto;
}

.footer-brand strong {
  color: var(--text);
}

.footer-brand p,
.footer-disclaimer {
  color: var(--muted);
}

.footer-col {
  display: grid;
  gap: var(--space-2);
}

.footer-col strong {
  color: var(--text);
}

.footer-col a {
  color: var(--muted);
  transition: color .18s ease;
}

.footer-col a:hover {
  color: var(--text);
}

.footer-social {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin-top: var(--space-5);
}

.footer-social a {
  display: inline-flex;
  color: var(--muted);
  transition: color .18s ease;
}

.footer-social svg {
  width: 20px;
  height: 20px;
}

.footer-social a:hover {
  color: var(--text);
}

.footer-disclaimer {
  font-size: .88rem;
}

.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;
}

@media (min-width: 720px) {
  .site-header {
    padding-inline: max(1.25rem, calc((100vw - 1080px) / 2 + 1.25rem));
  }

  .site-nav {
    gap: var(--space-4);
  }

  .site-nav > a:not(.btn) {
    display: inline-flex;
  }

  /* Tightened from 8rem: the hero now carries a sims line and a proof strip, and
     on a 800px-tall desktop the old top padding pushed the proof caption under
     the cookie banner on first visit. */
  .hero {
    gap: var(--space-6);
    padding-top: clamp(5.5rem, 10vw, 6rem);
  }

  .hero-proof {
    margin-top: var(--space-4);
  }

  /* Uncapped so the five sim names sit on one line here; the 640px cap that keeps
     them readable on narrow screens forces a needless second line at this width. */
  .hero-sims {
    max-width: none;
  }

  .feature-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .site-footer {
    /* Full-width bar: the brand block and every link column sit on a single
       row that spans the whole width, spaced apart, then the disclaimer runs
       full-width beneath. Works for both the five-column landing footer and
       the four-column content-page footer. */
    padding-inline: clamp(1.5rem, 5vw, 5rem);
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    gap: 2.5rem 2rem;
    align-items: start;
  }

  .footer-brand {
    /* Leftmost column: wordmark, tagline and social icons. */
    flex: 0 1 260px;
  }

  .footer-brand p {
    max-width: 40ch;
  }

  .footer-col {
    flex: 0 1 auto;
  }

  .site-footer > * {
    width: auto;
  }

  .footer-disclaimer {
    flex-basis: 100%;
  }
}

/* Phones: give the hero a 4:3 frame so the centered explainer fills the width
   instead of sitting small inside a short 16:9 letterbox. landing.js swaps in
   pre-cropped 4:3 sources; object-fit: cover also center-crops the 16:9 poster
   (and the desktop file, for no-JS) to the same safe region. */
@media (max-width: 719px) {
  .hero-video {
    aspect-ratio: 4 / 3;
  }

  /* The phone header is logo + one CTA, so the chip still fits — but only if it
     stops shouting. Smaller type and tighter padding, same words. */
  .site-header .beta-badge {
    padding: var(--space-1) var(--space-2);
    font-size: .6rem;
    letter-spacing: .08em;
  }

  .brand {
    gap: var(--space-3);
  }

  /* Tighten the hero stack so the CTA *and* the proof strip below it both clear
     the cookie banner (fixed, ~150px tall, 20px off the bottom) on a 812px
     phone. Without this the proof caption sits under the banner on first visit. */
  .hero {
    gap: var(--space-5);
    padding-top: 3.5rem;
  }

  /* Wide mono tracking wraps five sim names onto three lines here. Tighten it to
     two so the sims list costs the hero a line, not two. */
  .hero-sims {
    font-size: .6rem;
    letter-spacing: .08em;
    line-height: 1.6;
  }

  .hero-proof {
    margin-top: var(--space-4);
  }

  /* A thin frame on phones, so the screenshot keeps as much width as it can. */
  .hero-media {
    margin-top: var(--space-4);
    padding: var(--space-2);
  }

  .hero-shot {
    border-radius: var(--radius-2);
  }

  /* A square template cover-cropped into a tall portrait hero collapses to a
     muddy centre slice. On phones, lift the template (brighter, less veil) so
     it still reads as the real paint sheet, while keeping the centre dark
     enough for the headline. */
  .hero-canvas-img {
    background-position: center 44%;
    filter: saturate(.5) brightness(.64) contrast(1.05);
    opacity: .74;
  }

  .hero-canvas::after {
    background:
      radial-gradient(94% 44% at 50% 45%, rgba(var(--rgb-ink), .84), rgba(var(--rgb-ink), .5) 60%, rgba(var(--rgb-ink), .68)),
      linear-gradient(180deg, rgba(var(--rgb-ink), .42), rgba(var(--rgb-ink), .12) 32%, rgba(var(--rgb-ink), .8));
  }
}

/* ── Old-way-vs-Ailivery comparison ─────────────────────────────── */
.compare {
  background: var(--surface);
}

.compare-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  margin-top: var(--space-8);
}

.compare-col {
  padding: var(--space-7) var(--space-8);
  border: 1px solid var(--line);
  border-radius: var(--radius-4);
  background: var(--card-deep);
  box-shadow: var(--shadow-card);
}

.compare-col h3 {
  margin-bottom: var(--space-5);
}

.compare-col--new {
  border-color: var(--red);
  box-shadow: 0 24px 60px rgba(var(--rgb-red), .18), var(--shadow-card);
}

.compare-col ul {
  display: grid;
  gap: var(--space-4);
  list-style: none;
}

.compare-col li {
  position: relative;
  padding-left: var(--space-7);
  color: var(--muted);
  line-height: 1.45;
}

.compare-col li::before {
  position: absolute;
  left: 0;
  top: -.02rem;
  font-weight: 800;
}

.compare-col--old li::before {
  content: "\2715"; /* ✕ */
  color: var(--muted-dim);
}

.compare-col--new li::before {
  content: "\2713"; /* ✓ */
  color: var(--cyan);
}

.compare-col--new li {
  color: rgba(var(--rgb-text), .9);
}

/* ── Bring-your-design spotlight ────────────────────────────────── */
.spotlight {
  text-align: center;
}

.spotlight .eyebrow {
  display: block;
  margin-bottom: var(--space-3);
}

.spotlight-lead {
  max-width: 640px;
  margin: var(--space-5) auto 0;
  color: var(--muted);
  font-size: clamp(1.02rem, 1.6vw, 1.12rem);
}

.spotlight-cta {
  margin-top: var(--space-7);
}

@media (min-width: 720px) {
  .compare-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}

/* ══════════════ Studio scroll story: chat → paint → wall ══════════════ */
.st { position: relative; }
.st .eyebrow { display: block; margin-bottom: var(--space-4); }
.st-h { max-width: none; margin: 0 0 var(--space-4); text-align: left; font-family: var(--font-display); font-size: clamp(2rem, 4vw, 3rem); line-height: 1.02; letter-spacing: -.005em; }
.st-lead { max-width: 44ch; color: var(--muted); font-size: clamp(1rem, 1.5vw, 1.1rem); }
/* Cost-of-doing-it-yourself line. Aimed at the Photoshop evening, never at
   painters — see the marketing guardrail. */
.st-loss { max-width: 44ch; margin-top: var(--space-5); color: var(--text); font-size: clamp(.92rem, 1.3vw, 1rem); font-weight: 600; }

/* Sticky pinned stage (chat + paint). min-height = scroll-travel: the bigger it
   is, the longer each scene stays pinned and the slower its animation plays. */
.st-chat { min-height: 360vh; }
/* Seven templates share this travel, so the section is longer than the chat one — about
   0.6 viewport of scroll per template, quick enough that flicking through reads as a montage. */
.st-paint { min-height: 540vh; }
.st-chat .st-stick, .st-paint .st-stick {
  position: sticky; top: 0; min-height: 100vh;
  display: grid; grid-template-columns: 0.92fr 1.08fr; gap: clamp(1.5rem, 4vw, 3.2rem); align-items: center;
}
.st-side { align-self: center; }

/* Shared studio-window chrome */
.st-frame {
  width: 100%; max-width: 560px; justify-self: center;
  border: 1px solid var(--line); border-radius: var(--radius-4); overflow: hidden;
  background: var(--card-deep); box-shadow: var(--shadow-float);
}
.st-bar { display: flex; align-items: center; gap: var(--space-4); padding: var(--space-4) var(--space-5); background: var(--chat-panel); border-bottom: 1px solid var(--card); }
.st-dots { display: flex; gap: 6px; }
.st-dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--line); }
.st-mk { height: 15px; width: auto; margin-left: var(--space-1); }
.st-title { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .66rem; letter-spacing: .04em; color: var(--muted); }
.st-pill { margin-left: auto; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .56rem; font-weight: 700; letter-spacing: .16em; color: var(--cyan); border: 1px solid rgba(var(--rgb-cyan), .35); background: rgba(var(--rgb-cyan), .08); border-radius: var(--radius-5); padding: var(--space-2) var(--space-4); }
.st-pill.is-gen { color: var(--amber); border-color: rgba(var(--rgb-amber), .4); background: rgba(var(--rgb-amber), .1); animation: st-pulse 1.2s ease-in-out infinite; }
@keyframes st-pulse { 0%, 100% { opacity: 1 } 50% { opacity: .45 } }

/* Chat body + messages */
.st-chat-body { display: flex; flex-direction: column; gap: var(--space-4); padding: var(--space-5); height: 420px; max-height: 54vh; overflow: hidden; background: radial-gradient(120% 80% at 50% 0%, var(--surface), var(--chat-panel)); }
.st-msg { display: flex; align-items: flex-end; gap: var(--space-3); max-width: 88%; opacity: 0; transform: translateY(12px); transition: opacity .5s ease, transform .5s ease; }
.st-msg.in { opacity: 1; transform: none; }
.st-msg p { margin: 0; padding: var(--space-4) var(--space-5); border-radius: var(--radius-3); font-size: .92rem; line-height: 1.4; }
.st-msg--me { align-self: flex-end; }
.st-msg--me p { background: linear-gradient(180deg, rgba(var(--rgb-red), .22), rgba(var(--rgb-red), .12)); border: 1px solid rgba(var(--rgb-red-hi), .35); border-bottom-right-radius: 4px; color: var(--text); }
.st-msg--ai p { background: var(--card); border: 1px solid var(--line); border-bottom-left-radius: 4px; color: var(--text); }
.st-av { width: 22px; height: 22px; border-radius: 50%; padding: 3px; background: var(--chat-panel); border: 1px solid var(--line); flex: none; }
.st-typing { display: inline-flex; gap: 3px; margin-left: var(--space-3); vertical-align: middle; }
.st-typing i { width: 5px; height: 5px; border-radius: 50%; background: var(--cyan); animation: st-type 1s ease-in-out infinite; }
.st-typing i:nth-child(2) { animation-delay: .15s; }
.st-typing i:nth-child(3) { animation-delay: .3s; }
@keyframes st-type { 0%, 100% { opacity: .3; transform: translateY(0) } 50% { opacity: 1; transform: translateY(-3px) } }

/* Paint HUD */
.st-hud { margin-top: var(--space-7); font-family: ui-monospace, "SF Mono", Menlo, monospace; }
.st-pct { font-size: 2.8rem; font-weight: 800; line-height: 1; letter-spacing: -.03em; color: var(--text); font-variant-numeric: tabular-nums; }
.st-pct small { font-size: 1rem; color: var(--muted); margin-left: var(--space-1); }
.st-plab { text-transform: uppercase; letter-spacing: .22em; font-size: .62rem; color: var(--muted); margin-top: var(--space-1); }
.st-stages { list-style: none; margin: var(--space-5) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.st-stages li { display: flex; align-items: center; gap: var(--space-4); font-size: .78rem; color: var(--muted-dim); transition: color .3s; }
.st-stages b { font-size: .68rem; border: 1px solid var(--line); border-radius: var(--radius-1); padding: var(--space-1) var(--space-2); color: var(--muted-dim); transition: all .3s; }
.st-stages li.on { color: var(--text); }
.st-stages li.on b { background: var(--red); border-color: var(--red); color: var(--on-red); }

/* Paint canvas — a stack of seven templates, one visible at a time.
   The sweep variables (--p, --edge, --done) sit on .st-cart, so the wet edge and the
   RACE-READY stamp are single elements over the stack rather than one set per slide. */
.st-cart { position: relative; aspect-ratio: 1; overflow: hidden; background: var(--ink); }
.st-slide { position: absolute; inset: 0; opacity: 0; visibility: hidden; }
/* No crossfade: the outgoing slide is fully painted and the incoming one is bare, so any
   overlap reads as the paint flickering off. The swap happens on one frame, at the moment
   the sweep restarts from the left edge, where nothing is on screen to blend anyway. */
.st-slide.is-on { opacity: 1; visibility: visible; }
.st-layer { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.st-painted { clip-path: polygon(0 0, calc(var(--p, 0) * 118%) 0, calc(var(--p, 0) * 118% - 14%) 100%, 0 100%); }
/* Two effects below light the render rather than colouring the page, so they stay literal.
   .st-edge is the wet edge of the paint sweep — white through mix-blend-mode: screen, which
   is how it reads as a highlight on the car instead of a white bar. .st-shard's drop-shadow
   falls from a small piece flying over the page, and a shadow is dark on either theme. */
.st-edge { position: absolute; top: -4%; bottom: -4%; width: 7%; left: calc(var(--p, 0) * 118% - 12%); opacity: var(--edge, 0); background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .9), rgba(var(--rgb-cyan), .5), transparent); filter: blur(1.5px); transform: skewX(-8deg); mix-blend-mode: screen; pointer-events: none; }
.st-stamp { position: absolute; right: 14px; bottom: 14px; display: flex; align-items: center; gap: var(--space-3); opacity: var(--done, 0); transform: scale(calc(.85 + .15 * var(--done, 0))); transform-origin: bottom right; }
/* width:auto is load-bearing: the img carries intrinsic width/height attributes
   (Lighthouse wants them, and they reserve layout), and without this the 346px
   width attribute wins and stretches the mark. Same reason .brand img and
   .st-mk pair their height with width:auto. */
.st-stamp img { height: 24px; width: auto; }
.st-stamp b { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .56rem; letter-spacing: .16em; color: var(--on-red); background: var(--red); padding: var(--space-2) var(--space-3); border-radius: var(--radius-1); }
/* Seven ticks under the frame. Without them the section reads as one paint that keeps
   restarting; with them it reads as a queue you are working through. */
.st-rail { display: flex; gap: var(--space-3); margin-top: var(--space-4); padding: 0; list-style: none; }
.st-rail li { flex: 1; height: 3px; border-radius: var(--radius-5); background: var(--card); transition: background .2s linear; }
.st-rail li.done { background: rgba(var(--rgb-cyan), .45); }
.st-rail li.on { background: var(--red); }

/* Wall — pinned "hang" like chat/paint; the gallery drifts through the pin */
.st-wall { overflow-x: clip; min-height: 300vh; } /* pin travel; clip contains shard bleed (no scroll container → sticky-safe) */
.st-wall-stick {
  position: sticky; top: 0; height: 100vh; min-height: 560px; box-sizing: border-box;
  display: flex; flex-direction: column;
  padding: clamp(3.5vh, 6vh, 82px) 0 clamp(2.5vh, 4vh, 54px);
  gap: clamp(1rem, 2.4vh, 1.7rem);
}
.st-wall-head { flex: 0 0 auto; text-align: left; margin: 0; }
.st-gallery-vp {
  flex: 1 1 auto; min-height: 0; position: relative; overflow: hidden;
  /* The #000 in a mask is not a colour, it is an alpha channel written in hex: black means
     keep this pixel, transparent means drop it. Theming it would delete the fade. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 11%, #000 85%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 11%, #000 85%, transparent 100%);
}
.st-gallery { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; align-items: start; will-change: transform; }
.st-col { display: flex; flex-direction: column; gap: 16px; will-change: transform; }
.st-col--down { margin-top: 3.4rem; }
.st-tile { position: relative; aspect-ratio: 1; border-radius: var(--radius-3); overflow: hidden; border: 1px solid var(--line); background: var(--card-deep); box-shadow: var(--shadow-soft); }
.st-tile img { width: 100%; height: 100%; object-fit: cover; display: block; }
.st-cap { position: absolute; left: 0; right: 0; bottom: 0; display: flex; justify-content: space-between; align-items: center; padding: var(--space-6) var(--space-4) var(--space-3); font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .56rem; letter-spacing: .13em; color: var(--text); background: linear-gradient(0deg, rgba(var(--rgb-ink), .94), transparent); }
.st-cap span:last-child { color: var(--cyan); }
.st-shard { position: absolute; height: 50px; clip-path: polygon(22% 0, 100% 0, 78% 100%, 0 100%); opacity: .9; z-index: 4; pointer-events: none; will-change: transform; filter: drop-shadow(0 12px 22px rgba(0, 0, 0, .4)); }
.st-chip { position: absolute; z-index: 5; pointer-events: none; will-change: transform; font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: .58rem; font-weight: 700; letter-spacing: .15em; color: var(--bg); padding: var(--space-2) var(--space-3); border-radius: var(--radius-1); box-shadow: var(--shadow-soft); }

/* Reveal-on-scroll utility */
.st-reveal { opacity: 0; transform: translateY(26px); transition: opacity .7s cubic-bezier(.2, .7, .2, 1), transform .7s cubic-bezier(.2, .7, .2, 1); transition-delay: calc(var(--i, 0) * 70ms); }
.st-reveal.in { opacity: 1; transform: none; }

/* Responsive */
@media (max-width: 860px) {
  /* Stacked pin: center heading + card as ONE group. Without align-content the
     single-column grid rows stretch to fill 100vh and align-items:center then
     splits them — heading floats high, card sinks low with a dead gap between. */
  .st-chat .st-stick, .st-paint .st-stick { grid-template-columns: 1fr; gap: var(--space-6); align-content: center; }
  .st-side { text-align: center; }
  .st-h, .st-lead, .st-loss { text-align: center; margin-inline: auto; }
  .st-hud { display: none; }
  .st-chat { min-height: 300vh; }
  .st-paint { min-height: 460vh; }

  /* Chat card fits its whole thread on phones. Was a fixed 420px while the
     wrapped bubbles run ~500px, so scrollTop=scrollHeight pushed the first
     message off the top. Auto height (capped to the viewport) + tighter bubbles
     keep every message in view. */
  .st-chat-body { height: auto; max-height: 60vh; gap: var(--space-3); padding: var(--space-5); }
  .st-msg { max-width: 92%; }
  .st-msg p { font-size: .85rem; padding: var(--space-3) var(--space-4); }

  /* Wall hangs on mobile too, like chat + paint (was unpinned). The desktop
     pinned CSS carries over as-is; overflow-x:clip on .st-wall still contains
     any shard bleed. Head only needs re-centering. */
  .st-wall-head { text-align: center; }
  .st-wall-head .st-h, .st-wall-head .st-lead { margin-inline: auto; }
}
@media (max-width: 760px) {
  .st-gallery { grid-template-columns: repeat(2, 1fr); }
  .st-col { display: contents; }
  .st-col--down { margin-top: 0; }
  .st-chip { font-size: .5rem; }
  .st-shard { height: 38px; }
}

@media (prefers-reduced-motion: reduce) {
  .st-chat .st-stick, .st-paint .st-stick { position: static; min-height: 0; }
  .st-chat, .st-paint { min-height: 0; }
  .st-chat-body { height: auto; max-height: none; overflow: visible; }
  .st-msg, .st-reveal { opacity: 1 !important; transform: none !important; }
  .st-painted { clip-path: none !important; }
  .st-edge, .st-typing { display: none !important; }
  .st-stamp { opacity: 1 !important; transform: none !important; }
  /* Nothing scrolls here, so the stack has no way to advance. Lay the seven out as a grid
     instead of leaving six of them hidden behind the first. */
  .st-cart { aspect-ratio: auto; display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-3); }
  .st-slide { position: relative; opacity: 1; visibility: visible; aspect-ratio: 1; }
  .st-stamp { position: static; justify-content: flex-end; }
  .st-rail { display: none; }
  .st-wall { min-height: 0; }
  .st-wall-stick { position: static; height: auto; min-height: 0; display: block; padding: 0; }
  .st-gallery-vp { overflow: visible; -webkit-mask-image: none; mask-image: none; }
  .st-gallery, .st-col, .st-shard, .st-chip { transform: none !important; }
}
