/* larrychou.com — shared styles
   Follows BRAND-GUIDELINES.md exactly. Do not add colors or fonts.
   Two-background rule: every section field is Mallard or Soft White. Nothing else. */

:root {
  color-scheme: light;

  /* Palette (BRAND-GUIDELINES.md) */
  --mallard:    #294E4F;  /* primary dark / anchor */
  --paper:      #F2EFE9;  /* soft white, sun-stable */
  /* THE SYSTEM: white is the ground. Cream and mallard are brand colours that
     sit ON it as panels and cards, never as the page itself.
       page            -> white
       card on white   -> cream   (warmth arrives as a surface, not a field)
       panel           -> mallard or ink, inset with white showing around it
       card on mallard -> white   (maximum separation against the dark panel)
     TEXT is only ever full-strength ink or full white. Cream is a surface
     colour, never a text colour: as type it reads as washed out, and no text
     on this site is set at reduced opacity.
     A card nested inside another surface inverts against its parent, so no two
     touching planes ever share a value. */
  --surface:    #FFFFFF;
  /* Terracotta (#B96A46) and Burgundy (#6E2E38) exist in the brand system but
     are deliberately unused on this site. Palette here is Mallard, Soft White,
     Harvest Gold, Ink. Do not reintroduce them. */
  --gold:       #A9853F;  /* star, antique, never shiny */
  --ink:        #111111;  /* near-black, body on light */

  /* Hairlines are alpha on the two anchors, not separate greys, so the page
     never introduces a hue outside the four. */
  --rule:       rgba(17,17,17,0.17);      /* ink on paper */
  --rule-dark:  rgba(242,239,233,0.22);   /* paper on mallard */

  --sans: "Cabin", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* Was Oswald. Dropped site-wide: it is a condensed face, and condensed
     letterforms set uppercase and tracked out are exactly what kept reading as
     hard to read at label sizes. Cabin is the body face, so labels now differ
     from body copy by case, weight and tracking rather than by a second
     typeface fighting the first. */
  --desc: "Cabin", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  /* Alfa Slab One is already in the brand font set (it sets the "16" in the
     16DayPrep lockup). Used here for display headlines so the page carries the
     same 50s/60s weight as the wordmark instead of reading as neutral UI type. */
  --display: "Alfa Slab One", Rockwell, Georgia, serif;

  --measure: 36rem;
  /* The page's side inset. Kept separate from --gutter because .gallery
     redefines --gutter locally for its arrow rails, so anything that needs to
     negate the PAGE inset (the mobile full-bleed rules) has to read a token no
     component shadows. */
  --page-gutter: clamp(20px, 5vw, 40px);
  --gutter:  var(--page-gutter);
  --r-card: 0px;   /* squared off: no rounded rectangles, squares or photos */
  --r-pill: 999px;
}

/* Force light. The palette has no dark inversion. */
@media (prefers-color-scheme: dark) {
  html, body { background-color: var(--surface) !important; color: var(--ink) !important; }
}

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

html {
  background-color: var(--paper);
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background-color: var(--surface);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 19px;
  line-height: 1.75;
  font-weight: 500;
  -webkit-font-smoothing: antialiased;
}

p { max-width: var(--measure); }

h1, h2 {
  font-family: var(--display);
  font-weight: 400;
  line-height: 1.05;
  letter-spacing: -0.005em;
  margin: 0 0 0.3em;
}
h3 {
  font-family: var(--sans);
  font-weight: 700;
  line-height: 1.25;
  margin: 0 0 0.5em;
}

/* Interior pages. Alfa Slab at 60px is a hero voice: on a services list or a
   case study full of numbers it drowns out the content underneath it. The
   home hero keeps the big size, see .hero h1 below. */
h1 { font-size: clamp(30px, 4.4vw, 44px); }
h2 { font-size: clamp(24px, 2.8vw, 33px); }
h3 { font-size: clamp(19px, 2.2vw, 21px); }

/* Descriptor style: eyebrows and small-caps rows.
   Tracking stays open enough to read as a label, but .42em was so wide the
   words stopped holding together. */
.desc {
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  padding-left: .14em;
  font-size: 16px;
  color: var(--ink);
}

a { color: var(--mallard); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }

.wrap {
  width: 100%;
  max-width: 760px;
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Wider column for image grids, so three frames sit in one row
   instead of orphaning the third. */
.wrap-wide  { max-width: 1120px; }
.wrap-wider { max-width: 1340px; }

section { padding-block: clamp(16px, 2.2vw, 26px); }

hr { border: 0; border-top: 1px solid var(--rule); margin-block: 2.5rem; }

/* ---- header ----
   Positioned with a z-index so the hero's absolutely-positioned media can
   never paint over it, whatever the stacking order downstream. */
.site-header {
  position: relative;
  z-index: 100;
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  padding-block: 14px;
}

/* Rides below the hero at rest, then pins to the top on scroll. Only once
   there is room to flank the mark; a stacked bar would eat a phone screen. */
@media (min-width: 780px) {
  .site-header { position: sticky; top: 0; }
}
/* ---- DESKTOP (>= 780px) ----
   Nav splits either side of the lockup. .nav-panel is display:contents so the
   two navs become grid items directly; explicit columns put the mark between
   them regardless of DOM order. */
@media (min-width: 780px) {
  .site-header .wrap {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: clamp(18px, 3vw, 40px);
    /* Header spans the wide column, not the 760px prose column, so five
       links at 18px flank the mark without wrapping. */
    max-width: 1340px;
  }
  .nav-panel { display: contents; }
  /* grid-row is load-bearing. The brand follows both navs in the DOM, so
     without it auto-placement drops the mark onto a second row. */
  .nav-left  { grid-area: 1 / 1; justify-content: flex-end; }
  .brand     { grid-area: 1 / 2; }
  .nav-right { grid-area: 1 / 3; justify-content: flex-start; }
}

/* ---- MOBILE (< 780px) ----
   Compact bar: mark left, Menu button right. Links live in a full-screen
   mallard panel that slides down. */
@media (max-width: 779px) {
  .site-header .wrap {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
  }
  .site-header .brand img { width: 128px; }

  .nav-panel {
    position: fixed;
    inset: 0;
    z-index: 200;
    background: var(--mallard);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 4px;
    transform: translateY(-100%);
    transition: transform .34s cubic-bezier(.4,0,.2,1);
    overflow-y: auto;
  }
  body.nav-open .nav-panel { transform: none; }
  body.nav-open { overflow: hidden; }

  .nav-panel .site-nav { flex-direction: column; align-items: center; gap: 4px; }
  .nav-panel .site-nav a {
    display: block;
    font-family: var(--display);
    font-weight: 400;
    font-size: clamp(30px, 8vw, 44px);
    line-height: 1.5;
    color: var(--surface);
    padding: 6px 10px;
  }
  .nav-panel .site-nav a:hover { color: var(--gold); }
  /* var(--ink) is the header treatment and is unreadable on mallard. */
  .nav-panel .site-nav a[aria-current="page"] { color: var(--gold); text-decoration-color: var(--gold); }
  .nav-panel .nav-mark .mark-dark  { display: none; }
  .nav-panel .nav-mark .mark-light { display: block; }
  .nav-panel .nav-mark img { height: auto; width: clamp(150px, 42vw, 220px); }
}

/* ---- menu button ---- */
.nav-toggle {
  display: flex;
  align-items: center;
  gap: 12px;
  position: relative;
  z-index: 300;   /* stays clickable above the open panel */
  background: none;
  border: 0;
  padding: 6px 2px;
  cursor: pointer;
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .18em;
  font-size: 15px;
  color: var(--mallard);
}
.nav-toggle-bars {
  display: block;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--mallard);
  position: relative;
  flex: none;
}
.nav-toggle-bars i {
  position: absolute;
  left: 9px;
  width: 16px;
  height: 2px;
  background: var(--paper);
  transition: transform .28s ease, opacity .2s ease;
}
.nav-toggle-bars i:first-child { top: 14px; }
.nav-toggle-bars i:last-child  { top: 19px; }

/* The header sits below the hero, so the button's natural position can be
   anywhere on screen when the panel opens. Pin it to a predictable corner. */
body.nav-open .nav-toggle {
  position: fixed;
  top: 18px;
  right: var(--gutter);
}

body.nav-open .nav-toggle { color: var(--surface); }
body.nav-open .nav-toggle-bars { background: var(--gold); }
body.nav-open .nav-toggle-bars i { background: var(--ink); }
body.nav-open .nav-toggle-bars i:first-child { transform: translateY(2.5px) rotate(45deg); }
body.nav-open .nav-toggle-bars i:last-child  { transform: translateY(-2.5px) rotate(-45deg); }

.nav-toggle:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }

/* Must sit after the base rule above, or it loses on source order. */
@media (min-width: 780px) {
  .nav-toggle { display: none; }
}
/* Uses the -tight variants: same artwork, viewBox cropped to the ink. The
   masters carry ~37% horizontal and ~52% vertical padding, which made the
   wordmark render far smaller than its box suggested. */
.site-header .brand { display: block; line-height: 0; }
.site-header .brand img { width: clamp(132px, 13vw, 168px); height: auto; }

.site-nav { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px 26px; font-size: 18px; }
.site-nav a { text-decoration: none; color: var(--mallard); font-weight: 600; }
.site-nav a:hover, .site-nav a[aria-current="page"] { color: var(--ink); text-decoration: underline; }

/* The Prep Plan nav item is the 16DayPrep mark. Sized to the cap height of the
   sibling links so it sits on the same optical line, not underlined on hover
   (a rule under a logo reads as an artefact). */
.nav-mark { display: inline-flex; align-items: center; line-height: 1; }
.nav-mark img { height: 1.5em; width: auto; display: block; }
/* The dark wordmark is drawn for the paper header. The mobile panel is
   mallard, where it vanished down to just its gold star, so the knockout
   version takes over there. */
.nav-mark .mark-light { display: none; }
.nav-mark:hover, .nav-mark[aria-current="page"] { text-decoration: none; opacity: .72; }

/* Page title lockup: the mark reading as the words, followed by "Plan". */
.h1-mark { display: flex; align-items: baseline; flex-wrap: wrap; gap: .28em; }
.h1-mark img { height: .95em; width: auto; display: block; transform: translateY(.07em); }

/* Page intro. The title, deck, and the "5 steps" lead-in live in ONE block:
   as two sections they each carried their own padding, so a handful of lines
   sprawled down a mostly empty cream slab. */
.page-intro { padding-block: clamp(22px, 2.8vw, 34px) clamp(18px, 2.2vw, 26px); }
.page-deck {
  font-size: clamp(18px, 1.7vw, 21px);
  line-height: 1.4;
  color: var(--mallard);
  max-width: 34rem;
  margin: 10px 0 0;
}
/* The lead-in follows the deck closely; it is the same thought, not a new one. */
.page-intro h2 { margin-top: clamp(20px, 2.4vw, 30px); }
.page-intro h2 + p { margin-top: 8px; }

/* ---- buttons ---- */
/* Every button is gold. One colour = one signal: gold means "you can click
   this," and it holds on both the paper and the mallard fields so the meaning
   never depends on context. Ink type on gold clears AA. */
.btn, .btn-gold {
  display: inline-block;
  /* Mallard, not gold. Gold stays the accent (rules, bullets, the star); the
     action is the brand colour. On a mallard or ink panel it inverts to white
     so the button never sits on its own value. */
  background: var(--mallard);
  color: var(--surface);
  font-family: var(--sans);
  font-weight: 700;
  font-size: 20px;
  letter-spacing: .01em;
  padding: 19px 40px;
  border-radius: var(--r-pill);
  text-decoration: none;
  border: 2px solid var(--mallard);
  transition: background .2s ease, color .2s ease, border-color .2s ease;
}
/* Hover darkens the gold rather than changing hue, so the signal survives. */
.btn:hover, .btn-gold:hover { background: #1C3839; color: var(--paper); border-color: #1C3839; }

/* Inverted on the dark panels. */
.band-dark .btn, .band-close .btn {
  background: var(--surface);
  color: var(--ink);
  border-color: var(--surface);
}
.band-dark .btn:hover, .band-close .btn:hover {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--paper);
}

/* Gold reads on the mallard field too, so no per-field override. */

/* ---- bands: mallard only, per the two-background rule ---- */
/* Inset, not edge to edge. A flat colour running the full browser width reads
   as a section picker; held in from the edges with the cream showing around it,
   the same colour reads as a deliberate panel. The hero stays full bleed (it is
   the image) and so does the footer (it is the ground the page sits on). */
.band-dark {
  background: var(--mallard);
  color: var(--surface);
  padding-block: clamp(34px, 4.4vw, 62px);
  margin-inline: clamp(12px, 2.6vw, 44px);
  border-radius: 0;
}
.band-dark h2, .band-dark h3 { color: var(--surface); }
.band-dark a:not(.btn) { color: var(--surface); }
.band-dark .desc { color: var(--surface); }

/* ---- star ---- */
.star { width: 15px; height: 15px; display: inline-block; vertical-align: -2px; }

/* ---- forms ---- */
label { display: block; font-size: 18px; font-weight: 600; margin-bottom: 6px; }
input, textarea, select {
  width: 100%;
  font-family: var(--sans);
  font-size: 19px;
  font-weight: 500;
  padding: 14px 15px;
  background: #FFFFFF;
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 0;
  margin-bottom: 18px;
}
input:focus, textarea:focus { outline: 2px solid var(--mallard); outline-offset: 1px; }

/* ---- footer ---- */
.site-footer {
  background: var(--mallard);
  color: var(--surface);
  line-height: 1.6;
  /* the closing CTA band is also mallard, so without this rule the two merge
     into one block and the CTA loses its edge */
  border-top: 3px solid var(--gold);
  padding-block: 18px;
  font-size: 17px;
  line-height: 1.8;
}
@media (min-width: 760px) {
  /* Two columns: logo over the brokerage line on the left, contact block
     pinned bottom-right. align-items:end sits both rows on the baseline. */
  .site-footer .wrap {
    display: grid;
    grid-template-columns: 1fr auto;
    column-gap: clamp(30px, 5vw, 70px);
    align-items: end;
  }
  .site-footer img { grid-column: 1; grid-row: 1; }
  .site-footer .legal {
    grid-column: 1; grid-row: 2;
    /* start, not end: row 2 is as tall as the 3-line contact block opposite,
       so bottom-aligning left the brokerage line floating far below the logo */
    align-self: start; text-align: left; margin: 0;
  }
  .site-footer .wrap > p:not(.legal) {
    /* spans both rows so it starts level with the logo. Confined to row 2 it
       sat below the logo's height, which read as the right side hanging low. */
    grid-column: 2; grid-row: 1 / span 2;
    align-self: start; text-align: right; margin: 0;
  }
}
.site-footer a { color: var(--surface); }
.site-footer .legal { font-size: 15px; margin-top: 10px; }
.site-footer img { width: 158px; margin-bottom: 4px; }

img { max-width: 100%; height: auto; display: block; }

table { width: 100%; border-collapse: collapse; }
.scroll-x { overflow-x: auto; }

/* ---- home ---- */
/* Full-bleed hero: the before/after IS the hero, copy sits on top of it. */
.hero {
  position: relative;
  background: var(--mallard);
  color: var(--surface);
  /* Phones are narrow, so a tall hero forces object-fit to crop the sides hard.
     Keep it shorter here and let it grow once there is width to fill. */
  min-height: clamp(420px, 62vh, 560px);
  display: flex;
  align-items: center;
  overflow: hidden;
  /* Copy is flex-centred, so the heavier bottom padding lifts it above the
     midline of the frame. */
  padding-top: clamp(30px, 4vw, 46px);
  padding-bottom: clamp(100px, 15vw, 190px);
}
.hero .wrap { position: relative; z-index: 2; max-width: 1340px; }
/* Centred: the hero is a hook, and hooks get centred. Arguments below stay
   left-aligned. Width is set so "Hand us the keys." holds one line at 78px. */
.hero-copy {
  max-width: min(47rem, 100%);
  margin-inline: auto;
  text-align: center;
}
/* The global p rule caps paragraphs at 34rem, which left-anchored the lede
   box inside the wider centred block. Centre the box itself, not just its text. */
.hero-copy p { max-width: none; margin-inline: auto; }
.hero .lede { margin-bottom: 0; }

/* Gold measures 1.03:1 against the after frame, so it disappears once the
   scrim is gone. Paper at 19px bold clears the large-text threshold. */
.hero-copy .desc { color: var(--surface); font-size: 19px; }

/* The spine and the CTA moved out from over the photo. They sit on paper
   directly beneath it, where body copy belongs. */
.intro { padding-block: clamp(20px, 2.6vw, 32px); }
.intro h2 { max-width: 20ch; }
.intro .spine { margin-bottom: 2rem; }

/* ---- what we handle / what you handle ---- */
.handles {
  display: grid;
  gap: clamp(16px, 2vw, 24px);
  margin-top: 1.2rem;
}
@media (min-width: 720px) {
  /* Weighted, not equal. Six bullets against two in matched columns is the
     thing that reads as a template: the grid ignores what is in it. The wide
     column takes the long list, the short one drops down the page so the two
     cards do not start on the same line. */
  .handles { grid-template-columns: 1.45fr 1fr; align-items: start; }
  .handles > div:last-child { margin-top: clamp(28px, 4vw, 64px); }
  .hero { min-height: clamp(600px, 88vh, 900px); }
}
/* Paper cards on the mallard band, ink text: same readability move as the
   service tiers. Loose lists on a dark field were hard to scan. */
.handles > div {
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-card);
  /* Tighter than the standard card inset: these are short scannable lines, and
     the padding was eating more width than the text was using. */
  padding: clamp(16px, 1.8vw, 22px) clamp(16px, 1.8vw, 20px);
}
.handles .desc {
  color: var(--ink);
  font-size: 15px;
  padding-bottom: 12px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--rule);
}
/* No bullets. The gold dot cost 26px of indent on every line and the list is
   already unmistakably a list. Rows are separated by a hairline instead, which
   reads at a glance and takes no horizontal room. */
.handles ul { list-style: none; padding: 0; margin: 0; }
.handles li {
  padding: 9px 0;
  max-width: none;
  border-top: 1px solid var(--rule);
}
.handles li:first-child { border-top: 0; padding-top: 0; }
.handles li:last-child { padding-bottom: 0; }
/* A proof point, not a footnote. The two boxes above are already paper cards,
   so this is set as a pull quote on the mallard field to stand apart from them
   rather than becoming a third card. */
.handles-note {
  margin-top: clamp(1.8rem, 3vw, 2.6rem);
  padding: 2px 0 2px clamp(16px, 1.8vw, 24px);
  border-left: 4px solid var(--gold);
  border-top: 0;
  font-style: normal;
  font-weight: 600;
  color: var(--surface);
  font-size: clamp(18px, 1.85vw, 25px);
  line-height: 1.42;
  max-width: none;
}

.hero-media { position: absolute; inset: 0; z-index: 0; }
/* No Ken Burns on the hero. The before photo is already a tight field, and
   any added zoom re-crops it. The hero is a pure crossfade; the drift lives
   only in the gallery reels. */
.hero-media .reel-inner { position: absolute; inset: 0; animation: none; }
/* Wide desktop boxes crop these 4:3 photos vertically. Anchor at 30% from the
   top so the loss comes from the floor, not the ceiling and window head. */
.hero-media img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 30%; }

/* No zoom on either frame. The two shots have different fields of view (the
   before was taken tighter); show each photo as-is and let the crossfade
   carry it. Slight left anchor keeps the after's full window and staging in
   frame on narrow screens. */
.hero-media img.after { object-position: 38% 30%; }

/* Scrim. "Hand us the keys." is white type sitting on a bright, high-detail
   photograph, and text-shadow alone was doing all the work: against the white
   ceiling and window the strokes disappeared. A gradient over the image gives
   the type a ground to sit on. Weighted to the top and the middle band, which
   is where the headline and the offer block sit, and it fades out over the
   lower third so the staging in the after frame still reads. */
.hero-media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(to bottom,
      rgba(0,0,0,.46) 0%,
      rgba(0,0,0,.24) 18%,
      rgba(0,0,0,.06) 38%,
      rgba(0,0,0,0)   55%);
}
/* On a phone the hero is nearly square, so the old full-height wash sat over
   the whole photograph and both frames read as grey. The headline is the only
   thing near the top here and the offer block carries its own shadows, so the
   scrim tightens to the band the headline occupies and clears the image below
   it entirely. */
@media (max-width: 700px) {
  .hero-media::after {
    background:
      linear-gradient(to bottom,
        rgba(0,0,0,.44) 0%,
        rgba(0,0,0,.20) 15%,
        rgba(0,0,0,0)   34%);
  }
}

/* Wipe, not dissolve. The after frame sweeps across like a before/after
   slider: reveal left-to-right, hold, retract right-to-left. The gold edge
   line below runs the same timing so it rides the reveal edge. */
.hero-media img.after {
  opacity: 1;
  clip-path: inset(0 100% 0 0);
  animation: herowipe 6s cubic-bezier(.65,0,.35,1) .87 forwards;
}
.hero-wipe {
  position: absolute;
  top: 0; bottom: 0;
  left: 0;
  width: 3px;
  margin-left: -1.5px;
  background: var(--gold);
  opacity: 0;
  z-index: 1;
  animation: herowipeline 6s cubic-bezier(.65,0,.35,1) .87 forwards;
}

/* After wipes in from the RIGHT edge sweeping left, so before holds on the
   left longest (matches the before-left sliders). */
@keyframes herowipe {
  0%, 45%   { clip-path: inset(0 0 0 100%); }
  58%, 87%  { clip-path: inset(0 0 0 0); }
  100%      { clip-path: inset(0 0 0 100%); }
}
@keyframes herowipeline {
  0%, 44.9% { left: 100%; opacity: 0; }
  45%       { left: 100%; opacity: 1; }
  58%       { left: 0%; opacity: 1; }
  58.1%, 86.9% { left: 0%; opacity: 0; }
  87%       { left: 0%; opacity: 1; }
  99.9%     { left: 100%; opacity: 1; }
  100%      { left: 100%; opacity: 0; }
}

/* Chips snap with the wipe reaching their corner instead of slow-fading.
   The 2s delay MUST live in the shorthand: a later `animation-delay` rule loses
   to the shorthand's own reset here, which left the chips running 2s early. */
.hero .tag.t-after  { animation: herotagin 5s ease .4s .83 forwards; }
.hero .tag.t-before { animation: herotagout 5s ease .4s .83 forwards; }
/* Wipe timing: before holds 0-32, sweeps 32-48, after holds 48-72, retracts
   72-88. Each chip belongs to its own hold, and neither shows mid-sweep. */
@keyframes herotagin  { 0%, 48% { opacity: 0; } 50%, 84% { opacity: 1; } 86%, 100% { opacity: 0; } }
@keyframes herotagout { 0%, 34% { opacity: 1; } 36%, 98% { opacity: 0; } 100% { opacity: 1; } }

/* No scrim. On the before frame the copy shows in paper with a dark halo.
   As the wipe edge crosses centre, the copy hands off to the transparent
   mallard 16DayPrep mark, which owns the bright after frame. Both swaps snap
   in a 2% window (0.18s) instead of dissolving. */
.hero-copy { position: relative; }
/* The copy belongs to the before-world and the mark to the after-world, so
   the wipe edge erases one and paints the other as it passes. Both clip on
   the same cubic-bezier as the image wipe; their 36-44% / 76-84% windows are
   when the viewport-wide edge crosses the centred copy block. */
.hero-copy h1, .hero-copy .lede {
  animation: herotextwipe 6s cubic-bezier(.65,0,.35,1) .87 forwards;
  animation-delay: .4s;
  text-shadow:
    0 2px 3px rgba(0,0,0,.85),
    0 4px 12px rgba(0,0,0,.72),
    0 0 42px rgba(0,0,0,.55);
}
/* ---- hero offer: headline, subhead and step 1 of the form ----
   Replaces the 16DayPrep mark. Sits higher than the mark did so the form clears
   the bed in the after photo. Fades in only (no slide): a form that moves while
   you are reaching for it is hostile, and transform is already owned by the
   wipe timeline. */
.hero-offer {
  position: absolute;
  inset: 0;
  margin: auto;
  height: fit-content;
  width: min(96%, 760px);
  translate: 0 clamp(-190px, -17vh, -120px);
  animation: heroofferin 6s ease .87 both;
  animation-delay: .4s;
}
.hero-offer-h {
  font-family: var(--display);
  font-size: clamp(30px, 5vw, 58px);
  line-height: 1.04;
  color: var(--surface);
  margin: 0 0 6px;
  max-width: none;
  text-shadow: 0 2px 3px rgba(0,0,0,.85), 0 4px 14px rgba(0,0,0,.7), 0 0 42px rgba(0,0,0,.55);
}
.hero-offer-sub {
  font-size: clamp(17px, 2.1vw, 24px);
  font-weight: 700;
  color: var(--surface);
  margin: 0 0 16px;
  max-width: none;
  text-shadow: 0 2px 3px rgba(0,0,0,.85), 0 4px 12px rgba(0,0,0,.72);
}
/* One line, no wrap between the fields. Grid rather than flex so the two inputs
   share the leftover width evenly and the button takes only what it needs. */
.hero-form { display: grid; gap: 10px; }
@media (min-width: 700px) {
  /* Address and button only: email moved to step 2. The field takes everything
     the button does not need. */
  .hero-form { grid-template-columns: minmax(0, 1fr) auto; align-items: stretch; }
}
/* Sitting on a photograph, a 1px hairline and a flat fill read as decoration.
   Both controls get an ink outline and the site's hard offset shadow so they
   look like physical objects laid on the image rather than tinted areas of it. */
.hero-form input {
  margin: 0;
  height: 100%;
  background: #FFFFFF;
  border: 2px solid var(--ink);
  border-radius: 0;
  box-shadow: 4px 4px 0 rgba(17,17,17,.42);
  font-size: 18px;
  padding: 15px 16px;
}
.hero-form input::placeholder { color: var(--ink); }
.hero-form input:focus {
  outline: 3px solid var(--gold);
  outline-offset: 2px;
}
.hero-form .btn {
  cursor: pointer;
  white-space: nowrap;
  padding: 15px 30px;
  font-size: 18px;
  font-weight: 700;
  /* Ink, not gold, so the edge holds against the bright after photo. */
  border: 2px solid var(--ink);
  /* Buttons stay pills: the squaring-off applies to rectangles, squares and
     photos, and a pill is what still reads as pressable beside them. */
  border-radius: var(--r-pill);
  box-shadow: 4px 4px 0 rgba(17,17,17,.42);
  transition: transform .14s ease, box-shadow .14s ease;
}
.hero-form .btn:hover {
  transform: translate(-2px, -2px);
  box-shadow: 6px 6px 0 rgba(17,17,17,.5);
}
.hero-form .btn:active {
  transform: translate(2px, 2px);
  box-shadow: 1px 1px 0 rgba(17,17,17,.42);
}
.hero-form .btn[disabled] { opacity: .6; cursor: default; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .hero-form .btn { transition: none; }
  .hero-form .btn:hover, .hero-form .btn:active { transform: none; }
}

/* Held back until the after frame has settled, then stays. `both` so the 0%
   keyframe also covers the delay and it cannot flash on load. Reduced motion
   kills the animation, which leaves the base state: visible. */
@keyframes heroofferin {
  0%, 63%   { opacity: 0; visibility: hidden; }
  70%, 100% { opacity: 1; visibility: visible; }
}
/* Replaces the lockup's outlined HOME SALES descriptor. Same descriptor
   treatment, ink like the onlight variant, sized to the mark it sits under. */
/* Matches the before-frame lede exactly in size and weight, so the two
   subheadings feel like counterparts across the swap. */
.hero-mark-tag {
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(20px, 2.4vw, 26px);
  line-height: 1.3;
  text-align: center;
  color: var(--ink);
  margin: 10px 0 0;
  max-width: none;
  /* Dense paper halo, the mirror of the lede's dark one: a hard contact
     edge plus stacked glows so the ink line art sits on its own light
     ground wherever it crosses the photo. */
  text-shadow:
    0 0 1px rgba(242,239,233,1),
    0 1px 2px rgba(242,239,233,.95),
    0 -1px 2px rgba(242,239,233,.95),
    0 2px 6px rgba(242,239,233,.85),
    0 0 14px rgba(242,239,233,.75),
    0 0 28px rgba(242,239,233,.6);
}

/* text: gone the moment the logo enters at 32%, back once it has fully left
   at 88%. Quick fade, not a lingering dissolve. */
@keyframes herotextwipe {
  0%, 45%  { opacity: 1; }
  47%, 98% { opacity: 0; }
  100%     { opacity: 1; }
}
/* mark: rides the sweep. It enters from the RIGHT edge a beat ahead of the
   gold line, holds that lead across the frame, then settles into centre as
   the line carries on to the left edge. On the retract it slides back out
   ahead of the returning line. Offsets are from its centred resting spot. */
@keyframes heromarkslide {
  0%, 33.9% { opacity: 0; transform: translateX(calc(50vw - 150px)); }
  34%       { opacity: 1; transform: translateX(calc(50vw - 150px)); }
  /* moving left for the full duration of the sweep; at 48% the after frame
     is fully in and the logo is at its leftmost point */
  50%       { transform: translateX(-220px); animation-timing-function: cubic-bezier(.25,.1,.25,1); }
  /* then a slow 1.1s drift back to centre */
  62%, 84%  { transform: translateX(0); }
  99.9%     { opacity: 1; transform: translateX(calc(50vw + 60%)); }
  100%      { opacity: 0; transform: translateX(calc(50vw + 60%)); }
}

.hero .tag { position: absolute; z-index: 2; right: 20px; bottom: 20px; left: auto; }


.hero h1 { color: var(--surface); margin-bottom: 0.3em; font-size: clamp(34px, 6vw, 60px); }
/* Descriptor is paper here, not gold. Gold sits at 1.03:1 against the after
   frame with no scrim, so it would vanish for half the crossfade. */
.lede { font-size: clamp(20px, 2.4vw, 26px); font-weight: 700; margin-bottom: 1.4rem; }
.hero .spine li { border-color: var(--rule-dark); }
.hero .btn:hover { background: #1C3839; color: var(--paper); border-color: #1C3839; }

/* Closing CTA. An INK field: distinct from both the mallard sections (home,
   case study, about) and the paper ones (services), so the CTA never blends
   into its neighbours or the mallard footer. Ink also lets the button stay
   GOLD, so the site-wide "gold means clickable" signal holds with no exception,
   and gold on near-black is the highest-contrast button on the site.
   The gold rule above pins the boundary against the mallard section; the
   footer's own gold top border closes it below. */
.band-close {
  background: var(--ink);
  color: var(--surface);
  padding-block: clamp(34px, 4.4vw, 62px);
  margin-inline: clamp(12px, 2.6vw, 44px);
  border-radius: 0;
  /* The gold rule was a full-width hairline sitting on top of the band. On a
     rounded panel it becomes the panel's own edge. */
  border: 2px solid var(--gold);
}
/* Centred CTA: the closing band is a single call, so headline, line, and button
   stack down the middle rather than sitting left with the button adrift. */
.band-close .wrap { text-align: center; }
.band-close h2, .band-close p { margin-inline: auto; }
.band-close p { max-width: 46ch; }
/* index uses a split layout (headline left / button right); collapse it so every
   page's closing band reads the same. */
.band-close .cta-split { display: block; }
.band-close .cta-split-action { margin-top: 1.8rem; }

.band-close h2 {
  color: var(--surface);
  font-size: clamp(28px, 3.6vw, 42px);
  line-height: 1.06;
}
.band-close p { color: var(--surface); font-size: clamp(17px, 1.5vw, 20px); }
.band-close a:not(.btn) { color: var(--surface); text-decoration-thickness: 1px; text-underline-offset: 3px; }
.band-close a:not(.btn):hover { color: var(--gold); }
/* Gold button (the standard) at CTA scale. No shadow: it would be invisible on ink. */
.band-close .btn { font-size: 22px; padding: 22px 48px; }


.spine { list-style: none; padding: 0; margin: 0 0 2.2rem; max-width: var(--measure); }
.spine li { padding: 14px 0 14px 28px; border-top: 1px solid var(--rule); position: relative; }
.spine li:last-child { border-bottom: 1px solid var(--rule); }
.spine li .star { position: absolute; left: 0; top: 20px; }

/* The flap counters live on a physical board, like a station departure
   panel: ink housing, gold keyline, flap cells separated by seams. */
/* Departure-board layout: stacked rows, label on the LEFT, flap value on the
   right, like a real station board (DESTINATION .... TIME). */
.stats {
  display: flex;
  flex-direction: column;
  gap: 14px;
  background:
    linear-gradient(180deg, #1c1c1c, #0e0e0e);
  border-radius: var(--r-card);
  padding: clamp(22px, 3.2vw, 40px) clamp(22px, 3.4vw, 44px);
  box-shadow:
    0 0 0 1px #000,
    0 0 0 4px var(--gold),
    inset 0 2px 4px rgba(255,255,255,.06),
    inset 0 -8px 20px rgba(0,0,0,.6),
    10px 10px 0 rgba(17,17,17,.35);
  width: fit-content;
  max-width: 100%;
  overflow-x: auto;
}
.stats > div {
  display: flex;
  align-items: center;
  gap: clamp(14px, 2.4vw, 34px);
}
.stats .lbl {
  order: -1;                         /* label to the left of the flaps */
  flex: none;
  width: clamp(110px, 20vw, 210px);
  text-align: right;
  margin: 0;
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 15px;
  line-height: 1.3;
  color: var(--gold);
}
.stats .num {
  color: var(--surface);
  font-family: var(--sans);
  font-weight: 700;
  font-size: clamp(24px, 4vw, 38px);
  line-height: 1.05;
}
@media (max-width: 560px) {
  .stats { padding: 18px 16px; gap: 11px; }
  .stats > div { gap: 12px; }
  .stats .num { font-size: clamp(15px, 4.6vw, 26px); }
  .stats .lbl { width: clamp(78px, 26vw, 128px); font-size: 12.5px; letter-spacing: .08em; }
}
.caption { font-size: 18px; margin-top: 20px; max-width: none; }

.skipped { list-style: none; padding: 0; margin: 0 0 1.6rem; max-width: var(--measure); }
.skipped li { padding: 10px 0; border-bottom: 1px solid var(--rule); font-weight: 600; }

.quote { margin: 0; max-width: var(--measure); }
.quote p { font-size: clamp(21px, 3.2vw, 27px); line-height: 1.45; font-weight: 600; margin: 0 0 16px; }
.quote cite { font-style: normal; font-size: 18px; }

.cta-end { padding-bottom: clamp(52px, 10vw, 84px); }

/* ---- service tiers ---- */
.tiers {
  display: grid;
  gap: 20px;
  padding-top: 0;
}
@media (min-width: 900px) {
  /* No align-items: start. Cards stretch to the tallest in the row so the
     three boxes always match height. */
  .tiers { grid-template-columns: repeat(3, 1fr); }
}

.tier {
  /* White surface on the cream ground. Previously paper-on-paper, which left
     the hairline doing all the work and the tiers reading as ruled regions
     rather than objects. */
  background: var(--surface);
  color: var(--ink);
  /* No hairline. It existed only to make a cream card visible on a cream
     ground; the card carries its own value now. Same hard offset shadow the
     framework cards use, so the elevation idiom stays consistent. */
  border: 0;
  box-shadow: 6px 6px 0 rgba(17,17,17,.14);
  border-radius: var(--r-card);
  padding: clamp(20px, 2.2vw, 26px);
  /* Column layout so terms fill the stretch and notes pin to the bottom. */
  display: flex;
  flex-direction: column;
}
.tier-terms { flex: 1; }

/* The action sits directly under the price: the number is the decision, so
   the button belongs beside it rather than after everything else. */
.tier-cta { margin: 0 0 16px; }
/* Service picker on the contact form. Reads as a grouped question, not four
   stray checkboxes. */
.svc-pick { border: 1px solid var(--rule); border-radius: var(--r-card); padding: 14px 16px 6px; margin: 4px 0 18px; }
.svc-pick legend { font-weight: 700; padding: 0 6px; font-size: 16px; }
.svc-pick .check { display: block; margin-bottom: 10px; }

.tier-cta .btn {
  display: block;
  text-align: center;
  font-size: 16px;
  padding: 11px 18px;
}

/* Who each tier is for. Sits between the price and the mechanics. */
.tier-who {
  margin: 0 0 14px;
  padding-bottom: 13px;
  border-bottom: 1px solid var(--rule);
  max-width: none;
}

.tier-kind {
  text-align: center;
  margin: 0 0 10px;
}

/* Commission gets the display face, not a bullet. The ladder reads at a
   glance: same 2.5% base on every card, the add-on in gold. */
.tier-price {
  font-family: var(--display);
  font-size: clamp(30px, 3vw, 38px);
  line-height: 1.1;
  text-align: center;
  margin: 0 0 14px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
  max-width: none;
}
.tier-price-plus { color: var(--gold); white-space: nowrap; }
.tier-price-label {
  display: block;
  font-family: var(--desc);
  font-weight: 700;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: .2em;
  padding-left: .2em;
  margin-top: 5px;
  color: var(--ink);
}
.tier-featured { box-shadow: 0 0 0 2px var(--gold), 6px 6px 0 rgba(17,17,17,.14); }

.tier-badge {
  width: min(62%, 220px);
  height: auto;
  margin: 0 auto 12px;
}

/* Formal menu name as a descriptor line under the badge, per the lockup
   pattern: mark carries the name, the descriptor carries the formality. Tracking is
   tighter than the brand's .42em so the long names hold one or two clean
   lines inside a card. */
.tier-name {
  font-family: var(--desc);
  font-weight: 700;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: .2em;
  padding-left: .2em;
  line-height: 1.8;
  text-align: center;
  color: var(--ink);
  margin: 0 0 18px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--rule);
}

.tier-terms { list-style: none; padding: 0; margin: 0; }
.tier-terms li {
  position: relative;
  padding: 0 0 7px 22px;
}
.tier-terms li::before {
  content: "";
  position: absolute;
  left: 2px; top: 11px;
  width: 6px; height: 6px;
  background: var(--gold);
  border-radius: 50%;
}
.tier-terms strong { color: var(--ink); }

.tier-note {
  margin: 6px 0 0;
  padding-top: 14px;
  border-top: 1px solid var(--rule);
  font-size: 18px;
  line-height: 1.7;
  max-width: none;
}

/* ---- before/after reel ----
   Ken Burns on a wrapper (both frames move together), crossfade on the top
   image. No video files: same zoom and tilt at a fraction of the bytes. */
.reels {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr));
  gap: 26px;
  margin-top: 34px;
}

.reel {
  position: relative;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  background: var(--mallard);
  border-radius: var(--r-card);
}

/* Hero reel is taller and carries a warm edge so it reads as a framed object,
   not a stock photo dropped on a dark field. */
.reel-hero {
  aspect-ratio: 4 / 3;
  box-shadow: 0 0 0 2px var(--gold), 0 18px 40px rgba(0,0,0,.28);
}
@media (min-width: 860px) {
  /* Let the hero frame bleed past the text baseline so it reads as the
     dominant object on the screen. */
  .reel-hero { aspect-ratio: 5 / 4; min-height: 460px; }
}

.reel-inner {
  position: absolute;
  inset: 0;
  animation: kenburns 11s ease-in-out infinite alternate;
  will-change: transform;
}

.reel img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The "after" frame sits on top and fades in and out on a loop. */
.reel img.after { opacity: 0; animation: swap 9s ease-in-out infinite; }

/* Floor dropped from 1.12 to 1.05: every bit of zoom sits on top of the crop
   object-fit already takes, so a high floor throws away the room. Pan and tilt
   scaled back to match. At 1.05 the overhang is 2.5% per side and the pan plus
   tilt together need under 1.8%, so no corner is ever exposed. */
@keyframes kenburns {
  from { transform: scale(1.05) translate(-1.3%, -1%) rotate(-0.45deg); }
  to   { transform: scale(1.22) translate(1.3%, 1%)   rotate(0.45deg); }
}

/* Dwell-heavy loop: the before frame holds ~4.3s of every 9s cycle, the after
   ~2.5s, with ~1s crossfades. The before is the hook; it needs to register
   before it dissolves. */
@keyframes swap {
  0%, 34%   { opacity: 0; }
  46%, 74%  { opacity: 1; }
  86%, 100% { opacity: 0; }
}

/* Stagger so the reels never move in lockstep. */
.reel:nth-child(2) .reel-inner { animation-delay: -3.7s; }
.reel:nth-child(3) .reel-inner { animation-delay: -7.4s; }
.reel:nth-child(2) img.after   { animation-delay: -2.3s; }
.reel:nth-child(3) img.after   { animation-delay: -4.6s; }

/* Label chip swaps with the frame underneath it. Base styling is unscoped so
   it works both inside a .reel and directly on the full-bleed hero. */
.tag {
  position: absolute;
  z-index: 2;
  font-family: var(--desc);
  font-weight: 700;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: .28em;
  padding: 7px 15px 7px 18px;
  border-radius: var(--r-pill);
  background: rgba(17,17,17,.72);
  color: var(--surface);
  backdrop-filter: blur(3px);
}
.reel .tag { left: 12px; bottom: 12px; }
.tag.t-after { opacity: 0; animation: swap 9s ease-in-out infinite; }
.tag.t-before { animation: swapinv 9s ease-in-out infinite; }
.reel:nth-child(2) .tag { animation-delay: -2.3s; }
.reel:nth-child(3) .tag { animation-delay: -4.6s; }

/* A short settle before the first wipe: long enough for the hero image to
   paint and for the untouched room to register, short enough that it doesn't
   read as a stall. At 2s the first before-hold ran 5.06s against 3.06s on every
   later loop. Positive delay only affects the first cycle. Must sit after the
   .tag animation shorthands above, which reset animation-delay to 0. */
.hero-media img.after, .hero .tag, .hero-wipe { animation-delay: .4s; }

@keyframes swapinv {
  0%, 34%   { opacity: 1; }
  46%, 74%  { opacity: 0; }
  86%, 100% { opacity: 1; }
}

/* ---- about ---- */
/* The headshot sits under the "About" title in the editorial rail, so the bio
   gets the whole body column instead of being squeezed by a second column. */
.about-me { display: block; }
/* The one deliberate exception to the squared-off rule: an oval, not a
   rounded rectangle. 4:5 so the ellipse is tall enough to clear the hairline
   at the top while cutting the shoulders, which is what an oval should do. */
.headshot {
  width: 100%; max-width: 260px;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: 50%;
  margin-top: clamp(16px, 2vw, 24px);
}

.paths { display: grid; gap: 18px; margin-top: 1.6rem; }
@media (min-width: 720px) { .paths { grid-template-columns: 1fr 1fr; } }
/* White surface, not an outline. On mallard the ground does the separating,
   so no border is needed and the panel reads as an object. */
.paths > div {
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-card);
  padding: 20px 22px;
}
/* .band-dark paints headings, links and descriptors paper so they read on
   mallard. Inside a white card that inverts, so the card resets them to ink. */
.band-dark .paths > div h3,
.band-dark .paths > div .desc,
.band-dark .paths > div a:not(.btn),
.band-dark .handles > div h3,
.band-dark .handles > div .desc,
.band-dark .handles > div a:not(.btn) { color: var(--ink); }
.paths h3 { margin-bottom: 0.2em; }
.paths p { margin: 0; font-size: 19px; }

/* ---- FAQs ---- */
/* Anchor targets clear the sticky header. Without this a link to #faqs scrolls
   the section to y=0 and the 111px header sits on top of its heading. */
:target, [id] { scroll-margin-top: clamp(96px, 12vh, 132px); }

/* Two columns inside an answer, so four short lists read side by side instead
   of as one long scroll. Each heading stays with its own list. */
/* Four cards, two up. Each card holds its own heading and list, so the four
   phases read as distinct blocks rather than one long column. */
.faq-cols { display: grid; gap: clamp(12px, 1.6vw, 18px); margin-top: 1.2em; }
@media (min-width: 700px) { .faq-cols { grid-template-columns: 1fr 1fr; } }
.faq-col {
  background: var(--paper);
  box-shadow: 4px 4px 0 rgba(17,17,17,.10);
  border-radius: var(--r-card);
  padding: clamp(18px, 2.2vw, 26px);
}
.faq-col h3 { margin: 0 0 .6em; font-size: 19px; }
.faq-col ul { margin: 0; padding-left: 1.1em; }
.faq-col li { margin-bottom: .35em; }
.faq-col li:last-child { margin-bottom: 0; }

/* FAQs centred as a block. The questions and answers stay left-aligned, since
   centred multi-line prose is markedly harder to read. The !important is needed
   because .editorial becomes a two-column grid inside a 900px media query, which
   otherwise wins and drops the heading on top of the first question. */
.faqs .editorial { display: block !important; }
/* Not sticky here. .ed-head sticks by design in the case-study layout; inside a
   centred block it rides down over the questions. */
.faqs .ed-head { text-align: center; margin-bottom: clamp(18px, 2.4vw, 30px); position: static; }
.faqs .ed-body { max-width: 46rem; margin-inline: auto; }

.faqs details {
  border-top: 1px solid var(--rule);
  padding: 4px 0;
}
.faqs details:last-of-type { border-bottom: 1px solid var(--rule); }
/* The question is a heading, so it takes the display face. At 21px Cabin bold
   against 19px Cabin body it was two points and a weight apart, which is not a
   difference you can see once the panel is open. */
.faqs summary {
  cursor: pointer;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(19px, 1.8vw, 24px);
  line-height: 1.25;
  padding: 16px 40px 16px 0;
  position: relative;
  list-style: none;
}
/* Open, the title also gets a rule under it so the answer reads as a separate
   block rather than running straight on from the question. */
.faqs details[open] summary {
  padding-bottom: 14px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--rule);
}
.faqs summary::-webkit-details-marker { display: none; }
.faqs summary::after {
  content: "+";
  position: absolute;
  right: 4px; top: 14px;
  font-family: var(--display);
  font-size: 26px;
  color: var(--gold);
}
.faqs details[open] summary::after { content: "−"; }
.faqs details > *:not(summary) { margin-left: 0; }
.faqs details h3 { margin-top: 1.2rem; }
.faqs details ul { padding-left: 24px; margin: 0 0 1rem; max-width: var(--measure); }
.faqs details li { padding: 3px 0; }

/* ---- tables (case study, FAQ scale) ---- */
table { border-collapse: collapse; }
th, td { text-align: left; padding: 8px 16px 8px 0; border-bottom: 1px solid var(--rule); font-size: 18px; vertical-align: top; }
th { font-family: var(--desc); font-weight: 700; text-transform: uppercase; letter-spacing: .12em; font-size: 15px; }

/* ---- case study options ---- */
.options { display: grid; gap: 18px; margin-top: 1.4rem; }
@media (min-width: 900px) { .options { grid-template-columns: repeat(3, 1fr); } }
.options > div { border: 1px solid var(--rule); border-radius: var(--r-card); padding: 20px 22px; }
.options h3 { margin-bottom: 0.2em; }
.options p { margin: 0; font-size: 19px; }

/* ---- contact ---- */
.contact-grid { display: grid; gap: clamp(28px, 5vw, 56px); align-items: start; }
@media (min-width: 860px) { .contact-grid { grid-template-columns: 1.2fr 0.8fr; max-width: 1120px; } }
.contact-form { max-width: 34rem; }
.contact-form .btn { margin-top: 10px; border: 0; cursor: pointer; }
.contact-form .btn[disabled] { opacity: .6; cursor: default; }

/* Two-part form: step marker, the step 1 recap on step 2, and the note under
   the step 1 button that tells them how much is left. */
.step-count {
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 14px;
  color: var(--mallard);
  margin: 0 0 1.2rem;
}
.step-recap {
  font-size: 16px;
  color: var(--mallard);
  background: rgba(41,78,79,0.06);
  border-left: 3px solid var(--gold);
  padding: 10px 14px;
  margin: 0 0 1.4rem;
  word-break: break-word;
}
.form-note { font-size: 16px; margin: 12px 0 0; }

.hp { position: absolute; left: -9999px; }
.check { display: flex; gap: 12px; align-items: flex-start; font-weight: 500; font-size: 18px; line-height: 1.6; margin: 6px 0 16px; }
/* The box is excluded from the field styling above, so at its default 13px it
   was the smallest tap target on the site. The whole label is clickable, but
   the box itself still has to be hittable and visible against cream. */
.check input {
  width: 22px; height: 22px;
  flex: 0 0 22px;
  margin: 4px 0 0;
  accent-color: var(--mallard);
}
.contact-direct { background: var(--paper); box-shadow: 4px 4px 0 rgba(17,17,17,.10); border-radius: var(--r-card); padding: clamp(22px, 3vw, 32px); }
.contact-direct h2 { font-size: clamp(26px, 4vw, 34px); }

/* ---- thoughts ---- */
.prose article { margin-bottom: 1rem; }
.prose h2 { max-width: 22ch; }
.prose ul, .prose ol { max-width: var(--measure); padding-left: 26px; }
.prose li { padding: 4px 0; }
.prose hr { margin-block: 3rem; }

/* ---- hero drag compare (reduced-motion path) ----
   Same two images as the animated hero. Instead of the wipe playing itself,
   the visitor drags the edge. --p is the clip position: 100% is all before,
   0% is all after, so the handle starts at the right edge and pulls left,
   matching the direction the animation sweeps. JS unhides this, so a visitor
   with no JS keeps the plain static before frame rather than a dead handle. */
.hero { --p: 100%; }
.hero-cmp[hidden] { display: none; }
.hero-cmp { position: absolute; inset: 0; z-index: 2; }
.hero-cmp-line {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--p);
  width: 3px;
  margin-left: -1.5px;
  background: var(--gold);
  z-index: 2;
  pointer-events: none;
}
/* Deliberately larger than the in-page .cmp-handle: this one has to read as
   grabbable at a glance, over a photo, above the fold. */
.hero-cmp-handle {
  position: absolute;
  /* Below centre on purpose: at 50% it lands on the headline. This keeps it
     clear of the copy and closer to a thumb on a phone. */
  top: 72%; left: 50%;
  transform: translate(-50%, -50%);
  width: 76px;
  height: 76px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--ink);
  font-size: 22px;
  letter-spacing: 1px;
  display: grid;
  place-items: center;
  border: 3px solid var(--paper);
  box-shadow: 0 4px 18px rgba(0,0,0,.5);
}
.hero-cmp-range {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  margin: 0; padding: 0;
  opacity: 0;
  cursor: ew-resize;
  z-index: 3;
  -webkit-appearance: none;
  background: none;
  border: 0;
}
/* The input itself is invisible, so focus has to show up on the knob. */
.hero-cmp-range:focus-visible ~ .hero-cmp-line .hero-cmp-handle {
  outline: 3px solid var(--paper);
  outline-offset: 3px;
}
@media (max-width: 600px) {
  .hero-cmp-handle { width: 64px; height: 64px; font-size: 19px; }
}
/* While dragging, the chip shrinks and tucks into the very bottom right, well
   under the knob, so it labels the side you are looking at without competing
   with the handle. JS flips which of the two is showing. */
.hero.hero-drag .tag {
  font-size: 11px;
  letter-spacing: .2em;
  padding: 4px 9px 4px 11px;
  right: 12px;
  bottom: 12px;
  z-index: 1;
}

/* Motion is decoration. Anyone who asks for less keeps both frames, they just
   drive the reveal themselves: the animations stop and the drag handle above
   takes over the same clip the wipe used to animate. */
@media (prefers-reduced-motion: reduce) {
  .reel-inner, .reel img.after, .hero-media img.after, .tag,
  .hero-copy h1, .hero-copy .lede, .hero-mark { animation: none !important; }
  .hero-wipe { display: none; }
  .hero-media img.after { clip-path: inset(0 0 0 var(--p)); }
}

/* ---- case study: static before/after pairs ---- */
.pairs { display: grid; gap: 26px; }
.pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  margin: 0;
}
.pair-img { position: relative; border-radius: var(--r-card); overflow: hidden; }
.pair-img img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 3 / 2; }
.tag.t-static { position: absolute; left: 12px; bottom: 12px; animation: none; opacity: 1; }

/* ---- case study: numbered steps + sticky progress nav ---- */
.step-label {
  display: flex;
  align-items: center;
  gap: 14px;
  /* Was overriding .desc's size and tracking to survive the old .42em
     descriptor spacing. .desc is .14em now, so this inherits it and the step
     labels match every other descriptor on the site. */
  padding-left: 0;
}

/* The case study address is data, not an eyebrow. Uppercase and tracked out it
   read as a string of loose characters, so it gets normal sentence case. */
.case-addr {
  font-size: 18px;
  font-weight: 600;
  color: var(--mallard);
  margin: 8px 0 0;
}


/* ---- result comparison table on mallard ---- */
.table-dark th, .table-dark td { border-color: var(--rule-dark); color: var(--surface); }
.table-dark .th-sub { display: block; font-size: 15px; letter-spacing: .08em; }
/* Legal disclaimer: present and legible, but visually subordinate. Smaller,
   tighter, and set off by a hairline so it reads as fine print, not body copy. */
.fineprint {
  font-size: 13.5px;
  line-height: 1.55;
  margin-top: 1.8rem;
  padding-top: .9rem;
  border-top: 1px solid var(--rule-dark);
  max-width: 52rem;
}

/* ---- prep plan guide ---- */
.mistakes { list-style: none; padding: 0; margin: 0 0 1.4rem; max-width: var(--measure); }
.mistakes li { padding: 12px 0; border-bottom: 1px solid var(--rule); }
.tiers-list { max-width: var(--measure); padding-left: 26px; }
.tiers-list li { padding: 6px 0; }
.chain { font-weight: 700; line-height: 2; max-width: none; }

/* tier reference card inside the case study */
.tier-ref {
  background: var(--paper);
  box-shadow: 4px 4px 0 rgba(17,17,17,.10);
  border-radius: var(--r-card);
  padding: clamp(20px, 3vw, 28px);
  margin: 1.6rem 0;
  max-width: none;
}
.tier-ref li { max-width: none; }
.tier-ref .desc { margin-bottom: 12px; letter-spacing: .1em; padding-left: 0; }
.tier-ref ol { margin: 0; padding-left: 24px; }
.tier-ref li { padding: 4px 0; }

/* ---- framework chart (prep-plan teaser) ----
   Paper cards on the mallard band, same language as the handles and tier
   cards. Gold slab numerals, gold chevrons between stages, and a gold-ringed
   result card. */
.fw-chart { max-width: 1120px; margin-inline: auto; }
.fw-macro {
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-card);
  padding: clamp(20px, 3vw, 28px);
  text-align: center;
  box-shadow: 6px 6px 0 rgba(17,17,17,.28);
}
.fw-macro p { margin: 0 auto; max-width: none; }
.fw-macro .desc { letter-spacing: .14em; padding-left: 0; margin-bottom: 6px; color: var(--ink); }
.fw-macro p:last-child { font-weight: 700; margin-top: 4px; }


/* The funnel runs 4 / 1 / 3 / 1: four analysis steps produce the prep decision,
   the prep work sets up the sale, and the sale produces the result. Grid (not
   flex) is required because --hexcut is a percentage, and percentage padding
   resolves against the containing block, which under grid is the column and
   under flex would be the whole chart. */
.fw-steps, .fw-row, .fw-prep, .fw-tip {
  --gap: clamp(10px, 2cqw, 26px);
  display: grid;
  gap: var(--gap);
}
.fw-steps { grid-template-columns: repeat(4, minmax(0, 1fr)); }
/* One tile = a quarter of the chart. Prep and the result match it exactly and
   the sales row is three of them, so every hexagon is the same size. */
.fw-steps, .fw-row, .fw-prep, .fw-tip { --tile: calc((100% - 3 * var(--gap)) / 4); }
.fw-prep, .fw-tip {
  grid-template-columns: minmax(0, 1fr);
  width: var(--tile);
  margin-inline: auto;
}

.fw-row {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  width: calc(var(--tile) * 3 + var(--gap) * 2);
  margin-inline: auto;
}
/* Cards take the brand badge silhouette: flat top and bottom, points at mid-height.
   The clip lives on a background layer, not the card, so the hex numeral can still
   ride half-out of the top edge without being sliced off. */
.fw-chart {
  --hexcut: 16.5%;   /* measured off the badge art: 213px of 1289 */
  --badge-ratio: 1.606;
  --keyline: rgba(41,78,79,.26);
  --hex: polygon(var(--hexcut) 0, calc(100% - var(--hexcut)) 0, 100% 50%,
                 calc(100% - var(--hexcut)) 100%, var(--hexcut) 100%, 0 50%);
  isolation: isolate;
  /* cqw units let the hexagons shrink together and hold 1.606 at any width */
  container-type: inline-size;
}
.fw-steps > div, .fw-row > div, .fw-prep > div {
  position: relative;
  min-width: 0;   /* auto would refuse to shrink below the longest word */
  color: var(--ink);
  /* Only half the point inset is needed: the text block sits at the vertical
     middle, where the hexagon is at its widest. */
  padding: 1.25cqw calc(var(--hexcut) * 0.45 + 2px);
  aspect-ratio: var(--badge-ratio);
  text-align: center;
  font-weight: 700;
  font-size: clamp(13px, 2.1cqw, 21px);
  line-height: 1.22;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
}
.fw-steps > div::before, .fw-row > div::before, .fw-prep > div::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--surface);
  clip-path: var(--hex);
  /* drop-shadow follows the clipped silhouette; box-shadow would trace the box. */
  filter: drop-shadow(5px 5px 0 rgba(17,17,17,.28));
}
/* Subtle inner frame, mirroring the keyline inside the brand badge (measured at
   4.6% of the badge height). A border can't trace a clip-path, so the ring is
   four 1px drop-shadows cast off a paper hexagon inset by that amount. */
.fw-steps > div::after, .fw-row > div::after, .fw-prep > div::after {
  content: "";
  position: absolute;
  inset: 4.6% 2.86%;          /* equal in px: 2.86% of W == 4.6% of H at 1.606 */
  z-index: -1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160.6 100' preserveAspectRatio='none'%3E%3Cpolygon points='26.5,0 134.1,0 160.6,50 134.1,100 26.5,100 0,50' fill='none' stroke='%23294E4F' stroke-opacity='.55' stroke-width='1.1'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}
.fw-num {
  display: block;
  font-family: var(--display);
  color: var(--gold);
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.2;
}

/* Result card is the same badge shape, with the gold keyline built from two
   stacked clipped layers (a border can't trace a clip-path). */
/* Flatter cards need a deeper bite for the points to read at all.
   --hexcut resolves where var(--hex) is consumed, so redefining it here works. */
/* Left unchecked these stretch to 4.6:1 and read as flattened blobs.
   Cap the width and centre them so each keeps the badge's proportion. */

.fw-result {
  position: relative;
  min-width: 0;
  aspect-ratio: var(--badge-ratio);
  display: flex;
  flex-direction: column;   /* star above the words, not stealing line width */
  align-items: center;
  justify-content: center;
  gap: 2px;
  color: var(--ink);
  padding: 1.1cqw calc(var(--hexcut) * 0.45 + 2px);
  text-align: center;
  font-family: var(--display);
  font-size: clamp(12px, 1.85cqw, 19px);
  line-height: 1.15;
}
.fw-result::before,
.fw-result::after {
  content: "";
  position: absolute;
}
.fw-result::before { clip-path: var(--hex); }
.fw-result::before {
  inset: 0;
  z-index: -2;
  background: var(--surface);
  filter: drop-shadow(7px 7px 0 rgba(17,17,17,.28));
}
.fw-result::after {
  inset: 4.6% 2.86%;
  z-index: -1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160.6 100' preserveAspectRatio='none'%3E%3Cpolygon points='26.5,0 134.1,0 160.6,50 134.1,100 26.5,100 0,50' fill='none' stroke='%23A9853F' stroke-opacity='1' stroke-width='1.1'/%3E%3C/svg%3E");
  background-size: 100% 100%;
  background-repeat: no-repeat;
}

/* Drop columns before a card gets too narrow to hold its text inside
   width / 1.606, which is what silently overrode aspect-ratio before. */
/* Keyed to the chart's own width, not the viewport, so it reflows exactly
   when a card gets too small for its label rather than at a guessed breakpoint. */
/* Below this the five-across hexes get too small for their labels, so the
   pyramid stacks 3 / 2 / 1 instead. */
/* Below this the five-across hexes get too small for their labels. */
/* On phones the funnel stacks vertically. The sideways three-column version
   worked when the chart was 5 / 3 / 1, but the 4 / 1 / 3 / 1 shape needs four
   content columns plus three arrows, which leaves about 75px per hexagon on a
   390px screen. Stacking spends the axis a phone actually has. */
@media (max-width: 700px) {
  .fw-scroll { overflow: visible; }
  .fw-chart { min-width: 0; display: block; }
  /* Four analysis steps two-up; prep, the sales row and the result full width. */
  .fw-steps { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fw-prep, .fw-tip { width: calc((100% - var(--gap)) / 2); }
  .fw-row {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    width: 100%;
  }
  /* Arrows point down again. */
  .fw-connect { padding: 8px 0; }
  .fw-connect svg { transform: none; width: 15px; height: 24px; }
  .fw-steps > div, .fw-row > div, .fw-prep > div, .fw-result {
    font-size: clamp(11px, 3.2vw, 15px);
    padding-inline: calc(var(--hexcut) * 0.35 + 2px);
  }
}

/* dial + hex numerals + connectors + star result */
.fw-factors { font-weight: 700; margin-bottom: 10px !important; }
.fw-dial { width: min(66%, 300px); height: auto; display: block; margin: 6px auto 2px; }
.fw-dial-labels {
  display: flex;
  justify-content: space-between;
  max-width: 360px;
  margin: 0 auto;
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 15px;
  line-height: 1.5;
}
.fw-dial-labels span:last-child { text-align: right; }

.fw-connect { display: flex; justify-content: center; padding: 12px 0; }
.fw-connect svg { width: 18px; height: 30px; }

/* Numeral sits ABOVE the hexagon, not inside it. That leaves the interior to
   the label alone, so the shapes stay small and keep the badge ratio. */
.fw-hexnum {
  position: absolute;
  top: -1.35em;
  left: 0;
  right: 0;
  text-align: center;
  color: var(--gold);
  font-family: var(--display);
  font-size: clamp(15px, 2.7cqw, 27px);
  line-height: 1.1;
  margin: 0;
}

.fw-result { display: flex; align-items: center; justify-content: center; gap: 14px; }



/* ---- case study photo grids ----
   The story opens on every BEFORE and closes on every AFTER, so the page reads
   problem first, payoff last. These same shots run as draggable sliders on the
   home page; here they only need to establish scope, so no interaction. */

/* Before: a 6-wide collage, two rows, small enough to take in at a glance.
   Three shots were taken vertical; align-items:center lets them set the row
   height and centres the landscape tiles beside them, which is where the blank
   space above and below comes from. */
/* ---- salon hang ----
   Scale varies, ratio does not. Every source photo is 3:2, so varying the slot
   ratio is what cropped them (one slot came out 7.8:1, showing 19% of the
   picture). Vary the WIDTH instead and let 3:2 set the height: a 4-column piece
   and a 2-column piece are very different sizes and neither loses anything.
   The tall left piece spans both rows, which works out to almost exactly 3:2
   because two stacked 2-column tiles equal the height of one 4-column tile. */
.shots {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: clamp(6px, 0.8vw, 12px);
  margin-top: 10px;
}
.shots img {
  width: 100%;
  object-fit: cover;
  border-radius: var(--r-card);
  display: block;
}
/* Two grid rows, not three. The hero sets row 1's height from its own 3:2
   ratio, so it is never stretched and never cropped, and the pair beside it is
   a nested flex column rather than two items spanning a row each. The old
   version leaned on height:100% on a replaced element across a row span, which
   is the one construct here whose resolved height differs between engines and
   is what the "bleeds behind the others" reports were describing. A flex column
   cannot overrun its row no matter how the row resolves. */
.shots > :nth-child(1) { grid-column: 1 / 5; grid-row: 1; aspect-ratio: 3 / 2; }
.shots-stack {
  grid-column: 5 / 7; grid-row: 1;
  display: flex; flex-direction: column; gap: inherit; min-height: 0;
}
.shots-stack img { flex: 1 1 0; min-height: 0; }
.shots > :nth-child(3) { grid-column: 1 / 3; grid-row: 2; aspect-ratio: 3 / 2; }
.shots > :nth-child(4) { grid-column: 3 / 5; grid-row: 2; aspect-ratio: 3 / 2; }
.shots > :nth-child(5) { grid-column: 5 / 7; grid-row: 2; aspect-ratio: 3 / 2; }

@media (max-width: 700px) {
  /* Two columns: one wide piece, then pairs. Still all 3:2. */
  .shots { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shots > :nth-child(1) { grid-column: 1 / 3; grid-row: auto; height: auto; aspect-ratio: 3 / 2; }
  .shots > :nth-child(2),
  .shots > :nth-child(3),
  .shots > :nth-child(4),
  .shots > :nth-child(5),
  .shots > :nth-child(6) { grid-column: auto; grid-row: auto; }
  /* The stacked pair has no column to sit beside on a phone, so it stops being
     a unit and its two photos rejoin the flow as ordinary tiles. */
  .shots-stack { display: contents; }
  .shots-stack img { aspect-ratio: 3 / 2; }
  /* Six tiles in two columns after a full-width hero leaves one orphan and a
     half-empty last row. The orphan spans instead, so the set opens and closes
     wide with pairs between. Not on the quad page, which divides evenly. */
  .shots-after:not(.shots-quad) > :last-child { grid-column: 1 / -1; }
}

/* Before: a paged carousel. Six shots at a time, arrows swap the whole set.
   Reuses the .gallery component (arrows, dots, JS) from the home page.
   Rows are grouped by orientation: mixing portrait and landscape in one row
   left tiles floating at different heights and read as chaos. */
.photo-pager .gviewport { border-radius: 0; }
.photo-pager .page {
  flex: 0 0 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(8px, 1vw, 14px);
}
.prow {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(8px, 1vw, 14px);
}
.prow img {
  width: 100%;
  aspect-ratio: 3 / 2;
  object-fit: cover;
  border-radius: var(--r-card);
  display: block;
}
/* Shot vertical, so they keep portrait proportions. Capped and centred so they
   don't tower over the landscape rows. */
/* Four verticals span exactly the width of the three landscapes above them, so
   both rows share the same left and right edge. */
.prow.portrait { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.prow.portrait img { aspect-ratio: 3 / 4; }
@media (max-width: 560px) {
  /* Six photos split across two .prow grids of three each. At two columns that
     is 2 + 2 rows: four rows for six photos, with a half-empty row in the
     middle of the set. The rows stop being grids of their own and the whole
     slide becomes one, so the six flow as three even rows. */
  .photo-pager .page {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: clamp(8px, 1vw, 14px);
    align-content: start;
  }
  .photo-pager .prow, .photo-pager .prow.portrait { display: contents; }
  .prow, .prow.portrait { grid-template-columns: repeat(2, minmax(0, 1fr)); justify-content: stretch; }
}

/* Hexagons that open a step. The whole tile is the target, so it needs to look
   pressable and show which one is currently open. */
.fw-hit { cursor: pointer; }
.fw-hit:focus-visible { outline: 3px solid var(--gold); outline-offset: 4px; }
/* Open step: gold keyline instead of the mallard one, so the chart shows where
   you are without changing the tile's size or colour. */
.fw-hit.is-open::after {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 160.6 100' preserveAspectRatio='none'%3E%3Cpolygon points='26.5,0 134.1,0 160.6,50 134.1,100 26.5,100 0,50' fill='none' stroke='%23A9853F' stroke-opacity='1' stroke-width='1.6'/%3E%3C/svg%3E");
}
.fw-hit.is-open { font-weight: 700; }

/* The eight write-ups live here as a hidden store. Opening one moves the node
   into the dialog, so this section never renders. */
.case { display: none; }
.case-panel[hidden] { display: none; }
.case-panel .step-label { margin-top: 0 !important; }

/* Caption under the chart, on the mallard band. */
.chart-caption {
  margin: clamp(20px, 2.6vw, 32px) 0 0;
  font-size: clamp(17px, 1.5vw, 20px);
  max-width: 46ch;
}

/* ---- step window ---- */
.stepbox {
  position: fixed;
  inset: 0;
  z-index: 210;
  background: rgba(17, 17, 17, .72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(10px, 3vw, 40px);
}
.stepbox[hidden] { display: none; }
.stepbox-card {
  position: relative;
  background: var(--surface);
  color: var(--ink);
  border-radius: var(--r-card);
  width: min(780px, 100%);
  max-height: calc(100dvh - clamp(20px, 6vw, 80px));
  overflow-y: auto;
  padding: clamp(26px, 4vw, 48px);
  box-shadow: 0 18px 50px rgba(17,17,17,.35);
}
.stepbox-close {
  position: sticky;
  top: 0;
  float: right;
  width: 44px; height: 44px;
  margin: -8px -8px 0 12px;
  border: 0;
  border-radius: 50%;
  background: var(--gold);
  color: var(--ink);
  font-size: 28px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
  z-index: 2;
}
.stepbox-close:hover { background: #8a6b2f; color: var(--paper); }
/* Inside the window the write-up is a single column, not the editorial split. */
.stepbox-body .case-panel { display: block; }
.stepbox-body .ed-body { max-width: none; }
body.stepbox-open { overflow: hidden; }
@media (prefers-reduced-motion: no-preference) {
  .stepbox { animation: lightboxin .16s ease-out; }
}

/* Belief statement: the claim lands on line one at display scale, the specifics
   follow beneath it, and the gold rule under "one process" marks the phrase the
   whole page exists to argue. */
.belief { margin: 0 0 .5em; }
.belief-lead {
  display: block;
  font-size: clamp(34px, 4.6vw, 58px);
  line-height: 1.02;
}
.belief-rest {
  display: block;
  font-size: clamp(21px, 2.5vw, 31px);
  line-height: 1.18;
  margin-top: .12em;
}
/* Hand-drawn highlighter, the same swash used on all-done2. Sits behind the
   words so it reads as one continuous stroke; a text-decoration underline broke
   across the space and drew as two separate rules. Brand gold at higher opacity
   than the landing page version, which sits on paper rather than mallard. */
.belief-mark {
  display: inline-block;
  position: relative;
  isolation: isolate;
  padding: 0 6px;
}
.belief-mark::before {
  content: "";
  position: absolute;
  left: -2px; right: -4px; bottom: 0;
  height: 42%;
  z-index: -1;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 320 40' preserveAspectRatio='none'><path d='M6 18 C 24 9, 70 13, 120 11 C 175 8, 230 14, 285 12 C 305 12, 316 16, 315 20 C 314 26, 295 30, 268 29 C 200 27, 130 31, 70 29 C 38 28, 12 27, 8 24 C 4 22, 4 20, 6 18 Z' fill='%23A9853F' opacity='0.9'/></svg>");
  background-repeat: no-repeat;
  background-size: 100% 100%;
  transform: rotate(-0.4deg);
}

/* Title for the eight-step walkthrough. Sits above step 01 and sets up
   everything below it, so it reads larger than the step headings it precedes. */
.case-title {
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1.18;
  margin: 0 0 clamp(20px, 2.6vw, 34px);
  max-width: 34ch;
}

/* ---- lightbox ----
   Any case-study photo opens full screen; clicking again (anywhere, including
   the photo) closes it. */
.prow img, .shots img { cursor: zoom-in; }
.lightbox {
  --lb-pad: clamp(12px, 3vw, 40px);
  position: fixed;
  inset: 0;
  z-index: 200;
  background: rgba(17, 17, 17, .93);
  /* flex, not grid: an auto-sized grid row gives the image no definite height
     to resolve max-height:100% against, so tall portrait shots rendered at full
     1600px and ran off the top and bottom of the screen. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--lb-pad);
  cursor: zoom-out;
}
.lightbox[hidden] { display: none; }
.lightbox img {
  /* Viewport units as well as percentages: these always resolve, so a portrait
     shot can never exceed the screen regardless of how the box is sized. */
  max-width: min(100%, calc(100vw - var(--lb-pad) * 2));
  max-height: min(100%, calc(100dvh - var(--lb-pad) * 2));
  width: auto;
  height: auto;
  object-fit: contain;      /* whole photo, uncropped */
  border-radius: var(--r-card);
  display: block;
}
.lightbox-close {
  position: absolute;
  top: clamp(10px, 2vw, 22px);
  right: clamp(10px, 2vw, 22px);
  width: 46px; height: 46px;
  border: 0;
  border-radius: 50%;
  background: var(--gold);
  color: var(--ink);
  font-size: 30px;
  line-height: 1;
  cursor: pointer;
  display: grid;
  place-items: center;
}
.lightbox-close:hover { background: var(--paper); }
/* Stop the page scrolling behind the overlay. */
body.lightbox-open { overflow: hidden; }
@media (prefers-reduced-motion: no-preference) {
  .lightbox { animation: lightboxin .16s ease-out; }
  @keyframes lightboxin { from { opacity: 0; } to { opacity: 1; } }
}

/* These used to re-declare the column count for .shots and .shots-after at
   three breakpoints. The salon hang above defines its own 6-column track and
   places every tile explicitly, so these were silently collapsing columns 4-6
   to zero width and crushing the large tile to 32:1. Only the gap survives. */
.shots-after { gap: clamp(10px, 1.2vw, 16px); }

/* ---- before/after gallery: one set at a time, manual arrow nav ---- */
.gallery {
  --gutter: clamp(46px, 6vw, 72px);
  position: relative;
  /* fill the band; the 760px cap was leaving wide empty gutters on desktop */
  max-width: 100%;
  /* side padding reserves the gutters the arrows live in, so they sit beside
     the photo instead of over it */
  padding: 0 var(--gutter);
  margin: clamp(16px, 2vw, 26px) auto 0;
}
/* Align this section's heading with the photo's left edge (past the arrow
   gutter). Scoped by :has so the shared .how-head elsewhere is untouched. */
.how-head:has(+ .gallery) { padding-left: clamp(46px, 6vw, 72px); }
/* A bare label above a gallery needs the same indent. Without it the word sits
   flush to the section edge while the photos start one arrow-gutter in, so it
   hangs off their left edge. margin (not padding) so .desc keeps its own
   optical nudge. Only where a .gallery follows: .shots has no gutter. */
.desc:has(+ .gallery) { margin-left: clamp(46px, 6vw, 72px); }
/* The JS pins this to the height of the slide on screen; the transition keeps
   the change in step with the slide itself instead of snapping. */
.gviewport { overflow: hidden; border-radius: var(--r-card); transition: height .45s cubic-bezier(.4, 0, .2, 1); }
.gtrack { display: flex; align-items: flex-start; transition: transform .45s cubic-bezier(.4, 0, .2, 1); }
@media (prefers-reduced-motion: reduce) { .gviewport { transition: none; } }
/* Each slide fills the viewport; the .cmp visuals (layered images, gold line,
   handle) come from .cmp. Higher specificity than .cmp's margin:0. */
.gtrack .mtile { flex: 0 0 100%; margin: 0; }
/* Prev/next arrows flank the viewport so they never sit over the draggable
   photo area. */
.gnav {
  position: absolute;
  /* vertically centered on the photo (the .gviewport), whose height is the
     gallery height minus the dots row (~3.2rem) below it */
  top: calc((100% - 3.2rem) / 2);
  transform: translateY(-50%);
  width: clamp(38px, 4vw, 46px);
  height: clamp(38px, 4vw, 46px);
  border-radius: 50%;
  border: 0;
  background: var(--gold);
  color: var(--ink);
  font-size: 24px;
  line-height: 1;
  font-family: var(--sans);
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 3px 3px 0 rgba(17,17,17,.3);
  transition: background .2s ease, transform .1s ease;
  z-index: 4;
}
.gnav:hover { background: #8a6b2f; color: var(--paper); }
.gnav:active { transform: translateY(-50%) scale(.94); }
/* Seated in the padding gutters, clear of the photo edges. */
.gprev { left: 0; }
.gnext { right: 0; }
.gfoot {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
}
.gcaption {
  margin: 0;
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 15px;
  color: var(--surface);
}
.gdots { display: flex; gap: 0; justify-content: center; }
/* The dot stays 10px but the button is 44px so it can actually be tapped.
   Visual moves to ::before; the button itself becomes an invisible hit area. */
.gdot {
  width: 44px; height: 44px;
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  display: grid;
  place-items: center;
  -webkit-tap-highlight-color: transparent;
}
.gdot::before {
  content: "";
  width: 10px; height: 10px;
  border-radius: 50%;
  background: rgba(17,17,17,.26);
  transition: background .2s ease, transform .2s ease;
}
.gdot[aria-selected="true"]::before { background: var(--gold); transform: scale(1.3); }
/* On a mallard band the unselected dots invert, or they vanish into the dark. */
.band-dark .gdot::before { background: rgba(242,239,233,.42); }

/* Mobile: the side gutters were eating 92px of a 390px screen (24%) to hold
   the arrows, squeezing the photo to 258px. Drop the gutters, give the photo
   the full width, and lay the arrows out in a control row beside the dots.
   That also removes the touch conflict between the arrows and the drag slider. */
@media (max-width: 700px) {
  .desc:has(+ .gallery) { margin-left: 0; }
  .gallery {
    padding: 0;
    display: grid;
    grid-template-columns: auto 1fr auto;
    grid-template-areas:
      "photo photo photo"
      "prev  dots  next";
    align-items: center;
    row-gap: 14px;
  }
  .gviewport { grid-area: photo; }
  .gprev { grid-area: prev; }
  .gnext { grid-area: next; }
  .gfoot { grid-area: dots; margin-top: 0; }
  .gnav {
    position: static;
    transform: none;
    top: auto;
    width: 44px; height: 44px;   /* comfortable tap target */
  }
  .gnav:active { transform: scale(.94); }
}

/* Mobile hero. A tall hero on a narrow screen made object-fit crop 44% off the
   SIDES, which cut the sticker door out of frame entirely. Height now tracks
   the 4:3 before frame (75vw), so the full image width shows the way it does
   on desktop. Copy shrinks to sit inside the shorter frame. */
@media (max-width: 700px) {
  /* 75vw was sized for the old hero: a photo, a short headline and the mark.
     The offer block is absolutely positioned, so it cannot push the hero taller,
     and at 375px the block was 302px inside a 281px hero with its top 154px off
     the screen. The floor now covers the content with the photo still generous. */
  .hero {
    min-height: max(96vw, 540px);
    height: auto;
    padding-top: 0;
    padding-bottom: 0;
  }
  .hero-copy h1 { font-size: clamp(26px, 7.4vw, 36px); margin-bottom: .18em; }
  .hero .lede { font-size: clamp(15px, 4.2vw, 19px); margin-bottom: .7rem; }
  /* No lift on a phone: there is no spare height to lift into. */
  .hero-offer { translate: 0 0; width: min(100%, 440px); }
  .hero-offer-h { font-size: clamp(27px, 8.4vw, 38px); line-height: 1.06; margin-bottom: 4px; }
  .hero-offer-sub { font-size: clamp(15px, 4.2vw, 18px); margin-bottom: 14px; }
  .hero-form { gap: 9px; }
  .hero-form .btn { padding: 14px 20px; }
}

/* ---- drag comparison sliders ---- */
.cmps { display: grid; gap: 20px; margin-top: 16px; }
@media (min-width: 900px) { .cmps { grid-template-columns: 1fr 1fr; } }
.cmp {
  --p: 50%;
  position: relative;
  margin: 0;
  border-radius: var(--r-card);
  overflow: hidden;
  aspect-ratio: 3 / 2;
  background: var(--mallard);
  touch-action: none;
}
.cmp > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Before is the base (left); after is revealed on the RIGHT of the handle. */
.cmp .cmp-after { clip-path: inset(0 0 0 var(--p)); }
.cmp-line {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--p);
  width: 3px;
  margin-left: -1.5px;
  background: var(--gold);
  z-index: 2;
  pointer-events: none;
}
.cmp-handle {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 46px;
  height: 46px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--ink);
  font-size: 13px;
  letter-spacing: 1px;
  display: grid;
  place-items: center;
  box-shadow: 3px 3px 0 rgba(17,17,17,.3);
}
.cmp-range {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  opacity: 0;
  cursor: ew-resize;
  z-index: 3;
  -webkit-appearance: none;
  background: none;
  border: 0;
}
.cmp-hint {
  margin-top: 10px;
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 15px;
  margin-top: 14px;
}

/* ---- dial needle swing ---- */
.fw-needle-group {
  transform-origin: 120px 120px;
  transform: rotate(-120deg);
  transition: transform 1.3s cubic-bezier(.34, 1.4, .64, 1);
}
.fw-needle-group.swung { transform: rotate(0deg); }
@media (prefers-reduced-motion: reduce) {
  .fw-needle-group { transform: none; transition: none; }
}

/* ---- card lift micro-interactions ---- */
.tier, .handles > div, .fw-steps > div, .fw-row > div, .fw-prep > div, .fw-macro, .fw-result, .cmp {
  transition: transform .18s ease, box-shadow .18s ease;
}
.tier:hover { transform: translate(-2px, -2px); box-shadow: 8px 8px 0 rgba(17,17,17,.18); }
.tier-featured:hover { box-shadow: 0 0 0 2px var(--gold), 8px 8px 0 rgba(17,17,17,.18); }
.fw-steps > div:hover, .fw-row > div:hover, .fw-prep > div:hover { transform: translate(-2px, -2px); }
.fw-steps > div:hover::before, .fw-row > div:hover::before, .fw-prep > div:hover::before { filter: drop-shadow(7px 7px 0 rgba(17,17,17,.28)); }
@media (prefers-reduced-motion: reduce) {
  .tier, .handles > div, .fw-steps > div, .fw-row > div, .fw-prep > div, .fw-macro, .fw-result, .cmp { transition: none; }
  .tier:hover, .fw-steps > div:hover, .fw-row > div:hover, .fw-prep > div:hover { transform: none; }
}

/* ---- split-flap digits (bisected two-half Solari flaps) ----
   Photoreal exception scoped to this widget only. The digit is split across a
   real hinge gap so the seam cuts THROUGH the number, the tell of an actual
   split-flap. Warm matte-plastic flaps, recessed hinge channel, top flap
   drops on each change. The brand MARK stays flat per the guidelines. */
.ff { display: inline-flex; align-items: stretch; gap: 4px; }
.flap {
  position: relative;
  display: inline-block;
  width: 1.02em;
  height: 1.46em;
  font-family: var(--desc);
  font-variant-numeric: tabular-nums;
  border-radius: 0;
  background: #0a0908;
  filter: drop-shadow(0 2px 2px rgba(0,0,0,.55));
}
.flap-half {
  position: absolute;
  left: 0; right: 0;
  height: calc(50% - 0.6px);           /* tight 1.2px hinge channel */
  overflow: hidden;
  background: linear-gradient(180deg, #322d29 0%, #241f1c 52%, #1c1815 100%);
  backface-visibility: hidden;
}
.flap-top {
  top: 0;
  border-radius: 0;
  box-shadow: inset 0 1px 0 rgba(255,255,255,.14), inset 0 -1px 3px rgba(0,0,0,.5);
  transform-origin: 50% 100%;          /* hinge at the channel */
  z-index: 2;
}
.flap-bot {
  bottom: 0;
  border-radius: 0;
  background: linear-gradient(180deg, #211d1a 0%, #171310 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.05), inset 0 -1px 0 rgba(0,0,0,.6);
}
/* one glyph occupying the full flap height inside each half, positioned so the
   SAME character spans both halves continuously and the seam bisects it */
.flap-half b {
  position: absolute;
  left: 0; right: 0;
  height: 1.46em;
  line-height: 1.46em;
  font-weight: 700;
  font-style: normal;
  font-size: 1.02em;
  text-align: center;
  color: #e8e2d6;
  text-shadow: 0 1px 1px rgba(0,0,0,.75);
  transform: translateY(-0.045em);     /* lift so the digit's optical centre lands on the seam */
}
.flap-top b { top: 0; }
.flap-bot b { bottom: 0; }

/* recessed hinge channel with a lit lower lip */
.flap::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: calc(50% - 0.6px);
  height: 1.2px;
  background: #000;
  box-shadow: inset 0 1px 1px rgba(0,0,0,.9), 0 1px 0 rgba(255,255,255,.06);
  z-index: 3;
  pointer-events: none;
}

/* the top flap drops on each change */
.flap-top.fold { animation: flapdrop 105ms cubic-bezier(.36,0,.66,1); }
@keyframes flapdrop {
  0%   { transform: rotateX(-82deg); filter: brightness(1.6); }
  70%  { transform: rotateX(-8deg);  filter: brightness(1.06); }
  100% { transform: rotateX(0deg);   filter: brightness(1); }
}
@media (prefers-reduced-motion: reduce) {
  .flap-top.fold { animation: none; }
}

/* ---- editorial two-column desktop layout ----
   Section label/heading in a left rail, content fills the right. Text columns
   stay readable; the width comes from the asymmetric split, not long lines.
   Collapses to a single column on mobile. */
.editorial { display: block; }
.ed-head > :last-child { margin-bottom: 0; }
.ed-head .caption { color: inherit; }
.ed-body > :first-child { margin-top: 0; }

@media (min-width: 900px) {
  .editorial {
    display: grid;
    grid-template-columns: minmax(230px, 320px) minmax(0, 1fr);
    gap: clamp(24px, 3vw, 44px);
    align-items: start;
  }
  .editorial-wide { grid-template-columns: minmax(230px, 300px) minmax(0, 1fr); }
  /* the heading rail sticks briefly so it stays with its content on tall bodies */
  .ed-head { position: sticky; top: 96px; }
  .band-dark .ed-head, .band-close .ed-head { position: static; }
  .ed-body--center { display: flex; justify-content: flex-start; }
}

/* big pull-quote for the home testimonial */
.quote-big p { font-size: clamp(22px, 3vw, 34px); line-height: 1.28; max-width: 24ch; }

/* the before/after sliders sit two-up and taller on desktop */
@media (min-width: 900px) {
  .ed-body .cmps { margin-top: 0; }
}

/* wide standalone statement, no rail: the headline carries it */
.intro-statement h2 { max-width: 18ch; }

/* How-it-works: heading on top, wide equal boxes beneath */
.how-head { max-width: 44rem; margin-bottom: 1.1rem; }
.handles-wide { margin-top: 0; }

/* closing CTA fills the band: statement left, button right */
@media (min-width: 760px) {
  .cta-split {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: clamp(30px, 5vw, 72px);
  }
  .cta-split-action { margin: 0; flex: none; }
}

/* long-form editorial rows: each step is a rail + body pair */
/* hairline between steps so each one reads as its own block */
.case-row { padding-bottom: clamp(12px, 1.4vw, 17px); }
.case-row + .case-row {
  border-top: 1px solid var(--rule);
  padding-top: clamp(12px, 1.4vw, 17px);
}
.case-row:last-child { padding-bottom: 0; }
.case-row .ed-head h2 { margin-top: .3em; }
.case-row .step-label { margin-bottom: 0; }

/* thoughts: byline + title rail */
.prose .editorial { margin-bottom: 0; }
.prose .ed-head h2 { margin-top: .3em; }

/* Rail headings live in a ~360px column, so they cannot run at full h2 size.
   Scale them to the rail or long ones shred into many short lines. */
@media (min-width: 900px) {
  /* generic rails */
  .editorial > .ed-head > h2 { font-size: clamp(21px, 2vw, 28px); line-height: 1.16; }

  /* Long-form reads better stacked: label and heading above the prose, one
     readable column. The two-column rail fought the reading flow here. */
  /* one readable column, centred on the page rather than pinned left */
  .case-row.editorial,
  .prose .editorial {
    display: block;
    grid-template-columns: none;
    max-width: 46rem;
    margin-inline: auto;
  }
  .case-row.editorial > .ed-head,
  .prose .editorial > .ed-head {
    position: static;
    margin-bottom: .4rem;
    max-width: none;
  }
  .case-row.editorial > .ed-head > h2,
  .prose .editorial > .ed-head > h2 {
    font-size: clamp(30px, 4vw, 44px);
    line-height: 1.08;
  }
  .case-row.editorial > .ed-body,
  .prose .editorial > .ed-body { max-width: none; }
}

/* long-form sections share one centred reading axis */
.case > p, .case > h2, .case > h3 { max-width: 46rem; margin-inline: auto; }
@media (min-width: 900px) {
  .case-row { max-width: 46rem; margin-inline: auto; }
}

/* ---- long-form reading column ----
   The global `p { max-width: 36rem }` was capping every paragraph at 576px no
   matter how wide its container, which is why the case study only used a third
   of the page. Release it here and scale the type up with the measure so the
   character count per line stays readable at the wider width. */
.case p, .case li, .case h2, .case h3, .case .desc { max-width: none; }
@media (min-width: 900px) {
  .case-row.editorial,
  .prose .editorial,
  .case-row,
  .case > p, .case > h2, .case > h3 { max-width: 60rem; }
  .case { font-size: 20px; }
  .case p, .case li { line-height: 1.7; }
  .case-row.editorial > .ed-head > h2,
  .prose .editorial > .ed-head > h2 { font-size: clamp(22px, 2.1vw, 28px); }
  /* tables and card grids use the full container, wider than the prose */
  .case .scroll-x, .case .options, .case .handles { max-width: none; }
}

/* ---- release the global paragraph cap on the other prose pages ----
   Same fix as .case: the 36rem cap was overriding every container. Each of
   these sets its own centred measure with type scaled to match. */
.prose p, .prose li, .prose h2 { max-width: none; }
.faqs p, .faqs li, .faqs h3, .faqs summary { max-width: none; }
.about-me p { max-width: none; }
.ed-body > p, .ed-head > p, .how-head p, .intro-statement p { max-width: none; }
.tier-who, .tier-note, .handles-note, .fineprint { max-width: none; }

@media (min-width: 900px) {
  /* Thoughts: same centred reading column as the case study */
  .prose .editorial { max-width: 60rem; }
  .prose { font-size: 20px; }
  .prose p, .prose li { line-height: 1.7; }

  /* About: bio and FAQ bodies fill their editorial column */
  .about-me { max-width: none; }
  .faqs .ed-body { max-width: none; }
  .faqs summary { font-size: 20px; }

  /* Prep Plan: guide prose fills its column */
  .ed-body p, .ed-head p { max-width: none; }
}

/* Prep Plan's guide prose sits as direct section children, so the .ed-body
   release missed it. Give those a wide centred measure of their own. */
@media (min-width: 900px) {
  .wrap-wide > p, .wrap-wide > h2 { max-width: 60rem; }
  #service p, #guide p { max-width: 60rem; }
}

/* Domestic / International ad city lists */
.cities { display: grid; gap: clamp(20px, 3vw, 40px); margin: 1.2rem 0 1.6rem; }
@media (min-width: 640px) { .cities { grid-template-columns: 1fr 1fr; max-width: 40rem; } }
.cities .desc { letter-spacing: .12em; padding-left: 0; margin-bottom: 10px; color: var(--gold); }
.cities ul { list-style: none; padding: 0; margin: 0; }
.cities li { padding: 4px 0; border-bottom: 1px solid var(--rule); }

/* ---- 5-star review ----
   Standard star glyphs, not the brand sparkle: this is a rating, and the
   sparkle stays reserved for the mark per the guidelines. */
.review {
  margin: 0;
  max-width: 46rem;
  background: var(--paper);
  color: var(--ink);
  border-radius: var(--r-card);
  padding: clamp(22px, 2.8vw, 32px);
  box-shadow: 0 0 0 2px var(--gold);
}
.band-dark .review { background: var(--surface); }
.review-stars {
  font-size: 22px;
  letter-spacing: .16em;
  color: var(--gold);
  margin: 0 0 8px;
  line-height: 1;
  max-width: none;
}
.review-who {
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .14em;
  font-size: 14px;
  color: var(--ink);
  margin: 0 0 12px;
  max-width: none;
}
.review-quote {
  font-family: var(--display);
  font-size: clamp(19px, 2.2vw, 26px);
  line-height: 1.32;
  margin: 0;
  max-width: none;
}

/* review sitting under the board on the home page */
.ed-body .stats + .review { margin-top: clamp(18px, 2.4vw, 26px); }

/* ---- result section: heading across the top, board and review side by side ---- */
.result-head { margin-bottom: 1.2rem; }
.result-head h2 { margin-bottom: .2em; }
.result-head .caption { margin-top: 0; }
.result-grid { display: grid; gap: clamp(18px, 2.4vw, 30px); align-items: stretch; }
@media (min-width: 1000px) {
  /* The clock is the evidence and the quote corroborates it, so they are not
     peers. The quote also sits low, breaking the shared top edge. */
  .result-grid { grid-template-columns: minmax(0, 1.5fr) minmax(20rem, 1fr); align-items: start; }
  .result-grid .review { margin-top: clamp(30px, 4.5vw, 70px); }
}
.result-grid .stats { width: auto; }
.result-grid .review { max-width: none; display: flex; flex-direction: column; justify-content: center; }

@media (min-width: 1000px) {
  .result-grid .stats .num { font-size: clamp(22px, 2.4vw, 30px); }
  .result-grid .stats .lbl { width: clamp(96px, 12vw, 150px); }
}

/* tighter paragraph rhythm in long-form so steps read as blocks, not islands */
.case p, .prose p { margin: 0 0 .62em; }
.case h3, .prose h3 { margin-top: 1.1rem; }
.case .scroll-x, .case .options, .case .tier-ref, .case .cities { margin-block: 1rem; }
.case-row .ed-body > :last-child { margin-bottom: 0; }

/* CTA band copy was still hitting the global 36rem paragraph cap, wrapping a
   line that fits easily on one row. */
.band-close p, .band-dark .cta-split p { max-width: none; }

/* the case-study review is a paragraph, not a one-liner: step the display
   size down so it reads as testimony rather than a headline */
.case-study-review .review-quote,
.review-quote--long {
  font-family: var(--sans);
  font-weight: 500;
  font-size: 19px;
  line-height: 1.65;
}


/* ---- case study: the two on-page steps ----
   Constraining the prose to 38rem fixed a 103-character line, but left the text
   hugging the left of a 960px row with 40% of the width empty. The rail earns
   that space instead: label and heading sit in their own column and stay with
   the reader, and the body keeps the same readable measure. */
.case-read .ed-body > p,
.case-read .ed-body > h3 { max-width: 38rem; }
@media (min-width: 900px) {
  .case-read .case-row.editorial {
    display: grid;
    grid-template-columns: minmax(210px, 290px) minmax(0, 1fr);
    gap: clamp(28px, 3.6vw, 56px);
    max-width: none;
    align-items: start;
  }
  /* Sticks only for the height of its own row, so the heading is still on
     screen while you read the bottom of a long body. */
  .case-read .case-row.editorial > .ed-head { position: sticky; top: 110px; }
  .case-read .case-row.editorial > .ed-head h2 {
    font-size: clamp(21px, 1.9vw, 26px);
    line-height: 1.18;
    max-width: none;
  }
}

/* ---- white surfaces that break up the cream ----
   One idea throughout: a group of related content that the eye should take in
   as a unit becomes a surface. Continuous prose does not, because a box around
   a page of text adds a line rather than organisation. */

/* An open FAQ becomes an object; closed ones stay a quiet list. The hairline
   is suppressed on the open item and its neighbour, otherwise a rule cuts
   across the top and bottom of the card. */
.faqs details[open] {
  background: var(--paper);
  border-top-color: transparent;
  border-radius: var(--r-card);
  box-shadow: 4px 4px 0 rgba(17,17,17,.10);
  padding: 2px 24px 18px;
  margin: 14px 0;
}
.faqs details[open] + details { border-top-color: transparent; }
.faqs details[open]:last-of-type { border-bottom-color: transparent; }
/* Nested panels invert: the open card is now the surface, so its children take
   the cream so they stay legible as sub-groups instead of white on white. */
.faqs details[open] .faq-col {
  background: var(--surface);
  box-shadow: none;
  border: 1px solid var(--rule);
}

/* Reference blocks inside prose: the prep cost table and the option grids.
   These are lookup material, not reading, which is exactly what a card is for. */
.case-read .scroll-x,
.prep-costs {
  background: var(--paper);
  border-radius: var(--r-card);
  box-shadow: 4px 4px 0 rgba(17,17,17,.10);
  padding: clamp(16px, 2vw, 24px) clamp(18px, 2.2vw, 26px);
  margin: 1.4rem 0 1.8rem;
}
.case-read .scroll-x table { margin: 0; }

/* .options were outline-only boxes on cream, the same ruled-region problem the
   tiers had. */
.options > div {
  background: var(--paper);
  box-shadow: 4px 4px 0 rgba(17,17,17,.10);
  border: 0;
}

/* The contact form is the conversion point and sat bare on cream while the
   Questions? aside beside it was already a card, which read as lopsided.
   Inputs invert to cream so they stay visible on the white surface. */
.contact-form {
  background: var(--paper);
  border-radius: var(--r-card);
  box-shadow: 4px 4px 0 rgba(17,17,17,.10);
  padding: clamp(22px, 3vw, 32px);
}
/* White on cream is only 13 points of separation, so a hairline border left the
   fields reading as flat panels rather than something you type into. They take
   the same 2px ink frame and hard offset the hero controls use, which is the
   treatment on this site that reads as an active control. */
.contact-form input:not([type=radio]):not([type=checkbox]),
.contact-form textarea,
.contact-form select,
.contact-form gmp-place-autocomplete {
  background: var(--surface);
  border: 2px solid var(--ink);
  box-shadow: 3px 3px 0 rgba(17,17,17,.20);
}
.contact-form input:not([type=radio]):not([type=checkbox]):focus,
.contact-form textarea:focus,
.contact-form select:focus,
.contact-form gmp-place-autocomplete:focus-within {
  outline: 3px solid var(--gold);
  outline-offset: 2px;
}
/* The guarantee is the same width as the field directly above it, so with no
   gap the black bar read as part of the control. It is a separate claim and
   needs to sit apart from the thing you type in. */
.contact-form .guarantee { margin-top: clamp(14px, 2vw, 22px); }
/* The label is the field's name, so it carries the weight the hairline used to
   ask the border to carry. */
.contact-form label {
  display: block;
  font-weight: 700;
  font-size: 15px;
  margin-bottom: 7px;
}

/* The result comparison lifts off the mallard band onto a surface, so the
   numbers read as a document rather than text on a field. Inverts back to ink. */
.result-card {
  background: var(--surface);
  border-radius: var(--r-card);
  padding: clamp(18px, 2.4vw, 28px);
  margin-top: 1.6rem;
}
.result-card th, .result-card td { border-color: var(--rule); color: var(--ink); }
.result-card .th-sub { color: var(--ink); }

/* ---- single service panel ----
   Replaces the three-up tier grid. One card in a 3-column grid reads as two
   missing options, and a single narrow card stranded mid-page reads as an
   afterthought, so the offer goes full width and splits: the decision on the
   left rail, the terms on the right. */
.offer {
  background: var(--paper);
  border-radius: var(--r-card);
  box-shadow: 6px 6px 0 rgba(17,17,17,.14);
  padding: clamp(24px, 3vw, 40px);
  display: grid;
  gap: clamp(24px, 3.5vw, 48px);
  align-items: start;
}
@media (min-width: 820px) {
  .offer { grid-template-columns: minmax(240px, 300px) minmax(0, 1fr); }
  /* Hairline between the rail and the terms, echoing the rules inside the old
     tier cards rather than introducing a new divider treatment. */
  .offer-body { border-left: 1px solid var(--rule); padding-left: clamp(24px, 3vw, 40px); }
}
.offer .tier-badge { width: min(80%, 240px); margin: 0 auto 14px; }
.offer .tier-price { margin-bottom: 14px; padding-bottom: 14px; }
.offer .tier-cta { margin: 0; }
/* The guarantee and the button close the panel together, spanning both columns:
   the promise reads left, the action sits right of it. */
.offer-close {
  grid-column: 1 / -1;
  display: grid;
  gap: clamp(16px, 2vw, 26px);
  align-items: center;
  border-top: 1px solid var(--rule);
  padding-top: clamp(18px, 2.2vw, 26px);
}
@media (min-width: 720px) {
  .offer-close { grid-template-columns: minmax(0, 1fr) auto; }
  /* Auto width here, not the full-bleed block it uses inside a narrow card. */
  .offer-close .btn { display: inline-block; white-space: nowrap; padding: 15px 30px; font-size: 18px; }
}
.offer-close .guarantee { margin: 0; }
/* The rule under .tier-who separated it from the terms inside a narrow card.
   In the split layout the terms sit in their own column, so it is redundant. */
.offer .tier-who { border-bottom: 0; padding-bottom: 0; margin-bottom: 16px; font-size: 20px; }
.offer .tier-note { margin-top: 18px; }

/* ---- framework: the hexagon marks one node, not all of them ----
   The prep work is the pivot of the diagram (four inputs feed it, the sale
   flows out of it), so it keeps the badge shape. Every other node is a
   rectangle, which also hands the labels back the width the angled points
   were eating. A border can trace a rectangle, so those keylines stop being
   stretched inline SVGs. */
.fw-steps > div,
.fw-row > div,
.fw-prep > div,
.fw-result { padding-inline: 3.5%; }

.fw-steps > div::before,
.fw-row > div::before,
.fw-prep > div::before,
.fw-result::before { clip-path: inset(0 round 14px); }

.fw-steps > div::after,
.fw-row > div::after,
.fw-prep > div::after {
  background-image: none;
  border: 1px solid rgba(41,78,79,.55);
  border-radius: 0;
}
.fw-result::after {
  background-image: none;
  border: 1px solid var(--gold);
  border-radius: 0;
}
/* The hover lift casts its shadow off the clipped silhouette, so the two
   shapes need their own. */
.fw-steps > div:hover::before,
.fw-row > div:hover::before,
.fw-prep > div:hover::before { filter: drop-shadow(7px 7px 0 rgba(17,17,17,.28)); }

/* ---- budget guarantee ----
   The strongest risk-reversal on the site, so it stops being a quiet footnote
   and becomes the last thing read before the button. Gold because it is the
   one promise on the page, and gold is reserved for exactly that kind of
   accent; a tint rather than solid so it lifts without shouting over the CTA
   sitting immediately under it. */
/* A solid black bar. The gold tint read as a soft aside sitting on paper; the
   guarantee is the strongest thing on the form and needs to stop the eye
   before the button does. */
.guarantee {
  background: var(--ink);
  border-radius: 0;
  padding: 16px 20px 17px;
  margin: 0 0 16px;
  font-size: 16px;
  line-height: 1.45;
  color: #fff;
  max-width: none;
}
/* The label leads the bar, so it outranks the body copy instead of sitting
   above it as a small caption. White, not gold: gold on black is the weaker
   pair of the two and it read as a label rather than the headline of the bar. */
.guarantee span {
  display: block;
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .08em;
  font-size: 20px;
  line-height: 1.15;
  color: #fff;
  margin-bottom: 9px;
}
/* On mallard, black on mallard is too close a value pair, so it inverts to a
   white bar with ink type. */
.band-dark .guarantee { background: #fff; color: var(--ink); }
.band-dark .guarantee span { color: var(--ink); }

/* ---- framework: taper the rows into an inverted pyramid ----
   Widths run 100% / 87% / 75% / 25% top to bottom, so the chart narrows to a
   point instead of pinching in the middle. The prep work spans nearly the full
   width because everything above feeds into it.
   It has to drop the badge aspect ratio: at 87% wide, 1.606 would make it ~600px
   tall. A wide, shallow bar reads as the trunk of the funnel anyway. */
.fw-prep { width: calc(var(--tile) * 3.5 + var(--gap) * 2.5); }
.fw-prep > div {
  aspect-ratio: auto;
  min-height: clamp(60px, 6.5cqw, 96px);
  padding-block: clamp(14px, 1.6cqw, 22px);
}

/* Two panels in a row would otherwise touch. */
.band-dark + .band-dark,
.band-dark + .band-close,
.band-close + .band-dark { margin-top: clamp(14px, 2vw, 26px); }

/* ---- break the grid ----
   The gallery panel and the section under it both stopped at the same centred
   column, so every edge on the page lined up. Letting the gallery run wider
   than its own heading gives the page one deliberate asymmetry to read against. */
@media (min-width: 1000px) {
  .band-dark .gallery { margin-inline: calc(clamp(10px, 1.6vw, 30px) * -1); }
}

/* ---- Google Places widget ----
   The autocomplete replaces the address input with a <gmp-place-autocomplete>
   custom element, so it has to be dressed to match the field it stands in for.
   ::part(input) is the only way in; the host element carries the frame. */
gmp-place-autocomplete {
  display: block;
  width: 100%;
  color-scheme: light;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: 0;
  /* The element it stands in for is an input, which carries this. Without it
     whatever follows butts straight onto the field. */
  margin-bottom: 18px;
}
gmp-place-autocomplete::part(input) {
  width: 100%;
  box-sizing: border-box;
  background: transparent;
  border: 0;
  outline: none;
  box-shadow: none;
  color: var(--ink);
  font-family: var(--sans);
  font-size: 19px;
  font-weight: 500;
  line-height: 1.4;
  padding: 14px 15px;
}
/* In the hero it has to match the heavier treatment the other controls got. */
.hero-form gmp-place-autocomplete { margin-bottom: 0; }
.hero-form gmp-place-autocomplete {
  border: 2px solid var(--ink);
  border-radius: 0;
  box-shadow: 4px 4px 0 rgba(17,17,17,.42);
  height: 100%;
}
.hero-form gmp-place-autocomplete::part(input) { font-size: 18px; padding: 15px 16px; }
.hero-form gmp-place-autocomplete:focus-within { outline: 3px solid var(--gold); outline-offset: 2px; }
/* Inside a cream form card the field inverts, same as the other inputs. */
.contact-form gmp-place-autocomplete { background: var(--surface); }

/* ---- tap targets ----
   Anything a thumb has to hit gets a 44px minimum. Applied by padding the link
   rather than growing the type, so nothing about the layout changes visually.
   85% of this site's traffic is a phone. */
.site-footer a,
.legal a {
  display: inline-block;
  padding-block: 10px;
}
/* The footer contact block stacks phone and email on separate lines, so the
   padding would double the block height without this. */
.site-footer p a { padding-block: 8px; }

@media (max-width: 700px) {
  /* Gallery arrows sit either side of the photo and were at the low end of the
     clamp on the narrowest screens. */
  .gnav { width: 46px; height: 46px; }
  /* The drag handle on the comparison sliders. */
  .cmp-handle { width: 52px; height: 52px; font-size: 15px; }
}

@media (max-width: 700px) {
  /* The offer panel stacked to 986px on a phone: badge, price, terms, guarantee
     and button all at desktop scale. Everything tightens, and the badge in
     particular does not need to be that large once it is the only thing on the
     row. */
  .offer { padding: clamp(18px, 5vw, 26px); gap: 20px; }
  .offer .tier-badge { width: min(58%, 190px); margin-bottom: 10px; }
  .offer .tier-price { font-size: clamp(30px, 9vw, 38px); margin-bottom: 10px; padding-bottom: 10px; }
  .offer .tier-who { font-size: 18px; margin-bottom: 12px; }
  .offer .tier-terms li { padding-bottom: 8px; }
  .offer-close { padding-top: 18px; gap: 14px; }
  .offer-close .btn { display: block; text-align: center; }
}

/* ================= visual rhythm =================
   Measured on a 388px phone: type ran 13-32px, 12 of 17 images were the same
   1.50 ratio, and section gaps were 94/0/0/14. That is a metronome, and it is
   what reads as a flat linear grid. Three fixes, in order of effect.

   1. SCALE CONTRAST. Size does the work a grid does. A 32px maximum against
      19px body gives nothing to anchor on, so the one headline per screen that
      matters gets genuinely large and everything else stays where it is. */
@media (max-width: 700px) {
  .hero-offer-h { font-size: clamp(36px, 11.5vw, 50px); line-height: 1.02; letter-spacing: -0.01em; }
  .band-dark .how-head h2,
  .band-dark > .wrap > h2,
  .band-close h2 { font-size: clamp(30px, 8.6vw, 40px); line-height: 1.06; }
  h1 { font-size: clamp(32px, 9vw, 42px); line-height: 1.06; }
}

/* 2. FULL BLEED AGAINST INSET. On one column there is no horizontal placement
      to vary, so the contrast has to come from width: photography runs to the
      edge of its panel while the text stays in the gutter. */
@media (max-width: 700px) {
  /* --page-gutter, not --gutter: inside a .gallery (and inside .shots, which
     sits in one) --gutter is the arrow rail, 46px and 38px here, so negating it
     overshot the 20px page inset. The before pager sat 26px off the left edge
     and the after grid rendered 76px wider than its own slide. */
  .band-dark .gallery,
  .band-dark .fw-scroll { margin-inline: calc(var(--page-gutter) * -1); }
  .band-dark .gallery .gviewport { border-radius: 0; }
  /* max-width has to come off with the negative margin. Capped at 100% the
     element stayed at the content width and simply slid left, hanging off one
     edge and leaving a gutter of dead space on the other. */
  #photos .gallery,
  .after-pager,
  .band-dark .gallery { margin-inline: calc(var(--page-gutter) * -1); max-width: none; }
  .shots { margin-inline: calc(var(--page-gutter) * -1); }
  /* .shots got its own bleed back when it sat straight in a section. Inside a
     carousel the gallery above has already bled, and .gviewport clips, so
     bleeding again just cut 20px off each side of every photo. */
  .gallery .shots { margin-inline: 0; }
}

/* 3. PACE. Uniform gaps read as a list. Alternating them gives the scroll a
      beat: a tight join reads as one idea continuing, a wide one as a new
      subject starting. */
@media (max-width: 700px) {
  main > section { padding-block: clamp(38px, 9vw, 56px); }
  main > section + section { padding-top: clamp(26px, 6vw, 38px); }
  main > .band-dark,
  main > .band-close { margin-block: clamp(28px, 7vw, 44px); }
  main > .band-dark + section { padding-top: clamp(40px, 10vw, 60px); }
}

/* A salon hang was tried on the before carousel and reverted. The numbers:
   the "dominant" piece rendered 700x202 (3.46:1), the wide close 1056x131
   (8.09:1), and page 3's portraits came out 2.58:1 landscape. Cause: every page
   must be the same height or the carousel jumps, and six images inside one
   fixed height leaves no room for real scale variation. Varying the sizes would
   mean fewer images per page or a much taller gallery. Uniform rows are the
   honest answer here; the salon hang lives on .shots, which has no height cap. */

/* ================= The 16DayPrep System =================
   Built to a supplied design. Cream ground; white cards for the eight steps;
   mallard for the two moments that are not steps (the prep, and the outcome);
   gold for the numbers, the rules, and every connector.
   The connectors are borders and gradients rather than SVG so they scale to any
   width without distorting, and their x-positions are derived from the real
   column centres (gap included) rather than idealised percentages. */

/* Cream panel, as in the reference. The page ground is white and the step cards
   are white, so without this the cards have nothing to sit on.
   No margin-inline or max-width override here: .wrap already sets width:100% and
   a max-width, and adding margins on top of a 100% width pushed the panel 33px
   past the viewport and gave the whole page a horizontal scrollbar. */
.sys-wrap {
  text-align: center;
  background: var(--paper);
  border-radius: 0;
  padding: clamp(24px, 3.2vw, 44px) clamp(14px, 2.4vw, 34px) clamp(26px, 3.4vw, 46px);
}
.sys-title {
  font-family: var(--display);
  font-size: clamp(26px, 3.6vw, 42px);
  line-height: 1.05;
  color: var(--mallard);
  margin: 0 0 10px;
  max-width: none;
}
.sys-title sup { font-size: .38em; vertical-align: super; }
.sys-title::after {
  content: ""; display: block; width: 64px; height: 2px;
  background: var(--gold); margin: 12px auto 0;
}
/* The system's name steps down to an eyebrow so the belief line, which is the
   actual claim, carries the section.
   These are centred TEXT inside boxes that are capped narrower than the wrap
   and have no auto margins, so the boxes themselves were hugging the left and
   every line above the diagram sat ~49px off centre while the deck, which does
   have auto margins, sat true. Uncap them and centre the boxes.
   .desc's padding-left corrects letter-spacing drift for left-aligned use;
   centred it just pushes the line off, and the trailing letter-space is
   already trimmed, so it comes off here. */
.sys-eyebrow { margin: 0 auto 14px; max-width: none; padding-left: 0; }
.sys-eyebrow sup { font-size: .5em; vertical-align: super; letter-spacing: 0; }
.sys-wrap .belief { margin: 0 auto clamp(16px, 2vw, 24px); max-width: none; }
.sys-wrap .belief-lead { color: var(--mallard); }
.sys-wrap .belief::after {
  content: ""; display: block; width: 64px; height: 2px;
  background: var(--gold); margin: clamp(14px, 1.8vw, 20px) auto 0;
}
.sys-deck {
  font-size: clamp(16px, 1.6vw, 20px);
  color: var(--ink);
  max-width: 44rem;
  margin: 0 auto clamp(20px, 2.8vw, 36px);
}

.sys-scroll { overflow-x: auto; }
.sys {
  --gap: clamp(14px, 2.2vw, 30px);
  --arrow: clamp(16px, 1.8vw, 26px);
  --t: calc((100% - 3 * var(--gap)) / 4);
  /* Connectors have to land on CARD centres, not tile centres. Each row also
     spends width on its lateral arrow tracks, so a card is narrower than a
     tile: t - 3a/4 in the four-up row, t - 2a/3 in the three-up. Ignoring that
     put the outer drops ~7px off their boxes. */
  --c4a: calc(var(--t) / 2 - var(--arrow) * 3 / 8);
  --c4b: calc(1.5 * var(--t) + var(--gap) - var(--arrow) / 8);
  --c4c: calc(2.5 * var(--t) + 2 * var(--gap) + var(--arrow) / 8);
  --c4d: calc(3.5 * var(--t) + 3 * var(--gap) + var(--arrow) * 3 / 8);
  --c3a: calc(var(--t) + var(--gap) / 2 - var(--arrow) / 3);
  --c3c: calc(100% - var(--t) - var(--gap) / 2 + var(--arrow) / 3);
  min-width: 680px;
  text-align: center;
}

/* ---- rows ---- */
.sys-row { display: grid; align-items: stretch; padding-top: 18px; }
/* The lateral arrow sits in its own track between cards, so the cards stay
   equal and the arrow is never squeezed by them. */
.sys-4 { grid-template-columns: 1fr auto 1fr auto 1fr auto 1fr; gap: 0 calc(var(--gap) / 2); }
.sys-3 {
  grid-template-columns: 1fr auto 1fr auto 1fr;
  gap: 0 calc(var(--gap) / 2);
  width: calc(var(--t) * 3 + var(--gap) * 2);
  margin-inline: auto;
}
.sys-1, .sys-end { grid-template-columns: 1fr; }
/* Taper: 4 wide -> 3.4 -> 3 -> 1.45. Full width here read as a divider, and
   flattened the funnel the rows are meant to describe. */
.sys-1 { width: calc(var(--t) * 3.4 + var(--gap) * 2.4); margin-inline: auto; }
.sys-end { width: calc(var(--t) * 1.45); margin-inline: auto; }

/* ---- cards ---- */
.sys-card {
  position: relative;
  background: var(--surface);
  border-radius: 0;
  padding: clamp(24px, 2.4vw, 30px) clamp(12px, 1.4vw, 18px) clamp(14px, 1.5vw, 19px);
  box-shadow: 0 6px 18px rgba(17,17,17,.10);
  cursor: pointer;
  text-align: center;
  transition: transform .16s ease, box-shadow .16s ease;
}
.sys-card:hover { transform: translateY(-3px); box-shadow: 0 12px 26px rgba(17,17,17,.16); }
.sys-card:focus-visible { outline: 3px solid var(--gold); outline-offset: 3px; }

/* The number straddles the top edge, which is what makes the row read as a
   numbered sequence rather than four unrelated boxes. */
.sys-num {
  position: absolute;
  top: 0; left: 50%;
  transform: translate(-50%, -50%);
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--surface);
  font-family: var(--desc);
  font-weight: 700;
  font-size: 13px;
  letter-spacing: .04em;
  display: grid;
  place-items: center;
}
.sys-icon { display: block; color: var(--mallard); margin: 2px auto 8px; }
/* margin:auto on the wrapper does nothing: it is display:block and therefore
   already full width, so the SVG inside was sitting at its left edge. The
   centring has to happen on the SVG. */
.sys-icon svg { width: clamp(26px, 2.5vw, 34px); height: auto; display: block; margin-inline: auto; }
.sys-card h3 {
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(16px, 1.5vw, 19px);
  line-height: 1.15;
  color: var(--mallard);
  margin: 0 0 7px;
}
.sys-card h3::after {
  content: ""; display: block; width: 34px; height: 1.5px;
  background: var(--gold); margin: 7px auto 0;
}
.sys-card p {
  font-size: clamp(12.5px, 1.05vw, 14px);
  line-height: 1.45;
  color: var(--ink);
  margin: 0;
  max-width: none;
}

/* ---- the two mallard moments ---- */
.sys-dark { background: var(--mallard); box-shadow: 0 8px 22px rgba(17,17,17,.20); }
.sys-dark h3 { color: var(--surface); }
.sys-dark p { color: var(--surface); }
.sys-dark .sys-icon { color: var(--surface); }

/* Prep work is wide, so the icon sits beside the words rather than above. */
.sys-1 .sys-card { padding-inline: clamp(20px, 3vw, 40px); }
.sys-1 h3::after { display: block; }

.sys-sold { border: 2px solid var(--gold); cursor: default; }
.sys-sold:hover { transform: none; box-shadow: 0 8px 22px rgba(17,17,17,.20); }
.sys-sold .sys-icon { color: var(--gold); }
.sys-sold h3 { font-size: clamp(22px, 2.6vw, 32px); }
.sys-sold h3::after { background: var(--gold); width: 54px; }
.sys-sold-tag {
  font-family: var(--desc);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .18em;
  font-size: clamp(10px, 1vw, 12px);
  color: var(--surface) !important;
}

/* ---- lateral arrows between peers ---- */
.sys-arrow { align-self: center; color: var(--gold); display: block; }
.sys-arrow svg { width: var(--arrow); height: auto; display: block; }

/* ---- joins ---- */
.sys-join { position: relative; height: clamp(24px, 2.5vw, 34px); color: var(--gold); }
.sys-join .bus { position: absolute; height: 0; border-top: 1.4px solid currentColor; }
.sys-join .stem { position: absolute; left: 50%; width: 0; border-left: 1.4px solid currentColor; }
.sys-join .stem::after {
  content: ""; position: absolute; bottom: -1px; left: -5px;
  border: 5px solid transparent; border-top-color: currentColor;
}
/* short stubs into a bus, then one long line: that is what reads as converging */
.j-merge4 .bus { left: var(--c4a); right: var(--c4a); top: 30%; }
.j-merge4 .stem { top: 30%; bottom: 0; }
.j-merge4 { background-image:
  linear-gradient(currentColor,currentColor), linear-gradient(currentColor,currentColor),
  linear-gradient(currentColor,currentColor), linear-gradient(currentColor,currentColor);
  background-size: 1.4px 30%, 1.4px 30%, 1.4px 30%, 1.4px 30%;
  background-position: var(--c4a) 0, var(--c4b) 0, var(--c4c) 0, var(--c4d) 0;
  background-repeat: no-repeat; }

.j-merge3 .bus { left: var(--c3a); right: var(--c3a); top: 30%; }
.j-merge3 .stem { top: 30%; bottom: 0; }
.j-merge3 { background-image:
  linear-gradient(currentColor,currentColor), linear-gradient(currentColor,currentColor),
  linear-gradient(currentColor,currentColor);
  background-size: 1.4px 30%, 1.4px 30%, 1.4px 30%;
  background-position: var(--c3a) 0, 50% 0, var(--c3c) 0;
  background-repeat: no-repeat; }

/* one short stem out of prep, then three drops, each arriving with a head */
.j-split3 .stem-top { top: 0; height: 34%; }
.j-split3 .stem-top::after { display: none; }
.j-split3 .bus-bottom { top: 34%; left: var(--c3a); right: var(--c3a); }
.j-split3 .tip {
  position: absolute; top: 34%; bottom: 0; width: 0;
  margin-left: -0.7px; border-left: 1.4px solid currentColor;
}
.j-split3 .tip::after {
  content: ""; position: absolute; bottom: -1px; left: -5px;
  border: 5px solid transparent; border-top-color: currentColor;
}
.j-split3 .t1 { left: var(--c3a); }
.j-split3 .t2 { left: 50%; }
.j-split3 .t3 { left: var(--c3c); }

@media (max-width: 700px) {
  .sys { min-width: 620px; }
  .sys-num { width: 32px; height: 32px; font-size: 13px; }
}

/* Bridge from the system diagram into the two worked examples. The diagram is
   the general method; these two are that method applied to one address, and
   without a line saying so the reader arrives at "The Home" with no idea why
   only two of the eight steps are expanded. */
.case-bridge {
  font-size: clamp(18px, 1.8vw, 23px);
  line-height: 1.45;
  color: var(--ink);
  max-width: 40rem;
  margin: 0 0 clamp(26px, 3.4vw, 44px);
  padding-bottom: clamp(20px, 2.6vw, 32px);
  border-bottom: 1px solid var(--rule);
}
@media (min-width: 900px) {
  /* Sits on the same axis as the rail so it lines up with the labels below. */
  .case-read .case-bridge { max-width: 46rem; }
}

/* Sold is an end marker, so it does not need a step card's proportions. */
.sys-sold { padding: clamp(18px, 2vw, 26px) clamp(14px, 1.6vw, 22px) clamp(14px, 1.6vw, 20px); }
.sys-sold .sys-icon { margin-bottom: 4px; }
.sys-sold .sys-icon svg { width: clamp(24px, 2.2vw, 30px); }


/* ---- after photos: page 1 untouched, page 2 a plain 2x2 ----
   The .gallery component normally reserves a wide side gutter for its arrows.
   Applying that here would have narrowed page 1, which is the one thing this
   must not do, so the gutter is zero and the arrows sit over the photos. */
/* Sized off viewport HEIGHT, not width. The composition's height is a fixed
   fraction of its width (~0.53 for a 4-col 3:2 hero over a row of 2-col tiles),
   so at full page width the gallery ran past the bottom of a desktop screen.
   Capping the width at ~1.6x the height budget is what keeps the whole set
   visible at once without cropping anything to do it. */
/* Arrows sit in the side gutters, clear of the photo edges, same as the before
   gallery. The gutter is tighter than the default so it costs less width, and
   the cap is raised by both gutters so reserving that space does not shrink
   the photos themselves. */
#after-photos { --ag: clamp(38px, 4.2vw, 58px); --acap: min(100%, calc(108vh + 2 * var(--ag))); }
.after-pager {
  --gutter: var(--ag);
  max-width: var(--acap);
  margin-inline: auto;
}
.after-pager .gprev { left: 0; }
.after-pager .gnext { right: 0; }
/* The "After" label tracks the photo's left edge, which is now the centred
   pager's own inset plus its gutter, not the section edge. Same cap and auto
   margins as the pager, then indented by one gutter. The .14em keeps .desc's
   letter-spacing nudge. */
.desc:has(+ .after-pager) {
  max-width: var(--acap);
  margin-inline: auto;
  padding-left: calc(var(--ag) + .14em);
}
/* No arrow rail on a phone, so no indent to track, and the pager bleeds to the
   screen edge like the Before one does. Declared here rather than in the shared
   mobile block so these come after the desktop rules they override. */
@media (max-width: 700px) {
  .desc:has(+ .after-pager) { max-width: none; padding-left: .14em; }
  .after-pager { max-width: none; margin-inline: calc(var(--page-gutter) * -1); }
}

/* Page 2: four equal tiles, 3 columns each, both rows the same. Two rows of
   3-column 3:2 tiles come within ~10px of page 1's three-row height, so page 1
   is not padded out with dead space by the carousel equalising them. */
.shots-quad > :nth-child(1) { grid-column: 1 / 4; grid-row: 1; }
.shots-quad > :nth-child(2) { grid-column: 4 / 7; grid-row: 1; }
.shots-quad > :nth-child(3) { grid-column: 1 / 4; grid-row: 2; }
.shots-quad > :nth-child(4) { grid-column: 4 / 7; grid-row: 2; }
.shots-quad > * { aspect-ratio: 3 / 2; height: auto; }
@media (max-width: 700px) {
  .shots-quad > :nth-child(n) { grid-column: auto; grid-row: auto; }
}

/* ---- revised flow through the sales stage ----
   The whole tail is one chain, no fan: Prep -> Marketing -> List Price ->
   Negotiation -> Sold. Prep and Sold are both centred while the chain enters at
   the left column and exits at the right, so the two joins are mirrored elbows:
   drop, run sideways, drop again into the box. */

/* Prep (centre) -> Marketing (first column). */
.j-elbow-l .drop {
  position: absolute; top: 0; height: 40%; width: 0;
  left: 50%; margin-left: -0.7px;
  border-left: 1.4px solid currentColor;
}
.j-elbow-l .bus { top: 40%; left: var(--c3a); right: 50%; }
.j-elbow-l .stem { top: 40%; bottom: 0; left: var(--c3a); }

/* Negotiation (last column) -> Sold (centre). */
.j-elbow .drop {
  position: absolute; top: 0; height: 40%; width: 0;
  left: var(--c3c); margin-left: -0.7px;
  border-left: 1.4px solid currentColor;
}
.j-elbow .bus { top: 40%; left: 50%; right: calc(100% - var(--c3c)); }
.j-elbow .stem { top: 40%; bottom: 0; left: 50%; }

/* ---- the How-it-works CTA ----
   Lifted out of the mallard panel so the button can be the brand mallard
   instead of the white it had to invert to in there. Centred under the panel,
   with the same breathing room the panel has above it. */
.how-cta { text-align: center; padding-top: clamp(26px, 3.4vw, 44px); }
.how-cta p { max-width: none; margin: 0; }

/* ---- consent banner ----
   Ink panel, square, sitting on the page rather than dimming it: this is a
   choice, not an interruption, and a full-screen scrim on a first visit is the
   thing that makes people leave. Accept and Decline are deliberately identical
   in size and weight. CPRA requires the two paths be equally easy to take, and
   a greyed-out Decline next to a bright Accept is the exact pattern that gets
   read as a dark pattern. */
.consent {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 90;
  background: var(--ink);
  color: #fff;
  border-top: 2px solid var(--gold);
  padding: clamp(16px, 2.2vw, 22px) var(--page-gutter);
}
.consent-inner {
  max-width: 1120px;
  margin-inline: auto;
  display: flex;
  align-items: center;
  gap: clamp(16px, 3vw, 40px);
  flex-wrap: wrap;
}
.consent-copy {
  flex: 1 1 22rem;
  margin: 0;
  max-width: none;
  font-size: 15px;
  line-height: 1.5;
  color: #fff;
}
.consent-copy a { color: #fff; text-decoration: underline; }
.consent-actions { display: flex; gap: 10px; flex: none; }
/* Both buttons: white on ink, identical. The band already inverts .btn, so
   this only has to stop them diverging on hover. */
.consent .btn {
  font-size: 16px;
  padding: 12px 26px;
  cursor: pointer;
  min-width: 116px;
  text-align: center;
}
@media (max-width: 560px) {
  .consent-inner { gap: 14px; }
  .consent-actions { width: 100%; }
  .consent .btn { flex: 1; min-width: 0; padding: 13px 10px; }
}
