:root {
  --ink: #0c0c0d;
  --ink-soft: #171719;
  --paper: #e8e8e4;
  --mist: #b8b8b4;
  --line: rgba(232, 232, 228, 0.2);
  --finish-color: #4c151b;
  /* Invictus champagne gold. #c3a470 on --ink (#0c0c0d) measures ~8.4:1,
     comfortably clearing AA (4.5:1) for small text as well as for rules,
     borders and arrow glyphs, so it is used directly for both. --accent-text
     is one step lighter for the rare case of gold-on-gold-ish or busier
     backgrounds where a touch more separation reads cleaner - same hue, so it
     still reads as the one accent, not a second colour. --accent-deep is one
     step darker, for accent text set on --paper (light) surfaces. */
  --accent-text: #cdb183;
  --accent-deep: #a1804e;
  --sans: "Manrope", Arial, sans-serif;
  --serif: "Bodoni Moda", Georgia, serif;
  --mono: "DM Mono", monospace;
  --ease: cubic-bezier(.19, 1, .22, 1);
}

/* Subtle film-grain texture for dark sections - reads as filmic depth
   rather than a flat fill, common register in premium automotive work. */
.grain { background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>"); inset: 0; mix-blend-mode: overlay; opacity: .05; pointer-events: none; position: absolute; z-index: 1; }

/* Scroll reveal: content rises and fades in as it enters view. Hiding is
   scoped to html.js-ready so a failed/blocked script.js never leaves
   content permanently invisible - visible-by-default is the safe state. */
html.js-ready [data-reveal] { opacity: 0; transform: translateY(1.6rem); transition: opacity .9s var(--ease), transform .9s var(--ease); }
html.js-ready [data-reveal].is-visible { opacity: 1; transform: translateY(0); }
html.js-ready [data-reveal="stagger"] > * { opacity: 0; transform: translateY(1.4rem); transition: opacity .8s var(--ease), transform .8s var(--ease); }
html.js-ready [data-reveal="stagger"].is-visible > * { opacity: 1; transform: translateY(0); }
[data-reveal="stagger"].is-visible > *:nth-child(2) { transition-delay: .08s; }
[data-reveal="stagger"].is-visible > *:nth-child(3) { transition-delay: .16s; }
[data-reveal="stagger"].is-visible > *:nth-child(4) { transition-delay: .24s; }
[data-reveal="stagger"].is-visible > *:nth-child(5) { transition-delay: .32s; }
[data-reveal="stagger"].is-visible > *:nth-child(6) { transition-delay: .4s; }
@media (prefers-reduced-motion: reduce) {
  [data-reveal], [data-reveal="stagger"] > * { opacity: 1; transform: none; transition: none; }
}

/* ===========================================================================
   ENTRY GATE
   ---------------------------------------------------------------------------
   Full-screen overlay, first child of <body>, ahead of the site. Built from
   the page's own devices (grain, mono instrument line, hairline accent rule,
   serif display type) rather than a new visual language, so it reads as this
   studio's front door, not a marketing popup. Jay owns the show/hide class
   and the fetch/submit logic; these rules cover both static states.
   =========================================================================== */
.entry-gate {
  align-items: center;
  background: var(--ink);
  bottom: 0;
  display: flex;
  justify-content: center;
  left: 0;
  overflow-y: auto;
  padding: clamp(2.5rem, 6vw, 5rem) clamp(1.2rem, 4vw, 3rem);
  position: fixed;
  right: 0;
  top: 0;
  z-index: 100;
}
/* .entry-gate sets display:flex, which (same footgun Dex already fixed on
   .gate-panel/.gate-confirmed below) beats the UA [hidden] rule on cascade
   origin regardless of specificity - author styles win over the user-agent
   stylesheet. Jay's script.js sets gate.hidden = true to close the gate, so
   this override is required for that to actually take visual effect. */
.entry-gate[hidden] { display: none; }
/* Body scroll-lock while the gate is open, toggled by script.js. */
body.gate-open { overflow: hidden; }
/* A whisper of the hero's own stage-glow, so the gate is not a flat fill. */
.entry-gate::before {
  background: radial-gradient(circle at 28% 22%, rgba(195,164,112,.12), transparent 46%), radial-gradient(circle at 86% 84%, rgba(232,232,228,.05), transparent 42%);
  content: "";
  inset: 0;
  pointer-events: none;
  position: absolute;
}
/* min-width:0 overrides the flex item default of min-width:auto, which
   otherwise refuses to shrink .gate-inner below its content's min-content
   size and pushes the whole panel wider than the viewport on narrow screens
   (same footgun already fixed on .enquiry-form .field-group below). */
/* 34rem, not 26rem. At 26rem the display headline had a 416px measure to set
   54px type in, so "Precision does not scale. / Neither will the studio."
   rendered as FOUR lines with two one-word orphans ("scale." / "studio.")
   instead of the two the <br> asks for - an accidental rag on the first thing
   a visitor ever sees, which is the opposite of the deliberate typography the
   rest of the page holds to. The body copy below keeps its own 24rem measure,
   so only the headline and the instrument rule widen: the rule now sets the
   headline's width, which is the composition this panel wanted all along.
   Costs nothing on phones - width:100% still binds there. */
.gate-inner { margin: auto; max-width: 34rem; min-width: 0; position: relative; width: 100%; z-index: 3; }
.gate-panel { display: grid; }
/* .gate-panel sets display:grid, which beats the UA [hidden] rule on
   specificity, so both states need an explicit hidden override (same fix as
   .booking-form[hidden] / .booking-modes[hidden] above). */
.gate-panel[hidden] { display: none; }
/* The panel takes programmatic focus on open (script.js openGate) purely so
   the dialog announces itself; it is not an interactive control and must not
   draw a ring. Every real control inside it keeps its own :focus-visible. */
.gate-panel:focus { outline: none; }
.gate-wordmark { display: inline-block; margin-bottom: clamp(2rem, 5vw, 3.2rem); }
.gate-status-line { max-width: none; }
/* 16ch measured ~4px narrower than "Precision does not scale." sets, which is
   all it took to orphan "scale." onto a line of its own. The panel's own
   34rem already governs the measure; this only needs to stop being the
   tighter of the two. */
.gate-title { font-size: clamp(2.3rem, 4.6vw, 3.4rem); letter-spacing: -.045em; line-height: .98; margin: 0 0 1.2rem; max-width: 21ch; }
.gate-intro { color: rgba(232,232,228,.75); font-size: .86rem; line-height: 1.65; margin: 0 0 1.4rem; max-width: 24rem; }
/* Same accent left-rule the page already uses for a sentence spoken in the
   studio's own voice (.hero-creed). The "Grand Opening Price" line gets the
   same register: a fact stated once, not a banner. */
.gate-price { border-left: 1px solid #c3a470; color: rgba(232,232,228,.86); font-size: .82rem; line-height: 1.65; margin: 0 0 2.2rem; max-width: 24rem; padding-left: 1.1rem; }
.gate-form { display: grid; gap: 1.1rem; margin-bottom: 1.6rem; max-width: 24rem; }
.gate-field { align-content: start; display: grid; gap: .4rem; }
.gate-field label { color: rgba(232,232,228,.55); font-family: var(--mono); font-size: .56rem; letter-spacing: .12em; text-transform: uppercase; }
.gate-field input { background: transparent; border: 0; border-bottom: 1px solid rgba(232,232,228,.28); color: var(--paper); font-family: var(--sans); font-size: 1rem; padding: .55rem 0; transition: border-color .2s ease; width: 100%; }
.gate-field input:focus { border-color: #c3a470; outline: none; }
.gate-field input:user-invalid { border-color: #c3a470; }
.gate-submit { margin-top: .2rem; }
.gate-form .consultation-link[disabled] { opacity: .5; pointer-events: none; }
/* The escape hatch. Deliberately quiet: mono, small, no accent colour, no
   underline until hover. It must never compete with the email field or the
   submit button above it for attention.
   Was .42 alpha at .6rem, which measured 3.53:1 on this background - under the
   4.5:1 floor, on the ONE control whose whole job is to prove the visitor is
   not trapped. An escape hatch nobody can read is a door that is shut. Now
   .55/.65rem = 5.29:1, the same tone the field labels and the status key
   already use, so it is still plainly tertiary against the white submit row
   above it - quiet, but legible to a 55-year-old on a bright day. */
.gate-skip {
  background: none;
  border: 0;
  border-bottom: 1px solid transparent;
  color: rgba(232,232,228,.55);
  cursor: pointer;
  display: inline-flex;
  font-family: var(--mono);
  font-size: .65rem;
  gap: 1rem;
  letter-spacing: .1em;
  min-height: 2.75rem;
  padding: .3rem 0;
  text-transform: uppercase;
  transition: color .2s ease, border-color .2s ease;
}
.gate-skip span { font-size: .85rem; }
.gate-skip:hover, .gate-skip:focus-visible { border-bottom-color: rgba(232,232,228,.3); color: rgba(232,232,228,.88); outline: none; }
/* The gate's status line carries whole sentences ("Could not submit right now.
   You're welcome to continue and join the list further down the page
   instead."). .form-status is built for a three-word booking state, so at
   9.6px uppercase mono that sentence became two lines of shouting at the exact
   moment the visitor needs to READ something. Sentence case, sans, 12px: the
   failure is the one moment on this page that cannot afford style over
   legibility. */
.gate-form .form-status { font-family: var(--sans); font-size: .75rem; letter-spacing: 0; line-height: 1.55; text-transform: none; }

.gate-confirmed[hidden] { display: none; }
.gate-confirmed .gate-title { margin-top: .3rem; }
.gate-confirmed .gate-intro { margin-bottom: 2rem; }
.gate-enter { margin-top: .4rem; }

@media (max-width: 560px) {
  .entry-gate { padding: 2.2rem 1.1rem 2.6rem; }
  .gate-title { font-size: clamp(2rem, 9vw, 2.5rem); }
  .gate-wordmark { margin-bottom: 1.8rem; }
}
@media (max-width: 900px) {
  /* The gate's field sat at 40px because the .enquiry-form rule further down
     that lifts every other control on this site to the 44px touch floor does
     not match `.gate-field input`. Same fix, same number, applied to the one
     input a phone visitor meets before anything else. Font size was already
     16px, so iOS never zoomed here. */
  .gate-field input { padding: .7rem 0; }
}

/* SHORT VIEWPORTS. The gate stood 950px tall against a 720px-high window - the
   real height of a 1440x900 laptop once browser chrome is taken off, and of
   every 1366x768 Windows machine. Measured at 1440x720: the submit button
   ended at 764px and "Continue without joining" at 870px, so a visitor on a
   perfectly ordinary laptop met a black screen with a headline, no button and
   no way out, with only a hairline scrollbar (and on macOS, an overlay
   scrollbar that is not drawn at all) to tell them there was more. That is the
   paywall reading this gate exists to avoid. Nothing is removed here: the
   vertical rhythm compresses so the whole proposition, the field, the submit
   and the way out all sit above the fold together. */
@media (min-width: 561px) and (max-height: 880px) {
  .entry-gate { padding-block: 2.2rem; }
  .gate-wordmark { margin-bottom: 1.5rem; }
  .gate-status-line { margin-bottom: 1rem; padding-bottom: .6rem; }
  .gate-panel .eyebrow { margin-bottom: .8rem; }
  .gate-title { font-size: clamp(2rem, 3.6vw, 2.9rem); margin-bottom: .9rem; }
  .gate-intro { margin-bottom: 1rem; }
  .gate-price { margin-bottom: 1.4rem; }
  .gate-form { gap: .9rem; margin-bottom: 1.1rem; }
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; background: var(--ink); }
body { margin: 0; background: var(--ink); color: var(--paper); font-family: var(--sans); font-size: 16px; }
button, input { font: inherit; }
a { color: inherit; text-decoration: none; }
.skip-link { position: fixed; left: 1rem; top: -5rem; background: var(--paper); color: var(--ink); padding: 0.75rem 1rem; z-index: 20; }
.skip-link:focus { top: 1rem; }

.site-header { align-items: center; display: grid; grid-template-columns: 1fr auto 1fr; left: 0; padding: 1.6rem clamp(1.2rem, 4vw, 4.5rem); position: absolute; right: 0; top: 0; z-index: 10; }
/* Real brand mark (confirmed 2026-08-11: gold layered-V lockup, production
   SVG at assets/brand/, built by Dex from Nick's approved reference). The
   anchor carries layout + the accessible name (aria-label); the <img> is
   the mark itself, decorative alt="" so it isn't announced twice. One SVG,
   sized per context via height with width auto - swapping the brand mark
   later (a colour variant, a refined version) is a single file replace,
   never a markup change, by design. */
/* justify-self: start - .wordmark is a direct child of .site-header's grid,
   whose "1fr" first column otherwise STRETCHES this inline-block anchor to
   fill the whole column width (grid items default to justify-self: stretch,
   overriding display: inline-block). The visible logo image was already
   left-aligned inside that stretched box, so this doesn't move the logo a
   single pixel - it only shrinks the anchor's invisible box down to the
   image's real size, which .wordmark-tm below needs to sit next to the
   actual mark instead of floating near the nav links. */
.wordmark { display: inline-block; justify-self: start; line-height: 0; position: relative; white-space: nowrap; }
.wordmark-mark { display: block; height: 1.5rem; width: auto; }
.gate-wordmark .wordmark-mark { height: clamp(2rem, 4.4vw, 2.6rem); }
footer .wordmark-mark { height: 1.3rem; }
/* Trademark badge, sat at the wordmark's own top-right corner (the standard
   placement) rather than crowding the mark itself, which is vector art, not
   editable text. TM only, never (R) - see the filing-status comments beside
   both header and footer wordmarks in index.html for why. */
.wordmark-tm { color: var(--accent-text); font-family: var(--mono); font-size: .6rem; letter-spacing: 0; line-height: 1; position: absolute; right: -.7rem; text-transform: none; top: -.05rem; }
footer .wordmark-tm { font-size: .5rem; right: -.55rem; }
nav { display: flex; gap: clamp(1rem, 2.2vw, 2.5rem); }
nav a, .header-cta, .finish-rail, .eyebrow, .mono-label, .hero-status, .service-index, footer { font-family: var(--mono); font-size: 0.63rem; letter-spacing: 0.12em; text-transform: uppercase; }
nav a { color: rgba(232,232,228,.72); transition: color .2s ease; }
nav a:hover, nav a:focus-visible { color: var(--paper); }
.header-cta { border-bottom: 1px solid #c3a470; justify-self: end; padding: 0.45rem 0; }
/* Phone-drawer twin of .header-cta. Never both: this is display:none on
   desktop and .header-cta is display:none below 900px. */
.nav-cta { display: none; }
/* 2.75rem = the 44px touch-target floor; this is the only way into the nav on
   a phone. */
.nav-toggle { background: none; border: 0; cursor: pointer; display: none; height: 2.75rem; justify-self: end; padding: 0; position: relative; width: 2.75rem; z-index: 12; }
.nav-toggle span { background: var(--paper); display: block; height: 1.5px; left: 50%; margin-left: -0.85rem; position: absolute; transition: transform .3s ease, opacity .3s ease; width: 1.7rem; }
.nav-toggle span:first-child { top: 1rem; }
.nav-toggle span:last-child { bottom: 1rem; }
.nav-toggle[aria-expanded="true"] span:first-child { transform: translateY(.3rem) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:last-child { transform: translateY(-.3rem) rotate(-45deg); }

.hero { min-height: 100svh; overflow: hidden; padding: clamp(5.5rem, 9vw, 8.5rem) clamp(1.2rem, 4vw, 4.5rem) 2.5rem; position: relative; }
.hero::after { background: linear-gradient(90deg, rgba(8,8,9,.84) 0%, rgba(8,8,9,.33) 52%, rgba(8,8,9,.02) 100%); content: ""; inset: 0; pointer-events: none; position: absolute; z-index: 1; }
.hero-copy { max-width: 30rem; position: relative; z-index: 3; }
.eyebrow { color: #c3a470; margin: 0 0 1.2rem; }

/* The premise of the page, set as an instrument reading rather than a badge:
   a ruled mono line above everything, in the same technical register as the
   hero's finish annotations. A pill or a banner would read as a notification
   the visitor can dismiss; a ruled state line reads as a fact about the
   studio. The accent square is the only colour in it. */
.prelaunch-status { align-items: center; border-bottom: 1px solid rgba(232,232,228,.2); display: flex; flex-wrap: wrap; font-family: var(--mono); font-size: .58rem; gap: .5rem 1rem; letter-spacing: .14em; margin: 0 0 1.4rem; max-width: 30rem; padding-bottom: .75rem; text-transform: uppercase; }
.status-key { color: rgba(232,232,228,.55); }
.status-value { align-items: center; color: var(--paper); display: inline-flex; gap: .55rem; }
.status-value::before { background: #c3a470; content: ""; flex: 0 0 auto; height: .4rem; width: .4rem; }
.status-place { color: rgba(232,232,228,.55); margin-left: auto; }

/* The studio's position on not having a date, given the same accent left-rule
   as the waiting-list note further down: the page's established mark for a
   sentence spoken in the studio's own voice rather than a caption. It replaced
   the dashed AWAITING-NICK placeholder that stood here, and nothing on this
   page is provisional any more. */
.hero-creed { border-left: 1px solid #c3a470; color: rgba(232,232,228,.86); font-size: .88rem; line-height: 1.6; margin: 0 0 .5rem; max-width: 24rem; padding-left: 1.1rem; }
h1, h2 { font-family: var(--serif); font-weight: 400; letter-spacing: -0.055em; line-height: .9; margin: 0; overflow-wrap: break-word; }
/* 8ch was narrower than the brand's own word. "Unconquered." sets 5.7em wide
   in Bodoni Moda; 8ch is 4.96em, so at every desktop width the h1 overflowed
   its own max-width and the `overflow-wrap: break-word` above turned that into
   a silent mid-word break: the hero read "Precision. / Unconquer / ed." at
   1440px and at 1920px. The single word the whole brand is named for, snapped
   in half, above the fold, on a page whose headline is precision. Fixed on
   both sides: 11ch gives the longest word a line of its own, and the size cap
   comes down so that line still fits the 30rem copy column the instrument rule
   above it defines. Mobile is untouched - h1 max-width is already `none`
   there, which is why the phone layout was the only one rendering correctly. */
h1 { font-size: clamp(3.4rem, 6.6vw, 5.1rem); line-height: .92; max-width: 11ch; }
.hero-intro { color: rgba(232,232,228,.75); font-size: 0.86rem; line-height: 1.65; margin: 1.5rem 0; max-width: 22rem; }
.text-link { display: inline-flex; font-family: var(--mono); font-size: .67rem; gap: 2.4rem; letter-spacing: .1em; padding: .75rem 0; position: relative; text-transform: uppercase; }
.text-link::after { background: rgba(232,232,228,.5); bottom: 0; content: ""; height: 1px; left: 0; position: absolute; right: 0; transition: background .4s var(--ease), transform .5s var(--ease); transform-origin: right; }
.text-link:hover::after, .text-link:focus-visible::after { background: #c3a470; transform: scaleX(1.015); }
.text-link span { transition: transform .4s var(--ease); }
.text-link:hover span, .text-link:focus-visible span { transform: translateY(.2rem); }

.car-stage { --image-filter: saturate(.92) contrast(1.06) brightness(1.02); bottom: 0; inset: 0 0 0 23%; overflow: hidden; position: absolute; }
.car-stage img { filter: var(--image-filter); height: 100%; object-fit: cover; object-position: 52% center; transition: opacity .4s ease, transform .55s ease; width: 100%; }
.car-stage img.uploaded { object-position: center; }
.car-tint { background: var(--finish-color); inset: 0; mix-blend-mode: color; opacity: 0; pointer-events: none; position: absolute; transition: background .45s ease, opacity .45s ease; z-index: 1; }
.car-stage.tinted .car-tint { opacity: .5; }
.stage-glow { background: radial-gradient(circle at 69% 42%, rgba(233,233,229,.17), transparent 29%); inset: 0; pointer-events: none; position: absolute; z-index: 2; }
.stage-annotation { border-left: 1px solid rgba(232,232,228,.4); color: rgba(232,232,228,.67); font-family: var(--mono); font-size: .54rem; letter-spacing: .08em; line-height: 1.45; padding: .6rem 0 .6rem .65rem; position: absolute; text-transform: uppercase; z-index: 3; }
.stage-annotation span { display: block; opacity: .6; }
.stage-annotation strong { font-weight: 400; }
.annotation-code { left: 34%; top: 28%; }
.annotation-sheen { right: 23%; top: 26%; }
.annotation-view { bottom: 12%; left: 45%; }

.upload-zone { align-items: center; border-left: 1px solid rgba(232,232,228,.75); border-bottom: 1px solid rgba(232,232,228,.75); bottom: 2.5rem; cursor: pointer; display: flex; gap: 1rem; min-height: 4.6rem; outline: none; padding: 0 1.2rem .2rem 0; position: absolute; transition: border-color .2s ease, color .2s ease; width: min(21rem, 34vw); z-index: 4; }
.upload-zone::after { border-right: 1px solid rgba(232,232,228,.75); border-top: 1px solid rgba(232,232,228,.75); content: ""; height: 1.8rem; left: 0; position: absolute; top: 0; width: 1.8rem; }
.upload-zone:hover, .upload-zone:focus-visible, .upload-zone.is-dragover { border-color: #c3a470; color: #fff; }
.upload-plus { font-family: var(--serif); font-size: 2rem; font-weight: 300; }
.upload-zone div { display: grid; gap: .25rem; }
.upload-zone div span:last-child { color: rgba(232,232,228,.58); font-size: .61rem; line-height: 1.5; }

.finish-rail { background: rgba(12,12,13,.68); border-left: 1px solid rgba(232,232,228,.22); bottom: 0; display: flex; flex-direction: column; justify-content: space-between; padding: 1.5rem 1rem 1.2rem; position: absolute; right: 0; top: clamp(5.5rem, 9vw, 8.5rem); width: clamp(9.6rem, 12vw, 12rem); z-index: 4; }
.rail-heading { border-bottom: 1px solid rgba(232,232,228,.18); display: flex; font-size: .52rem; gap: .5rem; justify-content: space-between; padding-bottom: .85rem; }
.rail-heading span:first-child { white-space: nowrap; }
.finish-options { display: grid; gap: .75rem; margin-top: auto; }
.finish-option { align-items: center; background: none; border: 0; color: rgba(232,232,228,.54); cursor: pointer; display: grid; font-family: var(--mono); font-size: .52rem; gap: .55rem; grid-template-columns: 1.2rem 1fr; letter-spacing: .06em; padding: 0; text-align: left; text-transform: uppercase; transition: color .2s ease; }
.finish-option:hover, .finish-option:focus-visible, .finish-option.is-selected { color: var(--paper); outline: none; }
.finish-option.is-selected .sample { box-shadow: 0 0 0 1px var(--paper); }
.sample { background-position: 30% 55%; background-repeat: no-repeat; background-size: 320%; display: block; height: 2rem; width: 1.2rem; }
.finish-rail > a { border-top: 1px solid rgba(232,232,228,.18); color: rgba(232,232,228,.7); font-size: .52rem; margin-top: 1.5rem; padding-top: .9rem; }
.hero-status { bottom: 1.1rem; left: clamp(1.2rem, 4vw, 4.5rem); margin: 0; position: absolute; z-index: 4; }
.hero-status span { color: #c3a470; }

.section-dark { background: var(--ink); }
/* Copy column widened from .8fr to .95fr. "The finish is<br>the signature."
   asks for two lines and was rendering as four ("The / finish is / the /
   signature.") because 83px type had a 327px column to sit in - the same
   accidental-rag defect as the hero h1, in a different section. The image
   gives up ~50px; the headline gets its lockup back. */
.manifesto { display: grid; grid-template-columns: .4fr 1.35fr .95fr; min-height: 55rem; position: relative; }
.chapter-mark { color: var(--paper); font-family: var(--serif); font-size: clamp(6rem, 15vw, 14rem); letter-spacing: -.1em; line-height: .7; padding: clamp(2rem, 5vw, 5rem); }
.manifesto-image { min-height: 52rem; }
.image-vinyl { background: linear-gradient(90deg, rgba(12,12,13,.55), transparent 48%), url("assets/cars/craft-hands.jpg") center/cover; }
.manifesto-copy { align-self: end; padding: clamp(2rem, 5vw, 6rem) clamp(1.5rem, 4vw, 4rem); }
/* Floor lowered 3rem -> min(2.7rem, 11vw). The FLOOR is what binds on a
   phone (5.8vw is only 23px there), and a fixed floor cannot shrink for a
   narrower phone - so at 48px "Crafted by hand. / Perfected by time." needed
   351px of a 337px column and broke into "Perfected by / time.", worse again
   at 360px. Same accidental-rag defect as the hero h1 and the manifesto h2,
   on the device most of this audience arrives on. The min() keeps 2.7rem
   everywhere at or above ~393px and lets it track the viewport below that,
   so every two-line lockup this rule serves holds down to 320px. */
.manifesto-copy h2, .craft-copy h2, .archive-heading h2, .consultation h2 { font-size: clamp(min(2.7rem, 11vw), 5.8vw, 6.5rem); }
/* The manifesto's headline sits in the narrowest column on the page, so it is
   the one that has to be sized to its own measure rather than to the shared
   scale above. Editorial practice, not an inconsistency: type is set to its
   column. */
.manifesto-copy h2 { font-size: clamp(2.2rem, 3.9vw, 4rem); }
.manifesto-copy p:not(.eyebrow), .craft-copy p:not(.eyebrow), .archive-heading > p:not(.eyebrow), .consultation-copy p { color: rgba(232,232,228,.68); font-size: .84rem; line-height: 1.7; max-width: 20rem; }

.transformation { background: #dddeda; color: #161617; display: grid; grid-template-columns: minmax(16rem,1fr) minmax(26rem,1.6fr); gap: 2rem; padding: clamp(3rem, 8vw, 8rem) clamp(1.2rem, 5vw, 5rem); position: relative; }
.transformation .eyebrow { color: #9d343c; }
.transformation h2 { font-size: clamp(2.7rem, 5.8vw, 6rem); overflow-wrap: break-word; }
.transformation-copy > p:last-child { color: #565654; font-size: .86rem; line-height: 1.75; max-width: 18rem; }
.transition-car { background: linear-gradient(90deg, rgba(228,228,224,.85), rgba(13,13,14,.06) 45%, rgba(14,14,15,.72)), url("assets/cars/transform-car.jpg") center/cover; min-height: 38rem; position: relative; }
.transition-car::after { background: linear-gradient(#222, #222); content: ""; height: calc(100% - 4rem); left: 50%; mix-blend-mode: color; opacity: .48; position: absolute; top: 2rem; width: 1px; }
.service-index { background: #dddeda; grid-column: 2; justify-self: end; margin-top: -9rem; padding: 1.5rem 1.5rem 0; position: relative; width: min(32rem, 80%); z-index: 2; }
.service-index > p { color: #7d7d79; margin: 0 0 .6rem; }
.service-index a { border-top: 1px solid rgba(22,22,23,.23); display: grid; font-size: .66rem; gap: 1rem; grid-template-columns: 2rem 1fr auto; padding: 1rem 0; transition: padding .25s ease; }
.service-index a:last-child { border-bottom: 1px solid rgba(22,22,23,.23); }
.service-index a:last-of-type { margin-bottom: 1.5rem; }
.service-index a:hover, .service-index a:focus-visible { padding-left: .8rem; }
.service-index span { color: #9d343c; }
.service-index b { font-size: 1rem; font-weight: 400; }

.craft { display: grid; grid-template-columns: .4fr 1.25fr .75fr; min-height: 58rem; position: relative; }
.craft-main { min-height: 52rem; }
.image-craft { background: linear-gradient(180deg, transparent 55%, rgba(12,12,13,.85)), url("assets/cars/craft-main.jpg") center/cover; }
.craft-details { align-self: center; display: grid; gap: 1.4rem; margin-left: -6rem; padding: 2rem 1.4rem; position: relative; z-index: 2; }
.image-detail { aspect-ratio: 1.45; }
.detail-one { background: url("assets/cars/detail-beads.jpg") center/cover; }
.detail-two { background: url("assets/cars/detail-panel.jpg") center/cover; filter: contrast(1.08); }
.craft-copy { align-self: end; grid-column: 2 / 4; padding: 0 clamp(1.4rem, 4vw, 4rem) clamp(3rem, 7vw, 7rem); position: relative; z-index: 3; }
.craft-copy p:not(.eyebrow) { max-width: 23rem; }

.process { padding: clamp(3.5rem, 8vw, 8rem) clamp(1.2rem, 5vw, 5rem); }
.process-head { margin: 0 auto clamp(2.5rem, 5vw, 4.5rem); max-width: 78rem; }
.process-head h2 { font-size: clamp(2.6rem, 5.2vw, 5.2rem); margin: .3rem 0 1.2rem; }
.process-head > p:last-child { color: rgba(232,232,228,.62); font-size: .86rem; line-height: 1.7; max-width: 26rem; }
.process-steps { display: grid; gap: 1.5rem; grid-template-columns: repeat(4, 1fr); list-style: none; margin: 0 auto; max-width: 88rem; padding: 0; }
.process-step { border-top: 1px solid rgba(232,232,228,.18); padding-top: 1.1rem; }
.process-image { aspect-ratio: 4 / 3.4; background-color: #17171a; background-position: center; background-size: cover; margin-bottom: 1.2rem; width: 100%; }
.step-no { color: #c3a470; display: block; font-family: var(--mono); font-size: .62rem; letter-spacing: .14em; margin-bottom: .5rem; }
.process-step h3 { font-family: var(--serif); font-size: 1.5rem; font-weight: 400; letter-spacing: -.02em; margin: 0 0 .55rem; }
.process-step p { color: rgba(232,232,228,.6); font-size: .78rem; line-height: 1.6; margin: 0; }

.finish-archive { background: #d9dad5; color: #151516; padding: clamp(3rem, 7vw, 7rem) clamp(1.2rem, 5vw, 5rem); }
.archive-heading { display: grid; grid-template-columns: 1fr 1fr; margin: 0 auto clamp(2.5rem, 6vw, 6rem); max-width: 75rem; }
.archive-heading .eyebrow { grid-column: 1 / -1; }
.archive-heading h2 { font-size: clamp(3.2rem, 6vw, 7rem); max-width: 8ch; }
.archive-heading > p:last-child { align-self: end; color: #585957; justify-self: end; margin: 0; }
.archive-samples { display: grid; grid-template-columns: repeat(6, 1fr); gap: 2px; margin: 0 auto; max-width: 92rem; }
.archive-sample { align-items: flex-start; background-color: #111; background-position: center; background-size: cover; border: 0; color: rgba(232,232,228,.9); cursor: pointer; display: flex; flex-direction: column; font-family: var(--mono); font-size: .55rem; gap: .35rem; justify-content: flex-end; letter-spacing: .08em; min-height: 19rem; overflow: hidden; padding: 1rem; position: relative; text-align: left; text-transform: uppercase; transition: transform .55s var(--ease); }
.archive-sample::before { background: linear-gradient(180deg, rgba(12,12,13,0) 40%, rgba(12,12,13,.82)); content: ""; inset: 0; position: absolute; transition: opacity .5s var(--ease); z-index: 0; }
.archive-sample span, .archive-sample b { position: relative; z-index: 1; }
.archive-sample b { color: #c3a470; font-size: .5rem; font-weight: 400; letter-spacing: .14em; }
.archive-sample:hover, .archive-sample:focus-visible { outline: 1px solid #c3a470; outline-offset: -1px; transform: translateY(-.6rem) scale(1.015); }

.consultation { display: grid; grid-template-columns: minmax(0, .85fr) minmax(0, 1.15fr); grid-template-rows: auto 1fr; gap: 2.2rem clamp(2.5rem, 5vw, 5rem); padding: clamp(4rem, 9vw, 10rem) clamp(1.2rem, 5vw, 6rem); position: relative; }
/* The line breaks are authored, so the measure is set by them rather than by a
   ch cap that would re-wrap "Something is" onto two lines in this column. Four
   lines of Bodoni at .9 leading put the ascenders of one line into the
   descenders of the one above; .96 clears it without loosening the block. */
.consultation h2 { line-height: .96; max-width: none; }
/* Left rail holds the invitation + the studio's hard constraints; the right
   rail is the single booking path. Explicit placement so the two stacked
   left blocks never wrap under the panel. */
.consultation-head { grid-column: 1; grid-row: 1; }
/* The booking column runs roughly 450px longer than the left rail, which left
   a void beside the form. The rail now carries the "what happens next" answer
   AND travels with the form, so the studio's terms stay beside the calendar
   the whole way down instead of scrolling off at the top. Sticky is scoped to
   its own grid row, so it can never overtake the booking column. */
.consultation-copy { align-self: start; grid-column: 1; grid-row: 2; position: sticky; top: clamp(2rem, 8vh, 5rem); }
.consultation-copy p { margin: 0 0 2rem; }
.booking { grid-column: 2; grid-row: 1 / span 2; position: relative; z-index: 2; }

/* The only two promises on the page. They get the display serif and sit above
   the mechanics, because they are the reason to act rather than a description
   of what acting does. Thin rules, no box: the weight comes from the type. */
.list-gets { margin-top: 2.6rem; }
/* .consultation-copy p beats the shared .mono-label rule on specificity, so the
   size has to be restated here, as it is for .next-steps below. */
.list-gets > .mono-label { color: var(--accent-text); display: block; font-size: .63rem; margin: 0 0 .3rem; }
.list-gets dl { display: grid; margin: 0; max-width: 21rem; }
.list-gets dl > div { border-bottom: 1px solid rgba(232,232,228,.14); padding: .9rem 0; }
.list-gets dt { color: var(--paper); font-family: var(--serif); font-size: 1.2rem; letter-spacing: -.01em; line-height: 1.2; margin-bottom: .3rem; }
.list-gets dd { color: rgba(232,232,228,.68); font-size: .78rem; line-height: 1.6; margin: 0; }

/* Same technical register as .studio-spec above it: thin rules, mono numerals,
   no boxes. */
.next-steps { margin-top: 2.6rem; }
/* .consultation-copy p beats the shared .mono-label rule on specificity, so the
   size has to be restated here or the kicker renders at body size. */
.next-steps > .mono-label { color: rgba(232,232,228,.55); display: block; font-size: .63rem; margin: 0 0 .3rem; }
.next-steps ol { list-style: none; margin: 0; max-width: 21rem; padding: 0; }
.next-steps li { border-bottom: 1px solid rgba(232,232,228,.14); display: grid; gap: .8rem; grid-template-columns: 1.6rem 1fr; padding: .85rem 0; }
.next-steps li span { color: #c3a470; font-family: var(--mono); font-size: .55rem; letter-spacing: .12em; padding-top: .18rem; }
.next-steps li p { color: rgba(232,232,228,.68); font-size: .78rem; line-height: 1.6; margin: 0; max-width: none; }

/* Studio spec: the constraints stated up front, in the same technical
   register as the hero's finish annotations. */
.studio-spec { border-top: 1px solid rgba(232,232,228,.14); display: grid; margin: 0; max-width: 21rem; }
.studio-spec > div { border-bottom: 1px solid rgba(232,232,228,.14); display: grid; gap: .8rem; grid-template-columns: 6.5rem 1fr; padding: .7rem 0; }
.studio-spec dt { color: rgba(232,232,228,.55); font-family: var(--mono); font-size: .55rem; letter-spacing: .12em; text-transform: uppercase; }
.studio-spec dd { color: rgba(232,232,228,.8); font-family: var(--mono); font-size: .6rem; letter-spacing: .05em; margin: 0; }

/* Two ways in, one form. Reserving a slot is the default path; the waiting
   list is the honest fallback when the studio is full. */
.booking-modes { border-bottom: 1px solid rgba(232,232,228,.14); display: flex; gap: 2rem; margin-bottom: 2.2rem; }
/* Vertical padding is a touch-target requirement, not decoration: these two
   labels are the entry point to the whole booking flow on a phone. */
.booking-mode { background: none; border: 0; border-bottom: 1px solid transparent; color: rgba(232,232,228,.45); cursor: pointer; font-family: var(--mono); font-size: .62rem; letter-spacing: .12em; margin-bottom: -1px; min-height: 2.75rem; padding: .95rem 0; text-transform: uppercase; transition: color .25s ease, border-color .25s ease; }
.booking-mode:hover { color: rgba(232,232,228,.82); }
.booking-mode.is-selected { border-bottom-color: #c3a470; color: var(--paper); }
.booking-mode:focus-visible { color: var(--paper); outline: 1px solid #c3a470; outline-offset: 4px; }

/* .enquiry-form / .booking-modes set display, which beats the [hidden] UA
   rule, so both need an explicit hidden state. */
.booking-form { max-width: none; }
.booking-form[hidden], .booking-modes[hidden] { display: none; }
.schedule { display: grid; gap: 2.4rem; margin-bottom: 2.4rem; }
.schedule[hidden] { display: none; }

.calendar-head { align-items: center; display: flex; gap: .5rem; margin-bottom: 1.1rem; }
.cal-month { flex: 1; font-family: var(--serif); font-size: 1.45rem; letter-spacing: -.02em; margin: 0; order: -1; }
.cal-nav { background: none; border: 1px solid rgba(232,232,228,.2); color: rgba(232,232,228,.72); cursor: pointer; font-family: var(--mono); font-size: .75rem; height: 2.75rem; transition: border-color .2s ease, color .2s ease; width: 2.75rem; }
.cal-nav:hover:not([disabled]) { border-color: #c3a470; color: var(--paper); }
.cal-nav:focus-visible { outline: 1px solid #c3a470; outline-offset: 2px; }
.cal-nav[disabled] { color: rgba(232,232,228,.22); cursor: not-allowed; }

.calendar-table { border-collapse: collapse; table-layout: fixed; width: 100%; }
/* Tertiary text in the picker was rgba(...,.34)-.4, which lands at 2.7:1 to
   3.3:1 on this base. At 8-10px that is not readable; .55 clears 4.5:1 and is
   still clearly subordinate to the .78+ primary text. */
.calendar-table th { color: rgba(232,232,228,.55); font-family: var(--mono); font-size: .52rem; font-weight: 400; letter-spacing: .14em; padding-bottom: .75rem; text-align: center; text-transform: uppercase; }
.calendar-table th abbr { text-decoration: none; }
.calendar-table td { border-top: 1px solid rgba(232,232,228,.1); padding: 0; }
.cal-day { align-items: center; background: none; border: 0; color: var(--paper); cursor: pointer; display: flex; font-family: var(--mono); font-size: .78rem; justify-content: center; letter-spacing: .04em; min-height: 3.3rem; transition: background .2s ease, color .2s ease; width: 100%; }
.cal-day:hover { background: rgba(232,232,228,.07); }
.cal-day:focus-visible { outline: 1px solid #c3a470; outline-offset: -1px; }
/* Unavailable dates still have to be readable: the visitor needs the number to
   orient in the month. .5 keeps them obviously weaker than an open date
   (4.8:1 against ~15:1) while staying legible. */
.cal-day[aria-disabled="true"] { color: rgba(232,232,228,.5); cursor: not-allowed; }
.cal-day[aria-disabled="true"]:hover { background: none; }
/* Fully booked reads differently from closed: struck through, not just dim,
   so the two unavailable states are told apart without relying on colour. */
.cal-day[data-state="full"] { text-decoration: line-through; text-decoration-thickness: 1px; }
/* aria-selected sits on the gridcell (the APG date-picker pattern), so the
   selected styling hangs off the cell, not the button. */
td[aria-selected="true"] .cal-day { background: #c3a470; color: #f7f3f1; }
td[aria-selected="true"] .cal-day:focus-visible { outline: 1px solid var(--paper); outline-offset: -4px; }
.cal-pad { cursor: default; pointer-events: none; }

.cal-legend { align-items: center; color: rgba(232,232,228,.55); display: flex; flex-wrap: wrap; font-family: var(--mono); font-size: .53rem; gap: .45rem; letter-spacing: .1em; margin: 1rem 0 0; text-transform: uppercase; }
.cal-legend .key { display: inline-block; flex: 0 0 auto; height: .55rem; position: relative; width: .55rem; }
.key-open { background: rgba(232,232,228,.78); }
.key-closed { background: rgba(232,232,228,.17); }
.key-full { background: rgba(232,232,228,.17); }
.key-full::after { background: rgba(232,232,228,.62); content: ""; height: 1px; left: -1px; position: absolute; right: -1px; top: 50%; }

/* The chosen date set large in the display serif: the one moment in the
   booking flow that is composed rather than functional. */
.chosen { font-family: var(--serif); font-size: clamp(1.9rem, 3vw, 2.6rem); letter-spacing: -.03em; line-height: 1; margin: 0; }
/* The empty state is an instruction, not decoration. At .26 it read as a ghost
   (2.0:1) rather than as the next thing to do. */
.chosen[data-empty="true"] { color: rgba(232,232,228,.42); }
.chosen-time { color: #c3a470; margin: .6rem 0 1.4rem; min-height: .95rem; }
.slot-grid { display: grid; gap: .5rem; grid-template-columns: repeat(3, 1fr); }
.slot { background: none; border: 1px solid rgba(232,232,228,.18); color: rgba(232,232,228,.86); cursor: pointer; display: grid; font-family: var(--mono); gap: .22rem; justify-items: start; min-height: 3.1rem; padding: .6rem .75rem; text-align: left; transition: background .2s ease, border-color .2s ease, color .2s ease; }
.slot-time { font-size: .74rem; letter-spacing: .05em; }
/* Carries the "Taken" state in words. It only does that job if it is readable,
   so it clears 4.5:1 in all three slot states. */
.slot-end { color: rgba(232,232,228,.55); font-size: .52rem; letter-spacing: .1em; }
.slot:hover { border-color: rgba(232,232,228,.5); }
.slot:focus-visible { outline: 1px solid #c3a470; outline-offset: 2px; }
.slot[aria-checked="true"] { background: #c3a470; border-color: #c3a470; color: #f7f3f1; }
.slot[aria-checked="true"] .slot-end { color: #f7f3f1; }
.slot[aria-checked="true"]:focus-visible { outline-color: var(--paper); }
.slot[aria-disabled="true"] { border-color: rgba(232,232,228,.14); color: rgba(232,232,228,.55); cursor: not-allowed; }
.slot[aria-disabled="true"]:hover { border-color: rgba(232,232,228,.14); }
.slot[aria-disabled="true"] .slot-time { text-decoration: line-through; text-decoration-thickness: 1px; }
.slot[aria-disabled="true"] .slot-end { color: rgba(232,232,228,.55); }
.slot-hint { color: rgba(232,232,228,.55); margin: 1rem 0 0; }

.waitlist-note { border-left: 1px solid #c3a470; color: rgba(232,232,228,.62); font-size: .82rem; line-height: 1.7; margin: 0 0 2.2rem; max-width: 34rem; padding-left: 1.1rem; }
.waitlist-note[hidden] { display: none; }

/* Shown above the mode control only when the whole window is closed out. Same
   accent left-rule family as the waitlist note, so the two read as one voice. */
.booking-lede { border-left: 1px solid #c3a470; color: rgba(232,232,228,.72); font-size: .84rem; line-height: 1.7; margin: 0 0 1.9rem; max-width: 34rem; padding-left: 1.1rem; }
.booking-lede[hidden] { display: none; }

/* The waiting list as a standing offer inside the booking path, not a tab.
   Set like a short editorial aside - thin rule, mono kicker, one serif line -
   so it reads as an invitation with its own dignity rather than as the
   consolation prize for a booking that just failed. */
.waitlist-offer { border-top: 1px solid rgba(232,232,228,.14); display: grid; gap: 0; justify-items: start; margin: 0 0 2.4rem; max-width: 34rem; padding-top: 1.5rem; }
.waitlist-offer[hidden] { display: none; }
.waitlist-offer > .mono-label { color: var(--accent-text); margin: 0; }
.waitlist-offer-title { font-family: var(--serif); font-size: clamp(1.5rem, 2.3vw, 2rem); letter-spacing: -.02em; line-height: 1.1; margin: .45rem 0 .5rem; }
.waitlist-offer-copy { color: rgba(232,232,228,.62); font-size: .8rem; line-height: 1.7; margin: 0 0 .5rem; }
.waitlist-offer-cta { background: none; border: 0; border-bottom: 1px solid rgba(232,232,228,.5); color: inherit; cursor: pointer; display: inline-flex; font-family: var(--mono); font-size: .64rem; gap: 1.2rem; letter-spacing: .1em; line-height: 1; margin-top: .4rem; min-height: 2.75rem; padding: .95rem 0 .85rem; text-transform: uppercase; transition: border-color .2s ease; }
.waitlist-offer-cta span { color: #c3a470; font-size: .95rem; line-height: 1; margin-top: -.12rem; }
.waitlist-offer-cta:hover { border-color: #c3a470; }
.waitlist-offer-cta:focus-visible { outline: 1px solid #c3a470; outline-offset: 4px; }
/* When the chosen day comes back fully booked the offer stops being a standing
   invitation and becomes the answer to what just happened: it moves onto the
   accent rule and speaks to that day by name. */
.waitlist-offer.is-urgent { border-left: 1px solid #c3a470; border-top: 0; padding: 0 0 0 1.1rem; }

/* What the visualizer handed down, shown at the top of the form so the visitor
   can see their own car travelling with them into it. Thumbnails, not a count:
   the photographs are the reason they are filling this in. */
.carried-car { border-bottom: 1px solid rgba(232,232,228,.14); border-top: 1px solid rgba(232,232,228,.14); margin-bottom: .3rem; padding: 1.1rem 0; }
.carried-car > .mono-label { color: rgba(232,232,228,.55); display: block; margin: 0 0 .8rem; }
.carried-row { align-items: center; display: flex; gap: 1.1rem; }
.carried-shots { display: flex; flex: 0 0 auto; gap: .35rem; }
.carried-shot { background-position: center; background-size: cover; box-shadow: 0 0 0 1px rgba(232,232,228,.2); display: block; height: 2.6rem; width: 3.4rem; }
.carried-shots:empty { display: none; }
.carried-copy { color: rgba(232,232,228,.68); font-size: .76rem; line-height: 1.55; margin: 0; }

.booking-confirmed { border-top: 1px solid rgba(232,232,228,.14); padding-top: 2.2rem; }
.booking-confirmed[hidden] { display: none; }
.booking-confirmed .chosen { margin-top: .5rem; }
/* Rendered only when the backend returns a real integer. Set in the display
   serif because a place on a list is the one number this page ever shows, and
   it should feel like a thing you were given rather than a receipt. */
.confirmed-position { align-items: baseline; display: flex; gap: .9rem; margin: 1.1rem 0 0; }
.confirmed-position[hidden] { display: none; }
.confirmed-position .mono-label { color: rgba(232,232,228,.55); }
.confirmed-position strong { color: var(--paper); font-family: var(--serif); font-size: clamp(1.6rem, 2.6vw, 2.2rem); font-weight: 400; letter-spacing: -.02em; line-height: 1; }
.confirmed-copy { color: rgba(232,232,228,.64); font-size: .84rem; line-height: 1.7; margin: 1.5rem 0 0; max-width: 32rem; }
.consultation-link { border-bottom: 1px solid rgba(232,232,228,.5); display: flex; font-family: var(--mono); font-size: .67rem; justify-content: space-between; letter-spacing: .1em; padding-bottom: .85rem; text-transform: uppercase; }
.consultation-link span { color: #c3a470; font-size: 1rem; }
/* The arrow is decorative and may be accent-coloured at 3.78:1 (non-text 3:1
   applies). The LABEL is the name of the only conversion on the page and was
   inheriting that same accent from the rule above - 3.78:1 on real text, on
   the submit button. It reads in paper, like every other button label here. */
.consultation-link .label { color: var(--paper); font-size: inherit; }
.consultation-link { background: none; border: 0; border-bottom: 1px solid rgba(232,232,228,.5); cursor: pointer; color: inherit; width: 100%; transition: border-color .2s ease; }
.consultation-link:hover, .consultation-link:focus-visible { border-color: #c3a470; }

.enquiry-form { display: grid; gap: 1.1rem; margin-top: .5rem; max-width: 30rem; }
/* align-content:start stops a field's rows stretching to match a taller
   sibling in the same row. Without it the make field's helper text made the
   whole row taller and the model input drifted half an input below the make
   input, so a pair of fields on one line no longer read as one line. */
.enquiry-form .field { align-content: start; display: grid; gap: .4rem; }
.enquiry-form .field-row { display: grid; gap: 1.1rem; grid-template-columns: 1fr 1fr; }
/* A year is four characters. Giving it half the row would make the pairing
   read as two equal questions when one is much smaller than the other. */
.enquiry-form .field-row.row-tight { grid-template-columns: minmax(0, 8rem) minmax(0, 1fr); }
/* The four car fields are one question asked four ways, so they are grouped as
   one: a named block in the same technical register as .studio-spec, not four
   more anonymous inputs. min-width:0 is load-bearing - a fieldset's UA
   min-inline-size:min-content otherwise pushes the grid wider than its column
   and reintroduces horizontal overflow on a phone. */
.enquiry-form .field-group { border: 0; margin: .5rem 0 0; min-width: 0; padding: 0; }
.enquiry-form .field-group legend { color: var(--accent-text); font-family: var(--mono); font-size: .56rem; letter-spacing: .14em; padding: 0; text-transform: uppercase; }
.field-group-body { border-top: 1px solid rgba(232,232,228,.14); display: grid; gap: 1.1rem; margin-top: .75rem; padding-top: 1.15rem; }
/* Persistent helper text, not a placeholder: the datalist is a shortcut, and a
   visitor whose marque is not on it needs to know that before they hesitate. */
.enquiry-form .field-hint { color: rgba(232,232,228,.6); font-family: var(--mono); font-size: .66rem; letter-spacing: .03em; line-height: 1.5; margin: 0; }
/* Spinners on a borderless underlined field read as debris, and the year is
   typed, not stepped. Arrow keys still increment either way. */
.enquiry-form input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.enquiry-form input[type="number"]::-webkit-outer-spin-button,
.enquiry-form input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; appearance: none; margin: 0; }
.enquiry-form label { color: rgba(232,232,228,.55); font-family: var(--mono); font-size: .56rem; letter-spacing: .12em; text-transform: uppercase; }
.enquiry-form label .optional { color: rgba(232,232,228,.32); text-transform: none; }
.enquiry-form select option[value=""] { color: #6b6b68; }
.enquiry-form input, .enquiry-form select, .enquiry-form textarea { background: transparent; border: 0; border-bottom: 1px solid rgba(232,232,228,.22); color: var(--paper); font-family: var(--sans); font-size: .82rem; padding: .5rem 0; transition: border-color .2s ease; width: 100%; }
.enquiry-form textarea { resize: vertical; }
/* The two half-triangles must sit SIDE BY SIDE to read as a chevron, so the
   gap between their positions has to equal their width. They were .15rem and
   .05rem apart - a 1.6px offset on 5.6px triangles - so they overlapped into a
   tiny asterisk/x, and the only <select> on the studio's only conversion form
   wore a broken glyph. Offset now equals background-size (.35rem), which is
   the shape this recipe was always meant to draw. */
.enquiry-form select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, rgba(232,232,228,.5) 50%), linear-gradient(135deg, rgba(232,232,228,.5) 50%, transparent 50%); background-position: right .5rem center, right .15rem center; background-repeat: no-repeat; background-size: .35rem .35rem, .35rem .35rem; padding-right: 1.2rem; }
.enquiry-form select option { background: #141416; color: var(--paper); }
.enquiry-form input::placeholder, .enquiry-form textarea::placeholder { color: rgba(232,232,228,.32); }
.enquiry-form input:focus, .enquiry-form select:focus, .enquiry-form textarea:focus { border-color: #c3a470; outline: none; }
/* Was `:invalid:not(:placeholder-shown)`, which flagged the required Name and
   Email as errors on page load (they have no placeholder, so the negation was
   always true). `:user-invalid` only matches after the visitor has actually
   left the field, which is the correct moment to validate. */
.enquiry-form input:user-invalid { border-color: #c3a470; }
.hp-field { height: 0 !important; left: -9999px; opacity: 0; padding: 0 !important; position: absolute; width: 0 !important; }
/* Sits immediately above the submit, which is the only place a consent line
   does any work. Quiet, but 5.7:1 - it has to be readable to count. */
.consent-note { color: rgba(232,232,228,.58); font-size: .74rem; line-height: 1.65; margin: 1.3rem 0 -.2rem; max-width: 34rem; }
.enquiry-form .consultation-link { margin-top: .6rem; padding-bottom: .85rem; }
.enquiry-form .consultation-link[disabled] { opacity: .5; pointer-events: none; }
.form-status { font-family: var(--mono); font-size: .6rem; letter-spacing: .06em; margin: .2rem 0 0; min-height: 1rem; text-transform: uppercase; }
.form-status.ok { color: #7fbf8f; }
.form-status.err { color: #d98189; }
/* Visualizer: upload three photos, choose a finish, get an AI-rendered
   preview. Positioned as the bridge between browsing finishes and booking. */
.visualizer { padding: clamp(4rem, 9vw, 8rem) clamp(1.2rem, 5vw, 5rem); position: relative; }
.visualizer-head { margin: 0 auto clamp(2.5rem, 5vw, 4rem); max-width: 42rem; }
.visualizer-head h2 { font-size: clamp(2.8rem, 5.6vw, 5.6rem); margin: .3rem 0 1.1rem; }
.visualizer-head > p:last-child { color: rgba(232,232,228,.62); font-size: .88rem; line-height: 1.7; max-width: 30rem; }

.visualizer-form { margin: 0 auto; max-width: 62rem; position: relative; z-index: 2; }
/* Three empty bordered rectangles with a plus in the middle is the templated-
   grid tell: the eye lands on box edges and the section reads vibe-coded. So
   these are not boxes. They are the same L-bracket mark the hero's upload zone
   already uses - two hairlines and a corner - which becomes a real frame only
   once a real photograph fills it edge to edge. */
.viz-uploads { display: grid; gap: clamp(1rem, 2.4vw, 2rem); grid-template-columns: repeat(3, 1fr); }
.viz-slot { align-items: flex-start; aspect-ratio: 3 / 2; border-bottom: 1px solid rgba(232,232,228,.45); border-left: 1px solid rgba(232,232,228,.45); cursor: pointer; display: flex; flex-direction: column; gap: .45rem; justify-content: flex-end; overflow: hidden; padding: 0 1rem 1rem; position: relative; text-align: left; transition: border-color .35s var(--ease); }
.viz-slot::after { border-right: 1px solid rgba(232,232,228,.45); border-top: 1px solid rgba(232,232,228,.45); content: ""; height: 2rem; position: absolute; right: 0; top: 0; transition: border-color .35s var(--ease); width: 2rem; }
.viz-slot:hover, .viz-slot:focus-within, .viz-slot:hover::after, .viz-slot:focus-within::after { border-color: #c3a470; }
/* The input is visually hidden but still in the tab order (it used to carry
   the `hidden` attribute, which took the whole visualizer off the keyboard).
   The label wears the focus ring on its behalf. */
.viz-file { height: 100%; left: 0; opacity: 0; position: absolute; top: 0; width: 100%; }
.viz-slot:focus-within { outline: 1px solid #c3a470; outline-offset: 3px; }
.viz-slot.is-dragover, .viz-slot.is-dragover::after { border-color: #c3a470; }
.viz-slot.has-file, .viz-slot.has-file::after { border-color: rgba(232,232,228,.6); }
.viz-slot-fill { background-position: center; background-size: cover; inset: 0; opacity: 0; position: absolute; transition: opacity .4s var(--ease); }
.viz-slot.has-file .viz-slot-fill { opacity: 1; }
.viz-slot-plus { color: rgba(232,232,228,.55); font-family: var(--serif); font-size: 2rem; font-weight: 300; line-height: 1; position: relative; transition: opacity .3s ease; z-index: 1; }
/* Was .42 on this base, which is 3.1:1 - under the floor for the only text
   that says what each mark is for. */
.viz-slot-label { color: rgba(232,232,228,.6); font-family: var(--mono); font-size: .58rem; letter-spacing: .1em; padding: 0; position: relative; text-transform: uppercase; transition: opacity .3s ease, color .3s ease; z-index: 1; }
.viz-slot.has-file .viz-slot-plus { opacity: 0; }
.viz-slot.has-file .viz-slot-label { background: rgba(8,8,9,.72); border-radius: 2px; color: rgba(232,232,228,.85); opacity: 0; padding: .35rem .7rem; }
.viz-slot.has-file:hover .viz-slot-label, .viz-slot.has-file:focus-within .viz-slot-label { opacity: 1; }
.viz-slot.has-file .viz-slot-label::before { content: "Change "; }

.viz-controls { display: grid; gap: 2.5rem; grid-template-columns: 1.5fr 1fr; margin-top: 2.2rem; }
/* .viz-email-wrap here and in the two media queries below is PARKED with the
   retired inline render: the visualizer no longer collects an email of its
   own, because the one form on the page does. Kept so restoring it is a paste. */
.viz-finishes-wrap .mono-label, .viz-email-wrap label { color: rgba(232,232,228,.55); display: block; margin-bottom: .8rem; }
.viz-finishes { display: grid; gap: .5rem; grid-template-columns: repeat(6, 1fr); }
.viz-finish { background: none; border: 0; cursor: pointer; display: grid; gap: .45rem; padding: 0; }
.viz-finish i { background-position: 30% 55%; background-repeat: no-repeat; background-size: 320%; box-shadow: 0 0 0 1px rgba(232,232,228,.18); display: block; height: 2.6rem; transition: box-shadow .3s var(--ease), transform .3s var(--ease); width: 100%; }
.viz-finish:hover i, .viz-finish:focus-visible i { transform: translateY(-.15rem); }
.viz-finish.is-selected i { box-shadow: 0 0 0 2px var(--paper); }
.viz-finish span { color: rgba(232,232,228,.5); font-family: var(--mono); font-size: .5rem; letter-spacing: .04em; line-height: 1.3; text-transform: uppercase; }
.viz-finish.is-selected span { color: var(--paper); }
/* Where the email field and "Render my car" used to be. The visualizer no
   longer submits: it carries the car down to the one form on the page, so this
   column states what is about to happen and then does it. */
.viz-onward { align-self: end; }
.viz-onward-copy { color: rgba(232,232,228,.62); font-size: .8rem; line-height: 1.7; margin: 0 0 1.4rem; }
.viz-submit { margin-top: 0; }
.viz-submit[disabled] { opacity: .5; pointer-events: none; }
.visualizer-form .form-status { margin-top: 1.3rem; }

/* The escape hatch from the highest-intent action on the page. It sits outside
   the upload block on purpose: it has to survive half-filled photos, a photo
   that will not read, and a visitor who simply does not want to upload
   anything. The list is never gated behind the render. */
.viz-bridge { border-top: 1px solid rgba(232,232,228,.14); color: rgba(232,232,228,.68); font-size: .84rem; line-height: 1.7; margin: 2.4rem auto 0; max-width: 62rem; padding-top: 1.6rem; position: relative; z-index: 2; }
.viz-bridge .text-link { display: inline-flex; gap: 1.2rem; margin-left: .6rem; vertical-align: baseline; }

/* PARKED. The inline render frame was retired with the on-page render: while
   /api/visualize has no backend, a frame that says "your car, here" and stays
   empty is exactly the dead end this rebuild exists to remove. The render now
   travels back by email. Markup for the frame is in git at commit a763908;
   these rules are kept so restoring it is a paste, not a redesign. */
.viz-result { margin: 3rem auto 0; max-width: 46rem; position: relative; text-align: center; z-index: 2; }
.viz-result .mono-label { color: rgba(232,232,228,.5); }
.viz-result .mono-label span { color: #c3a470; }
.viz-result-frame { aspect-ratio: 4 / 3; background: rgba(232,232,228,.04); margin: 1rem 0 1.5rem; overflow: hidden; }
.viz-result-frame img { display: block; height: 100%; object-fit: cover; width: 100%; }
.viz-result-copy { color: rgba(232,232,228,.68); font-size: .86rem; }
.viz-result-copy .text-link { display: inline-flex; margin-left: .4rem; }

/* ===========================================================================
   THE STANDING RAIL. The studio's state and its only conversion, carried for
   the whole scroll instead of being repeated as another CTA block. Held back
   until the hero has left the screen so the fold stays composed. Hairline rule
   and mono micro-type, no pill and no shadow: an instrument reading, not a
   cookie banner.
   =========================================================================== */
.standing-bar { align-items: center; backdrop-filter: blur(12px); background: rgba(10,10,11,.88); border-top: 1px solid rgba(232,232,228,.2); bottom: 0; display: flex; gap: 1.5rem; justify-content: space-between; left: 0; opacity: 0; padding: .35rem clamp(1.2rem, 4vw, 4.5rem); position: fixed; right: 0; transform: translateY(100%); transition: transform .55s var(--ease), opacity .3s ease, visibility .3s; visibility: hidden; z-index: 15; }
.standing-bar[hidden] { display: none; }
.standing-bar.is-visible { opacity: 1; transform: none; visibility: visible; }
.standing-state { align-items: center; color: rgba(232,232,228,.75); display: flex; font-family: var(--mono); font-size: .58rem; gap: .6rem; letter-spacing: .14em; margin: 0; text-transform: uppercase; }
.standing-dot { background: #c3a470; display: block; flex: 0 0 auto; height: .4rem; width: .4rem; }
/* 2.75rem tall so the only persistent CTA on the page clears the touch floor. */
.standing-cta { align-items: center; border-bottom: 1px solid rgba(232,232,228,.45); display: inline-flex; font-family: var(--mono); font-size: .6rem; gap: 1rem; letter-spacing: .12em; min-height: 2.75rem; padding: .55rem 0 .5rem; text-transform: uppercase; transition: border-color .2s ease; white-space: nowrap; }
.standing-cta:hover, .standing-cta:focus-visible { border-color: #c3a470; }
.standing-cta span { color: #c3a470; font-size: .9rem; }
/* The rail is fixed, so the last of the footer would otherwise sit under it. */
body.has-standing-bar footer { padding-bottom: 5rem; }

footer { align-items: center; border-top: 1px solid var(--line); color: rgba(232,232,228,.58); display: grid; grid-template-columns: auto 1fr auto auto; gap: 2rem; padding: 1.7rem clamp(1.2rem, 4vw, 4.5rem); }
footer p { margin: 0; }
/* The name, quietly explained. Invictus = Latin, unconquered. */
.footer-meaning { color: rgba(232,232,228,.55); letter-spacing: .08em; text-transform: none; }
.footer-meaning span { font-family: var(--serif); font-size: .8rem; letter-spacing: .02em; margin-right: .1rem; }
/* Copyright + trademark filing status, stacked in the footer's last grid
   cell rather than added as a fifth column - keeps the existing four-column
   template untouched. */
.footer-legal { display: flex; flex-direction: column; gap: .35rem; }
.footer-trademark { color: rgba(232,232,228,.42); }

@media (max-width: 900px) {
  /* Keep form fields at 16px+ so iOS Safari doesn't auto-zoom on focus. The
     visualizer's email input was missed by the original rule and sat at
     14.08px, which zooms the viewport on focus and looks broken. */
  .enquiry-form input, .enquiry-form select, .enquiry-form textarea,
  .viz-email-wrap input { font-size: 1rem; }
  /* 36-38px controls on the primary booking path. Lift the submit and the
     fields to the 44px touch-target floor. */
  .enquiry-form input, .enquiry-form select, .enquiry-form textarea,
  .viz-email-wrap input { padding: .7rem 0; }
  .consultation-link { min-height: 2.75rem; padding-bottom: 1rem; }
  .text-link { padding: .95rem 0; }
  /* The left rail is above the form once the section stacks, so sticky would
     park the studio terms on top of the calendar. */
  .consultation-copy { position: static; top: auto; }
  .site-header { display: flex; align-items: center; justify-content: space-between; }
  .header-cta { display: none; }
  .nav-toggle { display: block; flex: 0 0 auto; }
  nav {
    /* Fully opaque, not rgba(10,10,11,.97). At 97% the hero headline read
       straight through the open panel, which is a legibility defect on the
       only navigation a phone has - and that nav now carries the single CTA
       this whole page exists to serve. There is nothing behind this panel the
       visitor needs to see while it is open. */
    background: #0c0c0d;
    border-top: 1px solid var(--line);
    flex-direction: column;
    gap: 0;
    left: 0;
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    padding: 0 clamp(1.2rem, 4vw, 4.5rem);
    position: absolute;
    right: 0;
    top: 100%;
    transition: max-height .35s ease, opacity .25s ease, padding .35s ease;
    visibility: hidden;
  }
  nav.is-open { max-height: 22rem; opacity: 1; padding: 1rem clamp(1.2rem, 4vw, 4.5rem) 1.6rem; visibility: visible; }
  nav a { font-size: .82rem; padding: .95rem 0; width: 100%; }
  nav a + a { border-top: 1px solid rgba(232, 232, 228, 0.1); }
  /* The action, given the accent and the last word in the drawer, so the menu
     ends on something to do rather than on the fourth place to look. */
  .nav-cta { color: #c3a470; display: block; }
  .nav-cta:hover, .nav-cta:focus-visible { color: #d9bd8b; }
  /* Hero: flowed stack instead of absolute-positioned overlay, so nothing
     overlaps and the section height matches its real content. */
  .hero {
    display: flex;
    flex-direction: column;
    gap: 1.3rem;
    min-height: 0;
    overflow: visible;
    padding: clamp(5.2rem, 13vw, 6.5rem) clamp(1.2rem, 4vw, 2rem) 2.5rem;
  }
  .hero::after { display: none; }
  .hero-copy { max-width: none; order: 1; padding-top: 0; }
  h1 { max-width: none; }
  .car-stage {
    aspect-ratio: 4 / 3;
    bottom: auto;
    inset: auto;
    order: 2;
    position: relative;
    width: 100%;
  }
  .car-stage img { object-position: 60% center; }
  .annotation-code, .annotation-sheen, .annotation-view { display: none; }
  .hero-status { bottom: auto; left: auto; order: 3; position: static; }
  .finish-rail {
    background: none;
    border-left: 0;
    bottom: auto;
    order: 4;
    padding: 0;
    position: static;
    top: auto;
    width: 100%;
  }
  .finish-options { grid-template-columns: repeat(6, 1fr); gap: .35rem; margin-top: 0; }
  .finish-option { grid-template-columns: 1fr; }
  .finish-option span { display: none; }
  /* Was 30px tall. These six swatches are the hero's only interaction on a
     phone, so they get the 44px touch-target floor. */
  .sample { height: 2.75rem; width: 100%; }
  .finish-rail > a, .rail-heading { display: none; }
  .upload-zone {
    bottom: auto;
    min-height: 3.8rem;
    order: 5;
    position: static;
    width: 100%;
  }
  .manifesto, .craft { min-height: 0; }
  .manifesto { grid-template-columns: .35fr 1.35fr; }
  .manifesto-copy { grid-column: 1 / -1; grid-row: 2; padding-top: 0; }
  .manifesto-image { min-height: 22rem; }
  .transformation { grid-template-columns: 1fr; }
  .transition-car { min-height: 20rem; }
  .service-index { grid-column: 1; justify-self: stretch; margin-top: 0; width: auto; }
  /* Craft: the desktop negative-margin overlap between craft-main and
     craft-details doesn't translate to a narrow single-column layout, so
     every child gets an explicit stacked placement instead of relying on
     grid auto-flow (which was silently opening an extra implicit row). */
  .craft { grid-template-columns: 1fr; }
  .chapter-mark { grid-column: 1; }
  .craft-main { grid-column: 1; min-height: 24rem; }
  .craft-details { align-self: auto; display: grid; grid-column: 1; grid-template-columns: 1fr 1fr; margin-left: 0; padding: 1.2rem; }
  .process-steps { grid-template-columns: repeat(2, 1fr); gap: 1.5rem 1.2rem; }
  .craft-copy { align-self: auto; grid-column: 1; }
  .archive-heading { grid-template-columns: 1fr; gap: 1.5rem; }
  .archive-heading > p:last-child { justify-self: start; }
  .archive-samples { grid-template-columns: repeat(3, 1fr); }
  .archive-sample { min-height: 13rem; }
  .consultation { grid-template-columns: 1fr; gap: 2rem; }
  .consultation-head, .consultation-copy, .booking { grid-column: 1; grid-row: auto; }
  .studio-spec { max-width: none; }
  .cal-day { min-height: 3rem; }
  footer { grid-template-columns: 1fr; gap: .8rem; }
  .viz-uploads { gap: .9rem; grid-template-columns: repeat(3, 1fr); }
  .viz-slot { aspect-ratio: 1 / 1; padding: 0 .6rem .7rem; }
  .viz-slot::after { height: 1.2rem; width: 1.2rem; }
  .viz-slot-plus { font-size: 1.5rem; }
  .viz-slot-label { font-size: .52rem; }
  .viz-controls { grid-template-columns: 1fr; }
  .viz-finishes { grid-template-columns: repeat(6, 1fr); }
  /* The link wraps onto its own line at this width, where the inline left
     margin reads as a stray indent. */
  .viz-bridge .text-link { margin-left: 0; }
}

@media (max-width: 560px) {
  .site-header { padding-top: 1.2rem; }
  .header-cta { font-size: .52rem; }
  h1 { font-size: clamp(2.5rem, 11.5vw, 3.2rem); }
  .hero { gap: 1.1rem; padding-top: 5.5rem; }
  .hero-intro { font-size: .78rem; max-width: 17rem; }
  .hero-status { font-size: .58rem; }
  .manifesto { grid-template-columns: .25fr 1fr; }
  .chapter-mark { font-size: 5rem; padding: 1.5rem .6rem; }
  .manifesto-image { min-height: 18rem; }
  .manifesto-copy { padding: 1.5rem 1.2rem 2.25rem; }
  .craft-main { min-height: 20rem; }
  .craft-details { gap: .8rem; padding: 1rem; }
  .craft-copy { padding: 1.25rem 1.2rem 2.25rem; }
  .transition-car { min-height: 16rem; }
  .consultation { padding: 3rem 1.2rem; }
  .process-steps { grid-template-columns: 1fr; }
  /* Was `.field-row`, which loses to `.enquiry-form .field-row` on specificity,
     so this never applied and the fields stayed two-up at 160px each. A real
     email needed 338px and was cut off mid-address. `.row-tight` is listed
     explicitly for the same reason: it carries an extra class and would
     otherwise outrank this rule and keep the year 128px wide next to a
     truncated colour field. */
  .enquiry-form .field-row,
  .enquiry-form .field-row.row-tight { grid-template-columns: 1fr; }
  .waitlist-offer-title { font-size: 1.5rem; }
  .booking-modes { gap: 1.4rem; }
  .booking-mode { font-size: .58rem; }
  .cal-month { font-size: 1.2rem; }
  .cal-nav { height: 2.75rem; width: 2.75rem; }
  .cal-day { font-size: .72rem; min-height: 2.9rem; }
  .slot-grid { grid-template-columns: repeat(2, 1fr); }
  .studio-spec > div { grid-template-columns: 5.5rem 1fr; }
  /* The status line has three parts and 352px of usable width. Dropping the
     place onto its own line keeps "The studio is not open yet" - the part that
     carries the whole page - unbroken and first. */
  .prelaunch-status { font-size: .55rem; gap: .45rem .8rem; letter-spacing: .1em; }
  .status-place { margin-left: 0; }
  .standing-bar { gap: .9rem; padding-left: 1.2rem; padding-right: 1.2rem; }
  .standing-state { font-size: .52rem; letter-spacing: .1em; }
  .standing-cta { font-size: .55rem; gap: .7rem; letter-spacing: .1em; }
  /* Thumbnail strip plus a sentence will not sit on one 352px line. */
  .carried-row { align-items: flex-start; flex-direction: column; gap: .8rem; }
}

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