/* ============================================================
   Cornell Group — cornellgroupllc.com
   Structure modeled on legora.com
   ============================================================ */

:root {
  --green:      #005032;
  --ink:        #0D1016;
  --paper:      #FAFAF9;
  --nav-h:      72px;
  --edge:       24px;
  --logo-cap:   14px;   /* height of THE CORNELL GROUP. */
  --mark-scale: 1.25;   /* the C, as a multiple of the above */
  --logo-gap:   5px;    /* space between the C and the T */
  --pill-pad:   13px;   /* side padding inside the nav hover pill */
  --logo-h:      127px; /* client logo height in the Trusted by strip */
  --logo-text:    30px; /* placeholder wordmarks, scaled to sit with that */
  --strip-inset:  41px; /* space between the label and the strip below it */
  --logo-tone:   .62;   /* black at this opacity = the dark grey they read as */
  --strip-gap:   68px;  /* space between logos in the marquee */
  --strip-speed: 40s;   /* one full loop; higher = slower */
  --fade:        10%;   /* fade width, same on both ends of the strip */
  --ease:       cubic-bezier(.22,.61,.36,1);
  --menu:       .16s;   /* every part of the nav open/close runs on this one clock */
  --nav-fs:     15.2px; /* nav links + Book a Demo. Was 13.8px, up 10%. */
  --card-pad:   26px;   /* inset inside the info card, all four sides */
  --card-shade: .55;    /* depth in the card's bottom-right corner; 0 = flat */
  --band-w:     min(1190px, 78vw);  /* shared width for every card in the info
                                       block, so the filled one and the outline
                                       pair below it always line up */
  --serif: "Playfair Display", "Times New Roman", Georgia, serif;
                        /* the editorial display face — feature headers only */
  --serif-wght: 450;    /* NOT 400. This is the weight that matches harvey.ai's
                           Ghost; the default is ~7% too light. */
}

/* ---- LEMON MILK (uppercase only, donationware — see fonts/LEMONMILK-LICENSE.txt) ---- */

/* WOFF2 first, OTF only as a fallback for anything that cannot read it.
   The OTFs were being served raw at ~34KB each with no font compression at
   all; WOFF2 is the same outlines Brotli-packed at ~21KB, and these two are
   on the critical path because they are preloaded in the <head>. */
@font-face {
  font-family: "Lemon Milk";
  src: url("/assets/fonts/LEMONMILK-Regular.woff2") format("woff2"),
       url("/assets/fonts/LEMONMILK-Regular.otf") format("opentype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Lemon Milk";
  src: url("/assets/fonts/LEMONMILK-Medium.otf") format("opentype");
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: "Lemon Milk";
  src: url("/assets/fonts/LEMONMILK-Bold.woff2") format("woff2"),
       url("/assets/fonts/LEMONMILK-Bold.otf") format("opentype");
  font-weight: 700;
  font-display: swap;
}

/* ---- PLAYFAIR DISPLAY, variable (OFL — see fonts/PLAYFAIRDISPLAY-LICENSE.txt)
   The editorial serif, for feature headers only. Never body copy.

   WHY THIS ONE: Roy asked for harvey.ai's display serif (2026-08-16). That
   face is TWK Ghost by TYPE.WELTKERN, licensed by Harvey and renamed
   "Harvey Serif" in their build. Its licence forbids redistribution, so it
   cannot be served from this domain and a stand-in was needed.

   Instrument Serif shipped here first and was WRONG — Roy called it, and the
   measurements agreed: against Ghost it came 25th of 30 candidates, far too
   condensed and too high-contrast.

   Playfair was chosen by rendering 30 free serifs at an identical cap height
   against the real Ghost and measuring each one. Playfair at weight 450 lands
   within 0.1% of Ghost's ink density and 1.2% of its set width, the closest
   of anything tested. THE 450 IS THE WHOLE POINT: at its default 400 it is
   ~7% too light and the match falls apart, which is why --serif-wght exists
   and why any header using this face must set it.

   TO SWAP IN THE REAL GHOST: buy it at type.weltkern.com, drop the webfont in
   /assets/fonts/, and repoint --serif. Nothing else changes. */
@font-face {
  font-family: "Playfair Display";
  src: url("/assets/fonts/PlayfairDisplay-Variable.woff2") format("woff2");
  font-weight: 400 900;          /* variable axis — do not collapse to one value */
  font-style: normal;
  font-display: swap;
}

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

/* ---- .nb — NEVER BREAK INSIDE (Roy's rule, 2026-08-16) ----
   A hyphen is a legal wrap point, so a browser will happily set "30-day" as
   "30-" on one line and "day" on the next. In a 61px display serif that reads
   as a typo. Any hyphenated term, date range, phone number, or price that
   must survive as one unit goes in this span.

   Use this instead of a hard <br>: a pinned break is correct at exactly one
   width and wrong at every other, while this stays right as the column
   resizes. */
.nb { white-space: nowrap; }

/* overscroll-behavior kills the macOS rubber-band bounce, so the page locks
   at the top instead of pulling past it. The black canvas is the backstop:
   on any browser that bounces anyway, the gutter matches the hero video
   instead of flashing the paper white. */
html {
  scroll-behavior: smooth;
  overscroll-behavior: none;
  background: #000;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: "Helvetica Neue", Inter, -apple-system, BlinkMacSystemFont, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

img, video, svg { display: block; max-width: 100%; }
a { text-decoration: none; color: inherit; }


/* ============================================================
   NAV
   ============================================================ */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  height: var(--nav-h);
  background: transparent;
  transition: background var(--menu) var(--ease), box-shadow var(--menu) var(--ease);
}

.nav-inner {
  height: 100%;
  padding: 0 var(--edge);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
}

/* left links.
   The pill's own padding is pulled back out with a negative margin, so the
   hover shape has room to breathe while "Case Studies" still starts on the
   same left edge as everything else on the page. */
.nav-left {
  display: flex;
  align-items: center;
  gap: 2px;
  margin-left: calc(var(--pill-pad) * -1);
}

/* nav type runs 15% larger than Legora's 12px */
.nav-left a {
  font-size: var(--nav-fs);
  font-weight: 400;
  letter-spacing: .01em;
  color: #fff;
  opacity: .92;
  padding: 8px var(--pill-pad);
  border-radius: 999px;
  transition: background .2s var(--ease),
              color var(--menu) var(--ease),
              opacity var(--menu) var(--ease);
}
.nav-left a:hover { opacity: 1; }

/* hover = white pill, black type. Services is skipped: it drops the menu
   and turns the whole bar white instead. */
.nav-left a:not(.nav-trigger):hover {
  background: #fff;
  color: var(--ink);
}

/* once the bar is already white, a white pill would vanish, so it goes grey */
.nav.is-scrolled .nav-left a:not(.nav-trigger):hover,
.nav.is-open     .nav-left a:not(.nav-trigger):hover {
  background: #F1F1F0;
  color: var(--ink);
}

/* button in the nav only — the hero button keeps its original size */
/* the nav-specific button override is gone; .btn now carries this spec */

/* center logo — real artwork, black ink on transparent.
   Mark and wordmark are separate so the C can be sized on its own.
   --mark-scale is the C's height as a multiple of the text cap height.
   The original lockup was 1.74 which read oversized. Lower it to shrink the C. */
.nav-logo {
  display: flex;
  align-items: center;
  gap: var(--logo-gap);
}

.nav-logo img {
  width: auto;
  filter: invert(1);                 /* white over the black hero */
  transition: filter var(--menu) var(--ease);
}
.logo-word { height: var(--logo-cap); }
.logo-mark { height: calc(var(--logo-cap) * var(--mark-scale)); }

.nav.is-scrolled .nav-logo img { filter: none; }   /* black on the white nav */

/* right side */
.nav-right {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 20px;
}


/* Full bar height so there is no dead gap between the link and the panel
   below it. Without this the mouse crosses un-hovered nav on the way down
   and the menu flickers shut. */
.nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: var(--nav-h);
}
.nav-caret {
  width: 9px;
  height: 6px;
  flex: none;
  transition: transform var(--menu) var(--ease);
}
.nav.is-open .nav-caret { transform: rotate(180deg); }


/* ---- white bar: on scroll, and while the services menu is open ---- */

.nav.is-scrolled,
.nav.is-open {
  background: #fff;
}
.nav.is-scrolled { box-shadow: 0 1px 0 rgba(13,16,22,.08); }

/* the panel supplies the bottom edge, so drop the bar's own hairline or you
   get a seam straight across the middle of one white block */
.nav.is-open { box-shadow: none; }

.nav.is-scrolled .nav-left a,
.nav.is-open .nav-left a { color: var(--ink); }

.nav.is-open .nav-logo img { filter: none; }


/* ============================================================
   SERVICES DROPDOWN — full-width white panel, square edges
   ============================================================ */

.mega {
  position: absolute;
  top: 100%; left: 0; right: 0;
  background: #fff;
  border-bottom: 1px solid var(--green);
  /* Pure opacity, both directions. There was a 6px slide here and it was the
     problem: 6px spread over the whole fade is 27px/sec, slow enough to read
     as the panel struggling into place. Nothing moves now, so there is
     nothing to look sluggish.
     visibility is held until the fade ends so the panel stays on screen for
     all of it, then leaves the tab order and screen readers in one step. */
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--menu) var(--ease),
              visibility 0s linear var(--menu);
}

.nav.is-open .mega {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--menu) var(--ease),
              visibility 0s;
}

/* one row across the whole bar, even columns.
   The gutter is pulled in by the item's own 16px so the first service title
   lands on the same left edge as "Case Studies" above it. The hover blocks
   overhang by that 16px on both ends, which is what gives them air. */
.mega-inner {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 2px;
  padding: 6px calc(var(--edge) - 16px) 26px;
}

.mega-item {
  display: block;
  padding: 18px 16px;
  color: var(--ink);
  transition: background .18s var(--ease), box-shadow .18s var(--ease);
}

/* Grey fill plus a hairline green edge. An inset shadow rather than a border
   or outline: a border would add 2px to the box and nudge every cell on
   hover, and an outline would paint into the 2px grid gap and touch the
   neighbour. This traces the fill exactly and costs no layout. */
.mega-item:hover {
  background: #F4F4F3;
  box-shadow: inset 0 0 0 1px var(--green);
}

/* Photo slot. Square corners to match the panel. */
.mega-media {
  display: block;
  position: relative;
  aspect-ratio: 3 / 2;
  margin-bottom: 13px;
  overflow: hidden;
  background: #EDEDEB;
}
.mega-media > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* This one is 16:9 in a 3:2 box, so cover trims the sides. Dead-centre
   cropping clips the left edge of the Meta loop, so the window is biased
   left to keep the mark whole. */
.mega-media > img[src*="svc-meta"] { object-position: 30% center; }

.mega-title {
  display: block;
  font-size: 15px;
  font-weight: 500;
  letter-spacing: -.01em;
  transition: color .18s var(--ease);
}
.mega-item:hover .mega-title { color: var(--green); }

.mega-desc {
  display: block;
  margin-top: 7px;
  font-size: 13px;
  line-height: 1.45;
  color: #6B7280;
}


/* ============================================================
   BUTTON — green pill with white arrow dot
   ============================================================ */

/* One spec for every Book a Demo on the page. The nav used to override this
   to a larger size, which left the hero button sitting at 12px next to a
   15.2px line of type. Sized off --nav-fs so they can never drift apart. */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  height: 34px;
  padding: 0 6px 0 18px;
  border-radius: 48px;
  background: var(--green);
  color: #fff;
  font-size: var(--nav-fs);
  font-weight: 400;
  white-space: nowrap;
  transition: transform .2s var(--ease), filter .2s var(--ease);
}
.btn:hover { filter: brightness(1.15); transform: translateY(-1px); }

.btn-dot {
  width: 22px; height: 22px;
  border-radius: 50%;
  background: #fff;
  color: var(--green);
  display: grid;
  place-items: center;
  flex: none;
}
.btn-dot svg { width: 12px; height: 12px; }


/* ============================================================
   HERO — full viewport, black where the video goes
   ============================================================ */

/* Normal document flow, like Legora. The hero scrolls up and off with the
   page and the white section stays attached right underneath it. Sticky was
   pinning the video so the white slid over the top of it, which read wrong.
   dvh (not svh) so the video fills the visible screen exactly on every
   device — svh leaves a gap when a phone's toolbars retract. */
.hero {
  position: relative;
  height: 100vh;
  height: 100dvh;
  background: #000;
  overflow: hidden;
}

/* Black underneath so there is never a white flash before the poster paints. */
.hero-media {
  position: absolute;
  inset: 0;
  background: #000;
  overflow: hidden;
}

/* object-fit: cover crops rather than letterboxes, so the frame always fills
   the hero at any aspect ratio. The poster is set as a background as well as
   the poster attribute: the attribute covers the pre-play moment, the
   background covers the case where a browser refuses to autoplay at all. */
.hero-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: #000 url("/assets/img/hero-poster.jpg?v=8") center / cover no-repeat;
  /* its own compositor layer, so repainting the video never touches the
     headline or the nav sitting above it */
  will-change: transform;
}

/* Legora's overlay, copied exactly: 50% black at the very top, down to 10% by
   13%, held flat through the middle, back to 50% at the bottom. Darkens the
   nav band and the headline band while leaving the footage itself alone. */
.hero-scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    rgba(0, 0, 0, .5) 0%,
    rgba(0, 0, 0, .1) 13.1176%,
    rgba(0, 0, 0, .1) 72.2498%,
    rgba(0, 0, 0, .5) 100%
  );
}

/* Motion-sensitive visitors get the still frame instead of the loop. */
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
  .hero-media {
    background: #000 url("/assets/img/hero-poster.jpg?v=8") center / cover no-repeat;
  }
}

.hero-copy {
  position: absolute;
  left: 0; right: 0;
  bottom: 8dvh;
  z-index: 3;                /* above the scrim, which is 2 */
  padding: 0 var(--edge);
  text-align: center;
  color: #fff;
}

/* Matched to Legora's hero, measured off their live site at five widths.
   Theirs holds weight 400, -0.040em and line-height 1.05 at every size and
   only the size moves, on a much shallower ramp than a raw vw gives you:
   60px at 1920, 57.2 at 1512, 54.2 at 1280, 51.4 at 900.
   The middle term is that line fitted, so 1512 lands on 57.4px. */
.hero-copy h1 {
  margin: 0;
  font-size: clamp(38px, 0.95vw + 43px, 60px);
  font-weight: 400;
  letter-spacing: -.04em;
  line-height: 1.05;
}

.hero-sub {
  margin-top: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  flex-wrap: wrap;
  /* 10% over the nav links. Expressed off --nav-fs rather than hard-coded so
     it keeps tracking if the nav ever resizes, but it no longer matches it. */
  font-size: calc(var(--nav-fs) * 1.1);
  font-weight: 400;
  opacity: .95;
}


/* ============================================================
   NEXT WHITE CARD
   ============================================================ */

/* Starts exactly at the fold — no negative pull, so none of it is on
   screen at rest. It rides up over the pinned hero as you scroll, meeting
   the black video on a hard straight edge. No corner radius. */
/* min-height dropped to auto: the Trusted by band now opens the white
   section, so forcing a full extra viewport here left a dead gap. */
/* Bottom padding doubled from 84px on Roy's call 2026-08-15: the headline
   needs clear air before the green card, and the info section below carries
   no top padding of its own — this edge IS the whole gap. */
.card {
  position: relative;
  z-index: 2;
  background: #fff;
  display: grid;
  place-items: center;
  padding: 40px var(--edge) 168px;
}

/* 900px forced the headline's longest line to wrap. It measures 1005px at the
   full 59px type, so the box has to clear that or the break comes back. */
.card-inner { max-width: min(1080px, 94vw); text-align: center; }

/* Line-height opened from 1.08 to 1.18 to give the highlight air — the three
   lines read as one thought, not a stack. */
.card h2 {
  margin: 0;
  font-size: clamp(33px, 4.18vw, 59px);
  font-weight: 300;
  letter-spacing: -.03em;
  line-height: 1.18;
}

/* The key phrase wears the brand green as a solid marker block with the type
   knocked out to white — a chip of the big green info card below, dropped
   into the headline. Painted as a background on the inline span, so it can't
   touch the line box, the pinned breaks, or the 1.18 line-height.
   The wipe is one-shot: JS arms the span (green text, no fill), then adds
   .is-in when the card scrolls into view and the block sweeps left to right.
   Without JS it simply renders already on. */
.card h2 .hl {
  padding: 0 .1em;
  color: #fff;
  font-weight: 500;
  background: no-repeat 0 0 linear-gradient(var(--green), var(--green));
  background-size: 100% 100%;
}
.card.is-armed h2 .hl {
  color: var(--green);
  background-size: 0% 100%;
}
.card.is-armed.is-in h2 .hl {
  color: #fff;
  background-size: 100% 100%;
  transition: background-size .65s cubic-bezier(.7, 0, .2, 1) .1s,
              color .18s ease .32s;
}
@media (prefers-reduced-motion: reduce) {
  .card.is-armed.is-in h2 .hl { transition: none; }
}

.card h2 .cadence {
  font-style: italic;
  font-weight: inherit;   /* rides the headline's 300 */
  color: var(--green);
}

/* Each line is its own block, so the three breaks are fixed rather than
   whatever the text happens to do at a given width.
   No margins anywhere: the blocks already inherit the headline's line-height,
   and anything extra spaces one line further apart than the others. */
.card h2 .h-line { display: block; }

/* second headline, sitting under the first while both are being tested */
.card h2.alt { margin-top: 1.1em; }

.card h2 .guarantee {
  display: block;
  font-style: italic;
}


/* ============================================================
   PAIN BAND — charcoal section between the headline and the card
   ============================================================ */

/* A softened dark gray rather than black, with a whisper of the brand green
   in it, so the green card below stays the page's darkest saturated surface.
   Kept thin on purpose: this is a beat between the promise and the pitch,
   not a destination. */
.pain {
  background: #212624;
  padding: 72px var(--edge) 80px;
}

.pain-inner {
  max-width: var(--band-w);
  margin: 0 auto;
}

/* Same banner as .info-head below, spec for spec — the body's Helvetica
   at 700, not Lemon Milk, on Roy's call 2026-08-15. Centred, caps, two
   pinned lines — the break lives in the markup. */
.pain-h2 {
  margin: 0 0 52px;
  font-size: clamp(34px, 4.9vw, 70px);
  font-weight: 700;
  letter-spacing: .005em;
  line-height: 1.1;
  text-transform: uppercase;
  text-align: center;
  color: #fff;
}

/* The payoff line takes the colour, same move as .info-head. The brand
   green is too dark to read on the charcoal, so it is lifted into a mint
   that carries on this surface. */
.pain-h2 .accent { color: #9FD8BE; }

/* The long first line has to fit a phone before the clamp floor lets it.
   A BARE vw HERE WAS THE BUG (fixed 2026-08-18): swapping the clamp for
   5.8vw also dropped its 34px floor, so at 320px these three banners --
   the biggest type in the design -- rendered at 18.6px, smaller than the
   33px body headline and the 46px stat beside them. Keep a floor. */
@media (max-width: 640px) {
  .pain-h2 { font-size: clamp(28px, 5.8vw, 34px); }
}

.pain-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

/* Hairline tiles the way Legora runs its compliance row: square corners,
   1px border, no fill. Tall on purpose — the reference cards are near-square
   and the height is what makes the row read as a section, not a footnote. */
.pain-card {
  border: 1px solid rgba(255,255,255,.16);
  padding: 52px 30px 58px;
  text-align: center;
}

/* Animated icons — 400px white line art on
   transparency, drawn with its own padding baked in, which is why these
   render larger than the 44px the old SVG strokes ran at. */
.pain-icon {
  display: block;
  width: 84px;
  height: 84px;
  margin: 0 auto 20px;
}

.pain-card h3 {
  margin: 0 0 16px;
  font-family: "Lemon Milk", "Helvetica Neue", Arial, sans-serif;
  font-size: clamp(17px, 1.45vw, 21px);
  font-weight: 700;
  letter-spacing: .03em;
  line-height: 1.4;
  text-transform: uppercase;
  color: #fff;
}

.pain-card p {
  margin: 0 auto;
  max-width: 36ch;
  font-size: 15px;
  line-height: 1.65;
  color: rgba(255,255,255,.62);
}

/* ---- Reveal on scroll: a fade-and-rise on entry (animate.css fadeInUp).
   Each element rises by its own height while it fades, once, the first time
   the band enters the viewport. main.js arms the hidden state and flips
   .is-in — with no JS running, nothing is ever hidden. ---- */

@keyframes pain-rise {
  from { opacity: 0; transform: translate3d(0, 100%, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.pain.is-armed .pain-h2,
.pain.is-armed .pain-card { opacity: 0; }

.pain.is-in .pain-h2,
.pain.is-in .pain-card {
  animation: pain-rise 1s var(--ease) forwards;
}
.pain.is-in .pain-card:nth-child(2) { animation-delay: .15s; }
.pain.is-in .pain-card:nth-child(3) { animation-delay: .3s; }

@media (prefers-reduced-motion: reduce) {
  .pain.is-armed .pain-h2,
  .pain.is-armed .pain-card { opacity: 1; animation: none; }
}

@media (max-width: 860px) {
  .pain { padding: 56px var(--edge) 60px; }
  .pain-grid { grid-template-columns: 1fr; }
  .pain-card { padding: 40px 24px 44px; }
}


/* ============================================================
   TRUSTED BY — client logo strip
   ============================================================ */

/* Opens the white section. Sits just under the fold, before any headline,
   which is where Legora puts theirs. */
/* No side padding, so the strip runs the full width. The centred text above
   carries its own gutter instead. */
.trusted {
  background: #fff;
  padding: 56px 0 76px;
  overflow: hidden;
}

.trusted-inner { display: block; }

/* ---- Google rating, sitting over the label ---- */

/* Reads as an eyebrow: source, then the visual, then the number as a quiet
   qualifier. The number is grey on purpose — five filled stars already say
   5.0, so setting it dark made the same fact compete with itself. */
.rating {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 0 var(--edge);
  margin-bottom: 20px;
}

.rating-g { width: 17px; height: 17px; flex: none; }

.rating-stars { display: flex; gap: 2px; }
.rating-stars svg {
  width: 15px;
  height: 15px;
  fill: #FBBC04;          /* Google's own review amber */
}

.rating-score {
  font-size: 14px;
  font-weight: 500;
  letter-spacing: -.01em;
  color: #6B7280;
}

/* The statement, not a caption. Up 10%, weight 300 -> 500, and darkened off
   #8A8F98 which was reading as disabled text rather than a subheader. */
.trusted-label {
  margin: 0 0 var(--strip-inset);
  padding: 0 var(--edge);
  text-align: center;
  font-size: clamp(19px, 1.65vw, 23px);
  font-weight: 500;
  letter-spacing: -.022em;
  color: #2E3238;
}

/* the one phrase that carries the brand colour */
.trusted-label .accent {
  font-style: italic;
  color: var(--green);
}


/* ---- infinite marquee ---- */

.marquee {
  flex: 1;
  min-width: 0;          /* lets the flex child actually clip instead of stretching the row */
  overflow: hidden;

  /* Feathers both ends so logos dissolve instead of getting guillotined at
     the edge. Percentages, so the two sides stay identical at any width. */
  -webkit-mask-image: linear-gradient(to right, transparent, #000 var(--fade), #000 calc(100% - var(--fade)), transparent);
          mask-image: linear-gradient(to right, transparent, #000 var(--fade), #000 calc(100% - var(--fade)), transparent);
}

.marquee-track {
  display: flex;
  width: max-content;
  animation: marquee-slide var(--strip-speed) linear infinite;
}

/* Shift by exactly one set, in pixels, measured by main.js. The 50% fallback
   only applies if JS never runs, where the track is the two sets in markup. */
@keyframes marquee-slide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(var(--marquee-shift, 50%) * -1), 0, 0); }
}

.marquee-set {
  display: flex;
  align-items: center;
  gap: var(--strip-gap);
  padding-right: var(--strip-gap);   /* keeps the seam spacing even */
}

@media (prefers-reduced-motion: reduce) {
  .marquee-track { animation: none; }
}

/* Logos are baked as flat black on transparent. Opacity is what turns them
   the same dark grey, so every client logo lands on one tone no matter what
   colours the original used. The text placeholders ride the same number. */
.marquee-set img {
  flex: none;
  height: var(--logo-h);
  width: auto;
  opacity: var(--logo-tone);
}

/* Placeholder client logos: icon plus a two-line wordmark, which is how most
   real roofing lockups are built. Drawn in currentColor on black so they ride
   the same --logo-tone as the real artwork and the row stays one flat grey. */
.brand {
  flex: none;
  display: flex;
  align-items: center;
  gap: 11px;
  color: #000;
  opacity: var(--logo-tone);
}

.brand-icon {
  width: calc(var(--logo-text) * 1.5);
  height: calc(var(--logo-text) * 1.5);
  flex: none;
  fill: currentColor;
}

.brand-type {
  display: flex;
  flex-direction: column;
  line-height: 1.06;
  white-space: nowrap;
}

.brand-name {
  font-size: var(--logo-text);
  font-weight: 700;
  letter-spacing: -.028em;
}

/* the descriptor line every contractor logo has under the name */
.brand-sub {
  font-size: calc(var(--logo-text) * .40);
  font-weight: 500;
  letter-spacing: .19em;
  text-transform: uppercase;
  margin-top: .28em;
}


/* ============================================================
   INFO CARDS
   ============================================================ */

.info {
  background: #fff;
  /* Top padding exists because the charcoal pain band now sits directly
     above: without it the green card touched the dark edge and the two
     dark surfaces read as one block. */
  padding: 110px var(--edge) 140px;

  /* ONE gap for the whole block, declared here so it cannot drift: it is the
     space between the two outline cards, the space above them, and the space
     between the green card and its video. Roy's ask (2026-08-16) was that all
     three read as the same break. Change it once and they all follow. */
  --pair-gap: 24px;
}

/* Roy's banner over the whole block: display-size bold caps, centred,
   bigger than the .card headline on purpose so it owns the section. The
   two lines are pinned, ink for the work and green for the payoff. */
.info-head {
  margin: 0 0 60px;
  font-size: clamp(34px, 4.9vw, 70px);
  font-weight: 700;
  letter-spacing: .005em;
  line-height: 1.1;
  text-transform: uppercase;
  text-align: center;
  color: var(--ink);
}

.info-head .h-line { display: block; }

/* italic caps look broken, so unlike the other accents this one only
   takes the colour */
.info-head .accent { color: var(--green); }

/* The long first line has to fit a phone before the clamp floor lets it.
   A BARE vw HERE WAS THE BUG (fixed 2026-08-18): swapping the clamp for
   5.8vw also dropped its 34px floor, so at 320px these three banners --
   the biggest type in the design -- rendered at 18.6px, smaller than the
   33px body headline and the 46px stat beside them. Keep a floor. */
@media (max-width: 640px) {
  .info-head { font-size: clamp(28px, 5.8vw, 34px); }
}

/* Wavy info-section lines — pulled 2026-08-17, Roy found them random.
   SVG parked in index.html's git history along with this block. */

/* Flat brand green, the way Legora fills theirs. Their page paints this exact
   value, rgb(0, 80, 50), so the card needs no colour of its own — it reuses
   --green and the site keeps a single green.
   aspect-ratio rather than a fixed height so the shape holds at every size. */
.info-card {
  position: relative;
  max-width: var(--band-w);       /* same width as the outline pair below */
  margin: 0 auto;
  /* 21/9 with the height taken up 10%. The photo is sized off this height,
     so it grows with the card automatically.
     A FLOOR, not a fixed ratio: aspect-ratio plus overflow:hidden clipped the
     bottom bar between 860px and 1000px, where the derived height was shorter
     than the four bars needed. min-height keeps the same proportion whenever
     the content fits and lets the card grow when it doesn't. */
  min-height: calc(var(--band-w) * 9.9 / 21);
  border-radius: 0;

  /* The card itself paints nothing now. The green lives on .info-copy so it
     can stop short of the video, and the gap between them is the same
     --pair-gap the outline cards below use (Roy, 2026-08-16). The video keeps
     its exact position and size: the media track is a percentage of the card
     width, which the gap does not change, so all 24px come off the copy. */
  background: none;

  /* Copy left, video right. The video column is "auto", so it takes only the
     width the media needs at full height — the copy gets the rest. */
  display: grid;
  column-gap: var(--pair-gap);
  /* The media column is stated, not measured. "auto" kept sizing itself from
     the asset's intrinsic width and came out too wide, which left a strip of
     green on the right. This is card-height x the media's own ratio,
     expressed against the card width: (9.9/21) is the height, (360/640) is
     the ad video now in the slot. */
  grid-template-columns: 1fr calc(100% * 9.9 / 21 * 360 / 640);
  padding: 0;
}

/* The text side keeps the breathing room the card used to provide, and it also
   carries the shading.
   The gradient lives HERE rather than on the card because the card's own
   bottom-right corner is underneath the photo — anchored there, the darkest
   part of the wash was hidden and only a trace reached the green. On this
   column, 100% 100% is the bottom-right of the green itself, and it keeps
   tracking correctly as the photo's width changes with the card height.
   A deeper GREEN does the darkening rather than black, so the hue stays rich
   instead of going muddy. */
.info-copy {
  padding: var(--card-pad);
  /* This block is the green card now, not the grid behind it. Colour and
     shading are set separately so the corner depth rides on top of the fill
     instead of replacing it. */
  background-color: var(--green);
  background-image: radial-gradient(115% 125% at 100% 100%,
                                    rgba(0, 34, 21, var(--card-shade)) 0%,
                                    rgba(0, 34, 21, 0) 72%);

  /* Copy stacks from the top. The space that opens up under it is deliberate,
     it is the room reserved for whatever goes in the bottom of the card. */
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
}


/* Sized off the card, not the viewport: this headline has to hold its
   proportion inside a box whose width is already clamped. */
.info-h2 {
  margin: 0;
  /* The measure runs the full copy column: the current headline is ~88
     characters, and any cap short of the column just multiplies the breaks.
     The type steps down from 58px so those characters land on three full
     lines instead of five stubs. */
  max-width: none;
  font-size: clamp(30px, 3.2vw, 50px);
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: 1.06;
  color: #fff;
}

/* the accent is green on white elsewhere; on the green card it goes light */
.info-h2 .accent {
  font-style: italic;
  color: rgba(255,255,255,.72);
}

.info-text {
  margin: 18px 0 0;
  max-width: 44ch;
  font-size: var(--nav-fs);
  line-height: 1.55;
  color: rgba(255,255,255,.72);
}

/* ---- Numbered steps on the green card. Replaced the white bars: those held
   one line each, and these carry a headline plus a supporting line.
   The number sits in its own narrow column so every headline and paragraph
   starts on one left edge, which is what makes the stack read as a list
   instead of three separate blocks. ---- */
.steps-list {
  margin: 34px 0 0;
  padding: 0;
  list-style: none;
  flex: 1;
}

.step-row {
  display: grid;
  grid-template-columns: clamp(84px, 8vw, 118px) 1fr;
  align-items: start;
  padding: 22px 0;
}

/* Hairline between rows only, so the list never opens or closes on a rule */
.step-row + .step-row { border-top: 1px solid rgba(255,255,255,.16); }

/* Big enough to be the thing you see first. White at half strength rather
   than the mint: at this size the mint read as a second headline and fought
   the copy for attention, where a dimmed white sits behind it.
   line-height under 1 pulls Lemon Milk's leading off the top so the digits
   line up with the headline's cap, not with its line box. */
/* Centred in its column and against the copy block beside it, so the number
   sits in the middle of the row instead of hanging off the top-left. */
.step-n {
  /* Same face and weight as MORE LEADS. BIGGER JOBS. — the page's body stack
     at 700, not Lemon Milk. No font-family of its own, so it inherits. */
  font-size: clamp(38px, 3.9vw, 55px);
  font-weight: 700;
  letter-spacing: .005em;
  line-height: 1;
  text-align: center;
  align-self: center;
  color: rgba(255,255,255,.5);
}

.step-copy h3 {
  margin: 0 0 8px;
  font-size: clamp(18px, 1.62vw, 23px);
  font-weight: 600;
  letter-spacing: -.015em;
  line-height: 1.22;
  color: #fff;
}

/* No measure cap: the copy column is already narrow enough to keep the line
   readable, and capping it here wrapped two-line paragraphs onto three and
   pushed the card taller than the photo beside it. */
.step-copy p {
  margin: 0;
  font-size: clamp(13.5px, 1.08vw, 15.5px);
  line-height: 1.55;
  color: rgba(255,255,255,.62);
}

@media (max-width: 860px) {
  .step-row { grid-template-columns: 66px 1fr; padding: 18px 0; }
  .step-n { font-size: 34px; }
}


/* the headline block sits together at the bottom */
.info-h2, .info-text { flex: none; }

/* Flush to the card's top, right and bottom — no green showing on those three
   sides. Only the left edge meets the copy.
   The column is given the PHOTO'S OWN ratio so its width is derived from the
   card height, not measured from the image's intrinsic 1024px. Letting the
   grid measure it was the bug: the track came out ~117px wider than the photo,
   which left a strip of green on the right and pushed the man inward. */
/* Outlined in the brand green, the same 1.5px hairline the .ocard pair and
   the secondary button use, so the video reads as a framed panel standing
   next to the green card rather than a piece cut out of it.
   border-box means the outline is drawn inside the frame's existing bounds,
   so adding it moves nothing. */
.info-media {
  margin: 0;
  border: 1.5px solid var(--green);
  border-radius: 0;
  overflow: hidden;
  height: 100%;
}

/* Ratio matches the frame exactly, so cover crops nothing — the full media
   shows and still reaches all three edges. */
.info-media img,
.info-media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (max-width: 860px) {
  /* stacking two columns into a 21:9 box leaves nothing readable, so the
     card gives up its fixed ratio on small screens */
  .info-card {
    grid-template-columns: 1fr;
    aspect-ratio: auto;
    min-height: 0;
  }
  .info-media { aspect-ratio: 4 / 3; }
}


/* ============================================================
   PROOF BAND — "But, why the Cornell Group?"
   ============================================================ */

/* Full-bleed dark green, the same #001A10 the /book page runs, sitting
   under the white info section.
   The bottom pad is deliberately much deeper than the top (Roy, 2026-08-16):
   this is the last band before the page ends, so the green has to carry air
   under the CTA instead of cutting off right beneath it. */
.proof {
  /* The air above AND below the feature row. One number drives both sides,
     which is what keeps that row centred between the subheader and the Book
     a Demo (Roy, 2026-08-16). Was 520px while the space was empty; the row
     itself pays for most of that height now. */
  --proof-gap: 112px;

  /* ONE radius for the picture and the outlined button, so the two corners
     can never drift apart (Roy asked for them to match, 2026-08-16). */
  --proof-radius: 14px;

  background: #001A10;
  padding: 96px var(--edge) 176px;
}

.proof-inner {
  max-width: var(--band-w);
  margin: 0 auto;
}

/* Same banner spec as .pain-h2 and .info-head: body Helvetica at 700,
   caps, centred, pinned break in the markup. */
.proof-h2 {
  margin: 0;
  /* the .pain-h2 / .info-head banner spec exactly — all three headers on the
     page are one size, on Roy's call 2026-08-15 */
  font-size: clamp(34px, 4.9vw, 70px);
  font-weight: 700;
  letter-spacing: .005em;
  line-height: 1.1;
  text-transform: uppercase;
  text-align: center;
  color: #fff;
}

/* The second line is a lockup, not just words: the mark and the name sit on
   one baseline in the same mint so they read as a single brand object. */
.proof-brand {
  display: inline-flex;
  align-items: center;
  gap: .26em;
}

/* mark.png is black artwork on transparency, so its alpha IS the shape.
   Masking a mint block through it tints the logo to the exact accent
   colour, where a filter chain could only approximate it.
   Width is derived from the art's own 177x136 so it never distorts. */
.proof-mark {
  flex: none;
  height: .82em;
  width: calc(.82em * 177 / 136);
  background: #9FD8BE;
  -webkit-mask: url("/assets/img/mark.png") center / contain no-repeat;
          mask: url("/assets/img/mark.png") center / contain no-repeat;
}

/* Lemon Milk on the brand name only. The rest of the header stays in the
   body Helvetica, so the name is the thing that changes texture and lands.
   Regular, not the header's 700: Lemon Milk Bold read heavier than the
   wordmark in the nav, and the name should match the logo (Roy, 2026-08-16).

   The tracking is MEASURED off the artwork, not guessed. wordmark.png's ink
   box is 997x78, a width-to-cap-height ratio of 12.782. Lemon Milk Regular
   set loose renders 13.321, so the logo is tracked tighter than the font's
   default. Solving for the value that reproduces 12.782 gives -0.0269em,
   which lands the ratio at 12.786. Re-derive it if the wordmark art is ever
   redrawn. */
.proof-h2 .accent {
  font-family: "Lemon Milk", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  letter-spacing: -.0269em;
  color: #9FD8BE;
}

/* The long first line has to fit a phone before the clamp floor lets it.
   A BARE vw HERE WAS THE BUG (fixed 2026-08-18): swapping the clamp for
   5.8vw also dropped its 34px floor, so at 320px these three banners --
   the biggest type in the design -- rendered at 18.6px, smaller than the
   33px body headline and the 46px stat beside them. Keep a floor. */
@media (max-width: 640px) {
  .proof-h2 { font-size: clamp(28px, 5.8vw, 34px); }
}

/* ---- the answer to the header (Roy, 2026-08-16) ----
   The reasons block. New .proof-* classes on purpose rather than reusing
   .step (that accordion is wired to /book's JS) or .ocard (white register,
   green borders that would disappear on this background). */

.proof-lede {
  max-width: 54ch;
  margin: 26px auto 0;
  text-align: center;
  font-size: clamp(16px, 1.2vw, 18.5px);
  line-height: 1.55;
  color: rgba(255, 255, 255, .72);
}

/* ============================================================
   COMPARISON CHART
   ============================================================

   The trick that makes this read the way it does is two separate
   backgrounds that do not line up:

   1. A light panel on the WRAPPER, pinned to the bottom and stopping
      short of the top, so the header row sits outside it on the
      section's own background.
   2. A dark column painted by ::before on the second cell of every
      row, which starts at the very top (above the panel) and, on the
      last row, runs past the panel's bottom edge.

   That offset is what makes the dark column look like a card standing
   in front of the table rather than a shaded column inside it. Every
   cell's text is a <span> at z-index 1 so it clears both layers. */

/* The bottom margin clears the dark column's overhang, which is drawn
   outside the wrapper and would otherwise sit on the row below. */
.compare {
  max-width: var(--band-w);
  margin: 54px auto 40px;
}

/* Horizontal scroll below the table's own min-width, so narrow screens
   pan the chart instead of crushing four columns together. */
.compare-table {
  position: relative;
  padding: 24px;
  overflow-x: auto;
}

/* Once the table fits, the scroll container has to go: overflow-x also
   clips vertically, which cut the foot off the dark column. */
@media (min-width: 992px) {
  .compare-table { overflow: visible; }
}

/* THE LIGHT PANEL LIVES ON THE ROWS, NOT ON THE WRAPPER (rebuilt 2026-08-18).
   It used to be a ::before on .compare-table, which broke twice on mobile:

   1. An absolutely-positioned box is sized against its ancestor's PADDING BOX,
      which excludes overflow. `width: 100%` resolved to the 250px visible
      window instead of the 700px table, so scrolling right dragged the rows
      off the panel and onto bare green.
   2. Its height hard-coded a `19px` header row. The header actually measures
      ~114px on a phone once the logo lockup and labels wrap, so the panel's
      top edge landed 47px INSIDE the header instead of below it.

   Painting the body rows directly fixes both by construction: a row is as
   wide as the table and as tall as itself, so it scrolls with the content and
   never needs to guess the header's height. */
.compare-table table {
  position: relative;
  width: 100%;
  min-width: 700px;
  /* separate + zero spacing, because border-radius does not render on cells
     under border-collapse: collapse, and the panel's rounded corners are the
     whole look. Row rules move to the cells below to compensate. */
  border-collapse: separate;
  border-spacing: 0;
  /* The .proof-lede spec above, taken down 10% on Roy's call (2026-08-17).
     Every number here is that clamp x 0.9, so the chart still tracks the lede
     as the window resizes instead of only matching at one width. Hook's port
     came in at a flat 14px, which read as fine print.
     One size for the whole chart: the row labels and column headers below
     inherit this and separate themselves with weight instead. */
  font-size: clamp(14.4px, 1.08vw, 16.65px);
  line-height: 1.45;
  color: #464747;
}

/* The panel itself: every row except the header. */
.compare-table tr:not(.compare-head) > td { background: #f3f3f3; }

/* Rounded corners on the four outer cells, so the body rows read as one card.
   The top pair is selected with the adjacent-sibling combinator, NOT
   :first-of-type — the first `tr` of its type IS the header row, so
   `:not(.compare-head):first-of-type` matches nothing at all. */
.compare-table .compare-head + tr > td:first-child { border-top-left-radius: 16px; }
.compare-table .compare-head + tr > td:last-child  { border-top-right-radius: 16px; }
.compare-table tr:last-child > td:first-child { border-bottom-left-radius: 16px; }
.compare-table tr:last-child > td:last-child  { border-bottom-right-radius: 16px; }

/* Row rules live on the cells now — tr borders do not paint under
   border-collapse: separate. */
.compare-table td { border-bottom: 1px solid rgba(130,130,130,.2); }
.compare-table tr:last-child > td { border-bottom: none; }

/* ---- header row ---- */

/* White, unlike Hook's dark: these three labels sit on the green band
   rather than on their white page. */
.compare-table th {
  position: relative;
  padding: 24px 16px 48px;
  text-align: center;
  /* no size of its own: same as every cell, set bold to lead */
  font-weight: 700;
  color: #fff;
}
.compare-table th span { position: relative; z-index: 1; }

/* The nav lockup, reused in the hero column's header. Same artwork and the
   same mark-to-wordmark ratio; only the cap height is its own, set a hair
   above the nav's so it holds its own against the 15px labels beside it.
   The art is black ink, so it inverts to white on the green exactly the way
   it does over the hero video. */
.compare-logo {
  display: inline-flex;
  align-items: center;
  gap: var(--logo-gap);
}
.compare-logo img {
  width: auto;
  filter: invert(1);
}
.compare-logo .logo-word { height: 15px; }
.compare-logo .logo-mark { height: calc(15px * var(--mark-scale)); }

/* Top cap of the hero column.
   Hook paints theirs near-black, which died on this band: #001A10 is already
   a near-black green, so a neutral black column read as a hole punched in the
   section rather than a card sitting on it. Brand green keeps the whole thing
   monochromatic and lets VALUE do the separating — the column is lighter than
   the band behind it and far darker than the panel it crosses. White on it
   measures 9.6:1, so the copy is stronger than it was on the black. */
.compare-table th:nth-child(2)::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--green);
  border-radius: 16px 16px 0 0;
}

/* ---- body cells ---- */

.compare-table td {
  position: relative;
  padding: 16px 32px;
  text-align: center;
  vertical-align: middle;
}
.compare-table td span { position: relative; z-index: 1; }

/* the row label column */
.compare-table td:first-child {
  text-align: left;
  color: var(--ink);
}
.compare-table td:first-child span {
  display: block;
  width: max-content;
}

/* the hero column. The extra 5px closes the 1px row rule plus rounding,
   so the column paints as one unbroken block instead of striping. */
.compare-table td:nth-child(2) { color: #fff; }
.compare-table td:nth-child(2)::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: calc(100% + 5px);
  background: var(--green);
}

/* The row rules die at the green column, because the fill above is painted
   over them (that is what the +5px is for). Roy asked for them to carry
   straight across (2026-08-17), so the column draws its own on top of its
   fill. White at low alpha, because the grey the light panel uses has nothing
   to separate itself from on this green. Sits at the cell's bottom edge, the
   same line the tr border occupies, so the two read as one rule. */
.compare-table td:nth-child(2)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 1px;
  background: rgba(255, 255, 255, .18);
}

/* the last row has no rule under it on the light side either */
.compare-table tr:last-child td:nth-child(2)::after { display: none; }

/* the foot of the dark column, hanging below the light panel */
.compare-table tr:last-child td:nth-child(2)::before {
  height: calc(100% + 24px + 36px);
  border-radius: 0 0 16px 16px;
}

/* ---- COMIC-BOOK OFFSET (Roy, 2026-08-17) ----
   A hard shadow, zero blur, thrown down and to the right, so the chart reads
   as something sitting ON the band instead of printed into it. The comic
   lettering trick, applied to the two blocks the chart is built from.

   TWO colours, because no single one works against both surfaces:
   - the light panel throws brand green, which is lighter than the near-black
     band and so actually reads on it. Black here was invisible, the band is
     already near-black.
   - the green column throws the band's own colour, which shows where it lands
     on the light panel and vanishes where it lands on the band, which is
     exactly where a shadow would be noise rather than depth.

   The column is painted per row, so each row casts its own. They stack into
   one unbroken strip down the right edge, and each row's downward shadow is
   covered by the row beneath it, which is what keeps the column from looking
   striped.

   Both offsets are set once here, so the whole effect retunes from one place.
   Swap the sign on --comic-x to throw it left instead. */
.compare {
  --comic-x: 12px;
  --comic-y: 12px;
}

/* THE PANEL CASTS PER ROW, exactly like the column does (restored 2026-08-18).
   This used to hang on .compare-table::before, which was the old single-element
   panel. That element was removed when the panel moved onto the rows — it was
   sized against the visible window instead of the 700px table, so the panel
   detached from the rows as soon as you scrolled sideways on a phone. The
   shadow rule was left pointing at the now-empty pseudo-element, so the light
   panel silently lost its offset and the chart went flat against the band.

   Same stacking trick the column uses: every body cell throws down and right,
   each row's downward throw is covered by the row beneath it and each cell's
   rightward throw by the cell beside it, so only the outer edges show and the
   result is one unbroken offset rather than a grid of them.
   The green column is excluded because it throws its own colour below. */
.compare-table tr:not(.compare-head) > td:not(:nth-child(2)) {
  box-shadow: var(--comic-x) var(--comic-y) 0 0 var(--green);
}

.compare-table th:nth-child(2)::before,
.compare-table td:nth-child(2)::before {
  box-shadow: var(--comic-x) var(--comic-y) 0 0 #001A10;
}

/* THE COLUMN HAS TO PAINT ABOVE THE CELLS BESIDE IT, or its shadow does not
   exist. Cell backgrounds paint in DOM order, so column 3's light background
   was drawing straight over the shadow column 2 casts to the right — the
   shadow was in the computed styles the whole time and simply buried.
   It used to work because the panel was ONE element behind the whole table;
   now that the panel is per-cell, the column needs to be lifted explicitly. */
.compare-table th:nth-child(2),
.compare-table td:nth-child(2) {
  z-index: 2;
}

@media (max-width: 900px) {
  .compare { margin-top: 40px; }
  .compare-table { padding: 24px 0; }
  .compare-table td { padding: 16px; }
}


/* ---- FEATURE ROW: picture left, serif header right (Roy, 2026-08-16) ----
   Equal margin top and bottom is what centres the row between the subheader
   and the Book a Demo. Both sides read the same number on purpose, so the
   row cannot drift off centre when the gap is retuned. */
.proof-feature {
  margin: var(--proof-gap) 0;
  display: grid;
  /* Not 50/50. The harvey.ai row Roy referenced runs a smaller picture
     against a wider column of words, with a lot of air between them. */
  grid-template-columns: minmax(0, .82fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(44px, 7vw, 112px);
  /* Positioning context for the rail. The rail is OUT OF the grid flow on
     purpose (Roy, 2026-08-18): as a third column it ate ~140px of track and
     narrowed every headline's measure; parked absolutely it changes
     nothing about the two original columns. */
  position: relative;
}

/* Picture on the right instead. Nothing else about the row changes. */
.proof-feature.is-flipped .proof-shot { order: 2; }

/* A SQUARE with ROUNDED corners, straight off the harvey.ai reference Roy
   sent (2026-08-16). This was built sharp-cornered and 4/3 the first time,
   which was wrong on both counts.
   The radius is ~2.5% of the picture's width, which is what keeps the
   softening proportional instead of turning into a pill on a big screen. */

/* ---- the conveyor deck (Roy, 2026-08-16) ----
   FIVE frames on one ring; three visible at a time. The stack owns real
   layout height (1.32x its width) so the faded frames above and below the
   active shot never reach the lede or the Book a Demo — they live and die
   inside this box. Width 90% of the column: the old full-width square,
   shrunk 10%.
   Every frame is the SAME rounded square. The back cards fade two ways at
   once: scale + opacity push them "into the distance", and a mask on the
   far edge dissolves them into the green.
   The choreography is the whole trick. Each slot transition is tuned so no
   two half-visible images ever share space:
   - EXITING dissolves in .3s and is gone before anything arrives above.
   - ACTIVE -> prev keeps FULL opacity while it lifts, and only dims once
     the incoming card (already opaque on top of it) has covered it.
   - NEXT -> active brightens to opaque in .45s, covering the card behind
     it before that card ever starts to dim.
   The ease is cubic-bezier(.16,1,.3,1) — fast out of the gate, long soft
   landing. Motion is transform + opacity only: all compositor, no layout. */
.proof-stack {
  position: relative;
  width: 90%;
  margin: 0 auto;
  aspect-ratio: 1 / 1.32;
}

.proof-shot {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  margin: 0;
  aspect-ratio: 1 / 1;
  border-radius: var(--proof-radius);   /* the pictures only; the button is square */
  overflow: hidden;
  will-change: transform, opacity;
}

/* video as well as img: the "1000s of ads" frame runs a silent loop, and it
   has to fill and crop exactly the way the stills do or that one slide would
   letterbox inside the square. */
.proof-shot img,
.proof-shot video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Center stage: full size, full ink, on top of both faded frames.
   Entering it: rise on the expo ease, brighten fast so it covers the
   card behind before that card dims. */
.proof-shot.is-active {
  transform: translateY(-50%) scale(1);
  opacity: 1;
  z-index: 3;
  transition: transform 1.2s cubic-bezier(.16, 1, .3, 1),
              opacity .5s ease .05s;
}

/* Just left: a short peek over the front square's top edge, far edge
   dissolving into the page. Entering it (from active): the lift is slow
   and silky, the dim waits .5s — until the new front card has fully
   covered this one — so the centre never double-exposes. */
.proof-shot.is-prev {
  transform: translateY(-79%) scale(.86);
  opacity: .34;
  z-index: 2;
  -webkit-mask-image: linear-gradient(to top, #000 52%, transparent 98%);
          mask-image: linear-gradient(to top, #000 52%, transparent 98%);
  transition: transform 1.2s cubic-bezier(.16, 1, .3, 1),
              opacity .6s ease .5s;
}

/* Coming up: the mirror image from below. Entering it (from staging):
   fade leads, rise follows. */
.proof-shot.is-next {
  transform: translateY(-21%) scale(.86);
  opacity: .34;
  z-index: 1;
  -webkit-mask-image: linear-gradient(to bottom, #000 52%, transparent 98%);
          mask-image: linear-gradient(to bottom, #000 52%, transparent 98%);
  transition: transform 1.2s cubic-bezier(.16, 1, .3, 1),
              opacity .7s ease .25s;
}

/* Ring parking. Exiting dissolves FIRST (.35s) and leaves the top slot
   empty before the front card arrives. Staging's long travel back down
   happens at opacity 0, so it is never seen. */
.proof-shot.is-exiting {
  transform: translateY(-100%) scale(.8);
  opacity: 0;
  z-index: 0;
  transition: transform .65s cubic-bezier(.5, 0, .75, .4),
              opacity .35s ease-in;
}
.proof-shot.is-staging {
  transform: translateY(0%) scale(.8);
  opacity: 0;
  z-index: 0;
  transition: transform 1.3s cubic-bezier(.16, 1, .3, 1),
              opacity .45s ease;
}

/* ---- the rail (Roy, 2026-08-18) ----
   A thin white line down the right edge of the row. It does two jobs at
   once: it SHOWS which slide is live (the thumb rides down it as the deck
   rotates) and it IS the control — hover thickens the line, scrubbing or
   scroll-wheeling over it walks the deck to that slide.
   Invisible until JS adds .is-live: with no script, or under reduced
   motion (where the rotator never starts), a control that does nothing
   never shows. */
.proof-rail {
  /* Absolute, not a grid track: it sits in the gutter RIGHT of the content
     column (between --band-w and the viewport edge), so the headline
     column keeps the exact measure it had before the rail existed. The
     negative right offset pushes it that clamp into the gutter; the gutter
     is ~(11vw - 24px) above the phone breakpoint, and this offset is
     always well inside it. Centred against the whole row, same as before. */
  position: absolute;
  top: 50%;
  right: calc(-1 * clamp(28px, 4.5vw, 64px));
  transform: translateY(-50%);
  /* The width is the HIT AREA; the visible line is the 1px hairline
     centred inside it, so the thing you aim at is twenty-six times wider
     than the thing you see. */
  width: 26px;
  height: clamp(240px, 30vw, 380px);
  padding: 0;
  cursor: pointer;
  opacity: 0;
  transition: opacity .6s ease;
}
.proof-rail.is-live { opacity: 1; }

.proof-rail-line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  /* A true hairline (Roy, 2026-08-18): 1px, with the alpha nudged up from
     .26 so the thinner strip keeps its ink against the green. */
  width: 1px;
  transform: translateX(-50%);
  border-radius: 1px;
  background: rgba(255, 255, 255, .3);
  transition: width .35s cubic-bezier(.16, 1, .3, 1),
              background .35s ease;
}

/* One dot per slide, laid out by the rotator at each stop (--p runs 0 at
   the top to 1 at the bottom). The live stop hides under the thumb, so the
   only dots showing are the ones you can travel TO. */
.proof-rail-dot {
  position: absolute;
  left: 50%;
  top: calc(var(--p) * 100%);
  width: 4px;
  height: 4px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(255, 255, 255, .4);
}

/* The thumb: a short white pill marking the live slide. JS writes --p onto
   the rail and this glides between stops on the same expo ease the deck
   uses. Animating `top` off the variable is the whole trick, and the
   travel subtracts the pill's own height so it centres on both end stops
   instead of overhanging them. */
.proof-rail-thumb {
  position: absolute;
  left: 50%;
  top: calc(var(--p, 0) * (100% - 38px));
  width: 3px;
  height: 38px;
  transform: translateX(-50%);
  border-radius: 3px;
  background: #fff;
  transition: top .9s cubic-bezier(.16, 1, .3, 1),
              width .35s cubic-bezier(.16, 1, .3, 1),
              box-shadow .35s ease;
}

/* Hover (Roy: "gets a little bit thicker"): the hairline swells 1 -> 2 and
   brightens, the thumb 3 -> 5 with a soft glow. Keyboard focus gets the
   same state so it never reads as mouse-only. */
.proof-rail:hover .proof-rail-line,
.proof-rail:focus-visible .proof-rail-line {
  width: 2px;
  background: rgba(255, 255, 255, .45);
}
.proof-rail:hover .proof-rail-thumb,
.proof-rail:focus-visible .proof-rail-thumb {
  width: 5px;
  box-shadow: 0 0 12px rgba(255, 255, 255, .4);
}

.proof-rail:focus-visible {
  outline: 2px solid #9FD8BE;
  outline-offset: 4px;
  border-radius: 4px;
}

/* A phone runs the same deck a touch shallower so the band doesn't turn
   into a long scroll past nothing. */
@media (max-width: 860px) {
  .proof-stack { aspect-ratio: 1 / 1.22; }
}

/* THE HERO'S FACE, on every slide (Roy, 2026-08-17). This deck used to run
   the display serif, with only the "prove-it" slide in the sans; Roy picked
   the sans and it now applies to all five. No font-family of its own: it
   inherits the body stack at weight 400 with -.04em tracking, exactly like
   "Word of mouth isn't a business" at the top of the page, so the two stay
   matched if that stack ever changes.
   The serif is no longer used anywhere; --serif / --serif-wght and their
   @font-face are still defined upstream if it is ever wanted back. */
.proof-title {
  margin: 0 0 28px;
  font-weight: 400;
  /* TWO LINES MAXIMUM. NEVER THREE. Roy's rule, 2026-08-16.
     Sized to fill the column, then capped at the largest value that still
     holds EVERY headline in the deck to two lines.

     RE-SWEPT 2026-08-17. A third track was added to .proof-feature, which
     narrowed the copy column by roughly 80px, and three of the five
     headlines silently went to three lines at every desktop width. The
     ceilings below are measured against the CURRENT column:
        900px viewport (302px column) -> 32px   <- the binding width now
       1024px viewport (346px column) -> 37px
       1280px viewport (436px column) -> 47px
       1440px viewport (492px column) -> 53px
      >=1600px         (516px column) -> 55px   <- the column stops growing,
     so the cap does too. 3.5vw sits under every one of them; the tightest
     is 900px, which is what sets the slope.

     text-wrap: balance was suspected and CLEARED — balance, pretty, and auto
     all produce identical ceilings.

     THE BINDING CONSTRAINT IS THE LONGEST HEADLINE, not the design. If a
     headline changes length OR THE COLUMN CHANGES WIDTH, re-run the sweep:
     either one will silently spill this to three lines. */
  font-size: clamp(24px, 3.5vw, 55px);
  line-height: 1.02;
  letter-spacing: -.04em;
  color: #fff;
  /* Evens the lines out. Without it a short last word ("do.") gets orphaned
     on its own line, which at this size is impossible not to see. */
  text-wrap: balance;
}

/* .is-sans was the modifier that put ONE slide in this face. The face is the
   default now, so the class is a no-op and is kept only so any leftover
   markup carrying it still renders correctly. */

/* Back to the body face underneath, so the serif stays an accent instead of
   turning the block into a book page. */
/* No max-width on purpose. The 46ch cap was holding this to about two thirds
   of the column and leaving the gutter Roy called out; the COLUMN is the
   measure now. Lifted off .72 opacity as well, which read as a caption
   instead of as something meant to be read. */
.proof-sub {
  margin: 0;
  font-size: clamp(17px, 1.5vw, 21px);
  line-height: 1.5;
  color: rgba(255, 255, 255, .84);
}

/* ---- the copy conveyor (2026-08-16) ----
   The words ride the same vertical conveyor as the deck: the leaving copy
   exits UP fast and clears out first, the entering copy rises from BELOW a
   beat later on the same expo ease the images use. All copies share one
   grid cell, so the stack is always as tall as its longest copy and the
   proof-btn under it never jumps. Parked copies are visibility:hidden with
   no transition at all, so the silent wrap from "above" back to "below"
   is instant and invisible. */
.proof-copies { display: grid; }

.proof-copy {
  grid-area: 1 / 1;
  align-self: center;
  opacity: 0;
  transform: translateY(26px);   /* parked below, waiting to rise */
  visibility: hidden;
  pointer-events: none;
}

/* leaving: quick fade and a short rise — gone before the next line arrives */
.proof-copy.is-above {
  transform: translateY(-26px);
  visibility: visible;
  transition: opacity .35s ease-in,
              transform .55s cubic-bezier(.5, 0, .75, .4),
              visibility 0s .38s;
}

/* arriving: the expo-out rise from below, delayed a beat so the exit reads
   first — the same choreography as the deck, only slower and flowier
   (Roy, 2026-08-16: the fast version read as twitchy) */
.proof-copy.is-on {
  opacity: 1;
  transform: translateY(0);
  visibility: visible;
  pointer-events: auto;
  transition: opacity .5s ease .18s,
              transform .9s cubic-bezier(.16, 1, .3, 1) .18s,
              visibility 0s .18s;
}

/* ---- outlined CTA under the subheader (Roy, 2026-08-16) ----
   A white LINE, not a white fill. The solid green pill at the bottom of the
   band is still the page's primary action, so this one has to read as the
   quieter of the two while staying obvious on the dark green.
   inline-flex, so the box is only as wide as its words instead of stretching
   the full column. Square corners on Roy's call (2026-08-16): the pictures
   keep --proof-radius, this button does not follow them. */
.proof-btn {
  margin-top: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Proportions off the harvey.ai "Explore Platform" reference: the type is
     BIG inside the box and the padding is snug around it. The first build had
     16.5px type floating in 32px of padding, which read timid.
     Tightened a further 10% on Roy's call (2026-08-16): the TYPE SIZE IS
     FIXED, so the box shrinks purely by pulling the outline in toward the
     words. 62->56 tall, 30->20 side. Do not "fix" this by growing the padding
     back; the snugness is the point. */
  min-height: 56px;
  padding: 0 20px;
  /* Full-strength white. At .8 alpha the line went soft against the dark
     green and lost the crispness the reference has. */
  border: 1px solid #fff;
  border-radius: 0;
  background: transparent;
  font-size: clamp(17px, 1.35vw, 20px);
  font-weight: 400;
  letter-spacing: 0;
  color: #fff;
  transition: background .22s var(--ease),
              color .22s var(--ease),
              border-color .22s var(--ease);
}

/* Fills on hover and inverts to the band's own green, so the state change
   reads instantly without introducing a colour the page does not already
   run. */
.proof-btn:hover {
  background: #fff;
  border-color: #fff;
  color: #001A10;
}

/* Keyboard users get the same affordance the mouse gets, plus a ring that
   survives against both the outlined and the filled state. */
.proof-btn:focus-visible {
  outline: 2px solid #9FD8BE;
  outline-offset: 3px;
}

/* Stacked on a phone: picture first, then the words. */
@media (max-width: 860px) {
  .proof-feature {
    grid-template-columns: 1fr;
    gap: 28px;
  }
  .proof-feature.is-flipped .proof-shot { order: 0; }

  /* No rail down here: it is a hover-and-scrub instrument, and a phone has
     neither the pointer for it nor the horizontal room beside a full-width
     deck. The auto-rotation carries the slides on its own. */
  .proof-rail { display: none; }

  /* Single column down here, so the measure is much wider relative to the
     viewport and the headline can afford more than the desktop curve gives
     it. This is the SANS phone curve, now that every slide runs the sans:
     ceilings are 27px at 320, 30px at 360, 32px at 390, 34px at 414, same
     two-line rule. */
  .proof-title { font-size: clamp(24px, 8vw, 38px); }
}

/* The button is the brand green on a darker green, so it needs the mint edge
   to separate from the band. Hover reuses the .btn transform. */
/* ---- RESULTS TABLE: the editorial stat block that closes the band ----
   Heading hard right, then rows of label-left / number-right split by
   full-width hairlines. The rules run the whole band on purpose: they are
   what turns three facts into a table instead of three stray lines. */
/* A tenth more air than the band's standard gap, so the table reads as its
   own closing beat rather than the next item in the feature stack
   (Roy, 2026-08-16). Still derived from --proof-gap, so it retunes with the
   rest of the band. */
.results { margin-top: calc(var(--proof-gap) * 1.1); }

/* Set right, with the break pinned in the markup rather than squeezed out
   of a max-width. A ch-based measure re-wrapped to three lines as the type
   scaled; the <br> holds it at two at every desktop size. */
.results-h {
  margin: 0 0 clamp(48px, 6vw, 104px) auto;
  text-align: right;
  font-size: clamp(23px, 2.5vw, 37px);
  font-weight: 400;
  letter-spacing: -.02em;
  line-height: 1.2;
  color: #fff;
}

.results-list { margin: 0; }

/* The number column is "auto" so it only takes the width the figure needs,
   which keeps every value flush to the same right edge no matter its
   length. The label takes whatever is left. */
.results-row {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 24px;
  padding: clamp(18px, 2.2vw, 32px) 0;
  border-top: 1px solid rgba(255, 255, 255, .16);
}

/* the last rule closes the table off rather than leaving it hanging open */
.results-row:last-child { border-bottom: 1px solid rgba(255, 255, 255, .16); }

/* Up 20% on Roy's call 2026-08-16 (was 13.5 / 1.05vw / 16.5), then another
   25% on 2026-08-17. The label is the half of the row that says what the
   number means, and next to a 94px figure it kept reading like fine print.
   Brightened at the same time: at this size .72 white was doing the muting
   the small size used to do, and the line stopped popping. */
.results-label {
  font-size: clamp(20.25px, 1.575vw, 24.75px);
  line-height: 1.35;
  color: rgba(255, 255, 255, .86);
}

/* Line-height under 1 on purpose: at this size the figure's own box carries
   more air than the row needs, and the rules drift apart without it. */
.results-value {
  margin: 0;
  /* OFF the band's serif on Roy's call 2026-08-16. No font-family here on
     purpose: the figures inherit the body Helvetica, which is the same face
     and the same 300 weight the white section's big headline runs, so the
     numbers now read as this site's display voice instead of borrowing the
     feature headers'. The serif is still --serif for those headers.
     A light weight at this size needs the track pulled in, or the figures
     drift apart and stop reading as one number. */
  font-size: clamp(46px, 6.2vw, 94px);
  font-weight: 300;
  letter-spacing: -.035em;
  line-height: .92;
  text-align: right;
  color: #fff;
}

/* Two columns survive down to a phone because the label wraps and the
   figure shrinks with the viewport. Below this the figure would win the
   whole row, so the label goes above it instead. */
@media (max-width: 560px) {
  .results-row {
    grid-template-columns: 1fr;
    gap: 10px;
  }
  .results-label { order: -1; }
  .results-value { text-align: left; }

  /* the pinned two-line break is desktop-only; at this width each of those
     lines is wider than the band, so the copy wraps on its own instead */
  .results-h br { display: none; }
}

.proof-cta {
  display: flex;
  justify-content: center;
}

.proof-cta .btn {
  height: 44px;
  padding: 0 8px 0 24px;
  box-shadow: inset 0 0 0 1px rgba(159, 216, 190, .45);
}
.proof-cta .btn-dot { width: 28px; height: 28px; }

/* A phone cannot afford the desktop air, or the band turns into a long
   scroll past nothing. Still equal on both sides, so still centred. */
@media (max-width: 860px) {
  .proof { --proof-gap: 72px; }
}

/* Under the header: open room on the left (content lands there next),
   the ad preview on the right. */
/* the phone, centred on the page under the header */
.proof-stage {
  margin-top: 56px;
  display: flex;
  justify-content: center;
}

/* ---- iPhone frame ----
   Fixed pixel canvas: the screen is 362x784 (the 19.5:9 iPhone glass), so
   the Facebook chrome inside can run real UI sizes instead of scaling. */
.phone {
  width: 384px;
  padding: 11px;
  background: #0B0D0F;
  border-radius: 54px;
  box-shadow: 0 30px 80px rgba(0, 0, 0, .45),
              0 0 0 1.5px rgba(255, 255, 255, .08);
}

.phone-screen {
  position: relative;
  width: 362px;
  height: 784px;
  border-radius: 43px;
  overflow: hidden;
  background: #F0F2F5;   /* Facebook feed grey behind the card */
  color: #050505;
  /* the platform stack, so the mock renders in the same face FB does */
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  text-align: left;
}

/* ---- iOS status bar ---- */
.ph-status {
  position: relative;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 26px 0;
  background: #fff;
}

.ph-time { font-size: 15px; font-weight: 600; letter-spacing: .02em; }

.ph-island {
  position: absolute;
  left: 50%;
  top: 11px;
  transform: translateX(-50%);
  width: 112px;
  height: 30px;
  border-radius: 999px;
  background: #0B0D0F;
}

.ph-icons { display: flex; align-items: center; gap: 6px; }
.ph-icons svg { height: 11px; width: auto; fill: currentColor; }

/* ---- Facebook app bar ---- */
.fb-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 14px 8px;
  background: #fff;
  border-bottom: 1px solid #E4E6EB;
}

.fb-word {
  font-size: 25px;
  font-weight: 700;
  letter-spacing: -.02em;
  color: #0866FF;
}

.fb-bar-icons { display: flex; gap: 8px; }

.fb-chip {
  width: 33px;
  height: 33px;
  border-radius: 50%;
  background: #E4E6EB;
  display: grid;
  place-items: center;
}
.fb-chip svg { width: 17px; height: 17px; fill: currentColor; }

/* ---- the ad card ---- */
.fb-card { margin-top: 8px; background: #fff; }

.fb-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 9px 12px 7px;
}

.fb-avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--green);
  display: grid;
  place-items: center;
  flex: none;
}
.fb-avatar svg { width: 20px; height: 20px; fill: #fff; }

.fb-id { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.fb-id strong { font-size: 14.5px; font-weight: 600; }

.fb-sub {
  font-size: 12.5px;
  color: #65676B;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.fb-sub svg { width: 12px; height: 12px; }

.fb-head-btns {
  margin-left: auto;
  display: flex;
  gap: 14px;
  color: #65676B;
}
.fb-head-btns svg { width: 19px; height: 19px; fill: currentColor; }

.fb-text {
  margin: 0;
  padding: 0 12px 9px;
  font-size: 14.5px;
  line-height: 1.35;
}
.fb-more { color: #65676B; }

/* ---- VIDEO SLOT — the ad drops in here, 4:5 like the real placement ---- */
.fb-media {
  aspect-ratio: 4 / 5;
  background: #14181D;
  display: grid;
  place-items: center;
}

.fb-play {
  width: 62px;
  height: 62px;
  border-radius: 50%;
  background: rgba(255, 255, 255, .92);
  display: grid;
  place-items: center;
}
.fb-play svg { width: 30px; height: 30px; fill: #14181D; margin-left: 4px; }

/* ---- link + CTA bar ---- */
.fb-cta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 9px 12px;
  background: #F0F2F5;
}

.fb-cta-copy { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.fb-domain { font-size: 11.5px; color: #65676B; letter-spacing: .01em; }
.fb-cta-copy strong { font-size: 14px; font-weight: 600; }

.fb-btn {
  flex: none;
  padding: 8px 14px;
  border-radius: 6px;
  background: #E4E6EB;
  font-size: 14px;
  font-weight: 600;
}

/* ---- reactions + Like/Comment/Share ---- */
.fb-social { padding: 0 12px 8px; }

.fb-counts {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 0;
  border-bottom: 1px solid #E4E6EB;
  font-size: 13px;
  color: #65676B;
}

.fb-reacts { display: flex; }

.fb-react {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1.5px solid #fff;
}
.fb-react svg { width: 10px; height: 10px; fill: #fff; }
.fb-react-like { background: #0866FF; }
.fb-react-love { background: #F33E58; margin-left: -4px; }

.fb-counts-right { margin-left: auto; }

.fb-actions { display: flex; padding-top: 3px; }

.fb-action {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: 7px 0;
  font-size: 13px;
  font-weight: 600;
  color: #65676B;
}
.fb-action svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
  stroke-linejoin: round;
  stroke-linecap: round;
}


/* ============================================================
   WHITE SECTION — the empty shell under the green band
   ============================================================ */

/* Same white and the same vertical rhythm as .info, so this block sits in
   the page like the other white ones rather than looking bolted on. The
   min-height is only here to give the empty shell something to see; drop it
   once there is real content, and the padding will carry the spacing. */
.white {
  background: #fff;
  padding: 110px var(--edge) 140px;
  min-height: 46vh;
}

.white-inner {
  max-width: var(--band-w);
  margin: 0 auto;
}


/* ---- PRICING SPLIT: headline left, copy and CTA right ----
   The right column is a stated width rather than a fraction, so the empty
   middle grows with the window instead of the copy stretching to fill it.
   That gap IS the layout: the two blocks are meant to sit apart.

   WHERE THE VISIBLE GAP ACTUALLY COMES FROM (Roy, 2026-08-17): almost none
   of it is the `gap` below. The left track is 1fr, so its BOX runs on well
   past where the headline text stops, and that slack is most of the white
   the eye reads as the gap. At 1440px it measured 327px of slack against
   65px of gap. Shrinking `gap` alone therefore does close to nothing.
   The lever that moves the copy left is the RIGHT track's width: the band
   is a fixed width, so every pixel added there is a pixel taken off the
   1fr slack, and the whole right column — copy and button together —
   slides left by that much. 372 -> 470 takes the visible gap from ~392px
   to ~294px, the 25% Roy asked for. The copy re-wraps a little wider as a
   result, which is the trade; keeping its right edge flush with the band
   is worth more than holding the old line breaks. */
.pricing {
  display: grid;
  grid-template-columns: 1fr minmax(290px, 470px);

  /* Column and row are set SEPARATELY, and that split is load-bearing. This
     was one `gap` shorthand, which meant the horizontal space between the
     headline and the copy and the vertical space down to the service grid
     were the same number and could not be tuned apart. Roy asked to pull the
     images up on 2026-08-17 without touching the side-by-side spacing, and
     with the shorthand that was impossible.
     Row goes to 0 so there is exactly ONE number controlling the drop to the
     images: .svc-grid's margin-top. Two stacked values fighting over the same
     gap is how it ended up at 180px in the first place. */
  column-gap: clamp(36px, 4.5vw, 86px);   /* pulled in 10% 2026-08-16 (was 40 / 5vw / 96) */
  row-gap: 0;
  align-items: start;
}

/* Sentence case, not the uppercase banner treatment the sections above run.
   This is a quieter register on purpose — the caps banners are for the
   turns in the argument, and this one opens a block. */
/* No max-width: the <br> is what sets the break, and a ch cap on top of it
   was wrapping the second line again and making three. Up 10% on Roy's call
   2026-08-16 (was 29 / 3vw / 46), which the copy can now carry because
   "margins" came off the second line in the same pass. */
.pricing-h2 {
  margin: 0;
  font-size: clamp(32px, 3.3vw, 51px);
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: 1.1;
  color: var(--ink);
}

/* A step up from .ocard-text: this paragraph carries the whole right column
   with nothing else around it, so it can afford the size. */
/* up 10% with the headline, so the two sides keep their proportion
   (was 16 / 1.25vw / 19) */
.pricing-text {
  margin: 0;
  font-size: clamp(17.6px, 1.375vw, 21px);
  line-height: 1.6;
  color: #5B6560;
}

/* ---- DORMANT: SECONDARY CTA (pulled from the markup 2026-08-17) ----
   Nothing on either page carries .btn-line now. It was the "Explore our
   services" button, which never had a destination and sat directly above the
   six services it was offering to take you to. Kept whole rather than
   trimmed, because this folder has no git history to restore it from.

   Secondary CTA. Square corners and a 1.5px green hairline, the same outline
   language as the .ocard pair, so it reads as part of this page rather than
   a second button system. The filled green pill stays reserved for Book a
   Demo, which keeps the hierarchy honest. */
.btn-line {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin-top: clamp(26px, 2.4vw, 36px);
  padding: 13px 20px;
  border: 1.5px solid var(--green);
  font-size: var(--nav-fs);
  font-weight: 500;
  color: var(--green);
  white-space: nowrap;
  transition: background .22s var(--ease), color .22s var(--ease);
}

.btn-line:hover {
  background: var(--green);
  color: #fff;
}

.btn-line-arrow {
  width: 14px;
  height: 14px;
  flex: none;
  /* nudges with the hover, so the button has somewhere to go */
  transition: transform .22s var(--ease);
}
.btn-line:hover .btn-line-arrow { transform: translateX(2px); }

/* ---- SERVICE GRID: three across, floating, no cards ----
   No card and no circle behind the icon on Roy's call 2026-08-16. The page
   already has two outlined-box systems (the .ocard pair and the stat tiles);
   a third would read as clutter, and the open space is what makes this row
   feel like a menu rather than a pitch. The columns are only held apart by
   the gap, so the row breathes at any width. */
.svc-grid {
  /* This sits inside .pricing, which is itself a two-column grid, so
     without this the whole row became one item in the LEFT column and got
     squeezed into half the page. It has to span both tracks to use the
     full band. */
  grid-column: 1 / -1;

  /* THE one number for the drop from the headline block to the images
     (Roy, 2026-08-17). It used to be clamp(72,8vw,128) sitting on top of the
     .pricing row gap, which stacked to 180px at 1440 and 214px at 1920, and
     left the grid reading as a separate section instead of the answer to the
     headline above it.
     Roy's reference was the space that used to sit between the subheader and
     the Explore button, measured at 35px, and he asked for about that, a
     touch looser. This lands at 46px at 1440 and 52px wide, close enough to
     that rhythm to feel related and enough air that the images are not
     crowding the copy. */
  margin-top: clamp(30px, 3.2vw, 52px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(56px, 6vw, 92px) clamp(32px, 4vw, 68px);
}

/* Left-aligned since 2026-08-17, when the drawn icons were replaced by
   photos. Centred text under a centred icon was fine, because a symmetrical
   glyph has no edge to line up to. A photo does: it is a hard-edged rectangle
   the full width of the column, so the name and copy now hang off its left
   edge. That single alignment is most of what makes the row look composed
   rather than floated. */
.svc {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  text-align: left;
}

/* ---- SERVICE MEDIA ----
   Square corners and a 3:2 window, matching .mega-media in the nav menu and
   .ocard-media in the section above. Every media slot on this site is
   square-cornered; a radius here would read as borrowed from another page.
   The images are 3:2 and 16:9, so the box is fixed and `cover` absorbs the
   difference rather than the row going ragged. */
.svc-media {
  display: block;
  position: relative;
  margin: 0 0 clamp(18px, 1.7vw, 26px);
  width: 100%;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  /* holds the slot at full height while the photo streams in, so the names
     below never jump once images land */
  background: #EDEDEB;
  /* Edge containment, not a card. Two of these six are SCREENSHOTS on white
     (the map pack, and the Google Ads dashboard behind the phone), and on a
     white page their real edges dissolve, so they read as loose content
     floating in the column instead of as pictures. At 1px and 8% ink this is
     invisible over the photographs and does nothing but hold the white ones
     in. This is deliberately not the .ocard border treatment: Roy killed
     boxes in this grid on 2026-08-16 and that still stands. */
  box-shadow: inset 0 0 0 1px rgba(13, 16, 22, .08);
}

.svc-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The same correction the nav menu already makes for this exact file: it is
   16:9 inside a 3:2 window, so a centre crop cuts the left edge off the Meta
   loop. Biasing the window left keeps the mark whole. */
.svc-media img[src*="svc-meta"] { object-position: 30% center; }

/* AWAITING-ART SLOT — currently unused (the last one, Advanced Website
   Design, got its photo on 2026-08-17). A tinted panel at the identical 3:2
   rather than a hole or a borrowed image, so a row missing one photo keeps
   its rhythm and the gap is obvious on sight. Kept live rather than deleted
   because a seventh service will land here eventually and this is the
   answer for it: add class="svc-media is-empty" with an inline svg. */
.svc-media.is-empty {
  background: rgba(0, 80, 50, .05);
  box-shadow: inset 0 0 0 1px rgba(0, 80, 50, .16);
  display: grid;
  place-items: center;
}

.svc-media.is-empty svg {
  width: 32%;
  fill: none;
  stroke: var(--green);
  stroke-width: .95;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: .3;
}

/* ============================================================
   SERVICE GRID HOVER (Roy, 2026-08-17)

   The brief: hovering one card lifts it, outlines it in green the way the
   Services menu in the nav already does, and drops the other five back so
   the one you are pointing at is the only thing in focus.

   Four things move together, on purpose. Any one of them alone reads as a
   cheap CSS trick; the set reads as the page responding to you.
     1. the card lifts and grows a little
     2. its photo pushes into its own frame, slower than the card moves
     3. the grey containment hairline turns into the brand green one
     4. the other five drop to 30%

   The timings are deliberately NOT equal. The card moves in .5s and the
   photo takes .9s, so the picture is still settling after the card has
   arrived. Matched durations feel mechanical; the lag is what makes it feel
   like weight.

   Everything is inside `hover: hover`. On a touch screen there is no hover,
   and a tap would otherwise latch one of these states on and leave a card
   stuck lifted with the rest of the grid greyed out until you tapped
   somewhere else.
   ============================================================ */
@media (hover: hover) {

  .svc {
    position: relative;    /* so the lifted card can rise above its neighbours */
    transition: opacity .5s var(--ease), transform .5s var(--ease);
  }

  .svc-media     { transition: box-shadow .4s var(--ease), transform .5s var(--ease); }
  .svc-media img { transition: transform .9s var(--ease); }
  .svc-name      { transition: color .35s var(--ease); }

  /* :has() is doing something a plain .svc-grid:hover cannot. The grid's own
     hover area includes the gutters between the cards, so with .svc-grid:hover
     every trip of the pointer across a gap would dim all six and highlight
     none, flickering the whole row. This fires only while a CARD itself is
     hovered. Browsers without :has() simply skip the rule and get the lift
     and the outline without the dimming, which is a fine floor. */
  .svc-grid:has(.svc:hover) .svc:not(:hover) {
    opacity: .3;
  }

  /* The card only LIFTS. The growing is done by the picture below, and that
     split is not cosmetic: scale() on .svc would resample the live text at a
     non-integer ratio, and the name and copy go visibly soft while held. Easy
     to miss on a retina screen and obvious on an external monitor. Moving the
     scale down to the media keeps every glyph pin sharp and looks identical,
     because the photo is the thing the eye reads as growing anyway. */
  .svc:hover {
    z-index: 2;
    transform: translateY(-10px);
  }

  .svc:hover .svc-media { transform: scale(1.03); }

  /* The green hairline is the same move .mega-item:hover makes in the nav
     menu, which is what Roy pointed at. It replaces the 8% grey containment
     line rather than stacking on it, so the edge never doubles.
     The second shadow is the lift: green-tinted rather than black, and pushed
     far down with a big negative spread so it reads as contact shadow instead
     of a glow. This page has no other drop shadows, so it stays a whisper. */
  .svc:hover .svc-media {
    box-shadow: inset 0 0 0 1.5px var(--green),
                0 22px 45px -28px rgba(0, 80, 50, .5);
  }

  /* the photo presses into its frame, the slow half of the move */
  .svc:hover .svc-media img { transform: scale(1.07); }

  .svc:hover .svc-name { color: var(--green); }

  /* the awaiting-art panel takes the same treatment, just a touch more fill,
     so the empty slot never feels dead next to five live ones */
  .svc:hover .svc-media.is-empty { background: rgba(0, 80, 50, .09); }
}

/* Motion off, meaning off. The lift and the photo push are both motion, so
   they go. The green outline, the green name and the dimming are STATE, not
   movement, and they stay: they are what tells you which card you are on. */
@media (hover: hover) and (prefers-reduced-motion: reduce) {
  .svc           { transition: opacity .5s var(--ease); }
  .svc-media     { transition: box-shadow .4s var(--ease); }
  .svc-media img { transition: none; }
  .svc:hover,
  .svc:hover .svc-media,
  .svc:hover .svc-media img { transform: none; }
}

/* ---- DORMANT: DRAWN SERVICE ICONS (replaced by photos 2026-08-17) ----
   Nothing carries .svc-icon any more. Three passes shipped and died here:
   hairline strokes, then thicker ones, then abstract layout blocks. Roy's
   call was the same each time, and the last one was final: "the icons just
   did not work at all." Kept rather than deleted only because this folder
   has no git history — do not spend a fourth pass on them.

   Tinted brand green rather than a grey: at this opacity it reads as
   texture sitting in the paper, the harvey.ai move, but it is still the
   page's own colour rather than a neutral bolted on. Stroke-only, matching
   the pain-band icons, so the whole site draws icons one way. */
.svc-icon {
  /* Sized off the harvey.ai grid Roy pointed at: the icon is roughly a
     third of its column, big enough to be architecture rather than a
     bullet. That is most of what makes their row feel calm. */
  width: clamp(104px, 10.5vw, 160px);
  height: clamp(104px, 10.5vw, 160px);
  margin-bottom: clamp(30px, 3.2vw, 48px);
  fill: none;
  stroke: var(--green);
  /* In viewBox units, so it scales WITH the icon.
     History, because this number has now been wrong in both directions:
     1.2 was tried first and read as a fat cartoon (8px at a 160px icon).
     The correction overshot to .62, and Roy called that one thin and lame
     on 2026-08-17. .95 is the middle, landing near 6.3px at 160px — clearly
     drawn rather than hairline, still nowhere near the 1.2 that failed. */
  stroke-width: .95;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Fainter than a normal icon on purpose: it should sit in the paper, not
     on top of it, so the name stays the loudest thing in the card.
     Raised from .2 with the same 2026-08-17 note. At .2 these were washing
     out to near-invisible, which was half of why they read as weak — a
     thicker stroke at that opacity would still have looked faded. The other
     half of the old reasoning is gone too: the prices these were built to
     stay quiet behind came out of the markup the same day. */
  opacity: .34;
}

/* ---- BRAND MARKS (Meta, Google) ----
   These two cards are named after somebody else's platform, so they carry
   that platform's real logo instead of an icon we drew. Registered marks are
   solid shapes, so they flip the fill/stroke of the set rather than being
   redrawn as outlines to match.

   They are also sized DOWN. A filled silhouette carries far more ink than a
   .95 stroke over the same box, so at matching dimensions the two logos
   punched a hole in the row and the four drawn icons looked timid next to
   them. 76% is where all six read as one family. This is the standard
   optical-size correction: equal measured size is not equal visual size. */
.svc-icon.is-brand {
  fill: var(--green);
  stroke: none;
  transform: scale(.76);
}

/* The same correction a second time, because the two logos are not the same
   KIND of shape. Meta's is an open ribbon with air through the middle;
   Google's is a solid near-circle, so it lays down far more ink over the same
   box and at a shared .76 it was the loudest thing in the grid. Judged by eye
   against the four drawn icons, not calculated — that is the only way optical
   weight is ever settled. */
.svc-icon.is-brand.is-g {
  transform: scale(.64);
}

.svc-name {
  margin: 0 0 12px;
  font-size: clamp(19px, 1.55vw, 23px);
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--ink);
}

/* The bottom margin was `auto` until 2026-08-17, to push the price block down
   to a common baseline. The copy is the last thing in the card now, so there
   is nothing left to push and the auto just ate the leftover space. */
/* The 34ch cap came off with the switch to photos. Centred under an icon it
   was doing real work, keeping the line from sprawling. Left-aligned under a
   full-width photo it did the opposite: it stopped the copy ~60px short of
   the picture's right edge and left a ragged gutter that made the block look
   unfinished. The column is the measure now, which lands near 41ch here,
   still well inside a comfortable line length. */
.svc-text {
  margin: 0;
  font-size: clamp(14.5px, 1.05vw, 16px);
  line-height: 1.6;
  color: #5B6560;
}

/* ---- DORMANT: PRICE BLOCK (pulled from the markup 2026-08-17) ----
   Nothing on the page carries these classes any more. They are kept whole,
   and NOT trimmed to what looks used, because this folder has no git history
   to recover them from. If a rate card is ever set, the price goes back as
   two lines inside each <article class="svc">:
     <p class="svc-price">$2,500<span class="svc-per">/mo</span></p>
     <p class="svc-note">Starting price, SEO only</p>
   and .svc-text goes back to `margin: 0 0 auto` so the figures line up
   across each row again. */

/* margin-top:auto on the price is what levels the row: the descriptions run
   different lengths, and without it every price sat at a different height. */
.svc-price {
  margin: 26px 0 0;
  /* a price that wraps stops being one number, and the broken line pushed
     its own row's figures out of alignment */
  white-space: nowrap;
  font-size: clamp(30px, 2.6vw, 40px);
  font-weight: 600;
  letter-spacing: -.03em;
  line-height: 1;
  color: var(--ink);
}

.svc-per {
  margin-left: 3px;
  font-size: .42em;
  font-weight: 500;
  letter-spacing: 0;
  color: #8A938E;
}

/* Two lines are reserved whether the note needs them or not. The notes run
   different lengths, and since the block is pinned to the bottom of the
   cell, a note that wrapped pushed its own price up out of line with the
   rest of its row. */
.svc-note {
  margin: 10px 0 0;
  min-height: 2.8em;
  font-size: 13px;
  line-height: 1.4;
  color: #8A938E;
}

@media (max-width: 1040px) {
  .svc-grid { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  .svc-grid {
    grid-template-columns: 1fr;
    gap: 56px;
  }
}

/* Stacks at 1040, not the page's usual 860. Between those two widths the
   left column is still wide enough to hold the layout but too narrow for
   "protect your profit margins" on one line, so the pinned break spilled to
   a third line. Stacking before that happens is better than shrinking the
   headline for every size to rescue a 180px band.
   The <br> comes out here too: stacked, the copy wraps on its own. */
@media (max-width: 1040px) {
  .pricing {
    grid-template-columns: 1fr;
    /* Stacked, so this gap is vertical and it IS the whole space between the
       headline and the copy — no 1fr slack hiding in it like the wide layout
       above. Straight 25% off with Roy's call, was 32. */
    gap: 24px;
  }
  .pricing-h2 br { display: none; }
}


/* ============================================================
   MOBILE
   ============================================================ */

@media (max-width: 860px) {
  .nav-left { display: none; }
  .nav-inner { grid-template-columns: 1fr auto 1fr; }
  .hero-copy { bottom: 10dvh; }

  /* Legora's headline ramp gets steeper below tablet (41px at a 500px
     screen), so the desktop line would run too big down here. */
  .hero-copy h1 { font-size: clamp(32px, 8.2vw, 50px); }

  /* the trigger is hidden here, so the panel has nothing to hang off.
     A tap-open mobile menu is its own build. */
  .mega { display: none; }
}


/* ============================================================
   BOOK A DEMO PAGE  (/book/)
   ============================================================ */

/* This page is a single fixed screen — nothing scrolls. The strip runs at a
   smaller scale than the home page so the calendar still gets the room.
   Overriding the shared variables means every rule that already reads them
   picks the new sizes up, no duplicate styling. */
body.page-book {
  background: #001A10;
  overflow: hidden;          /* this page never scrolls */
  --logo-h:     74px;
  --logo-text:  19px;
  --logo-tone:  .82;   /* white on green, so it runs brighter than the .62 on white */
  --strip-gap:  52px;
}

/* The photo and the green wash are two layers of one background. The
   gradient alphas are the only dial: raise them to sink the photo further,
   lower them to bring it forward. It runs deeper at the edges than the
   centre so the frame has some depth instead of reading as flat fill. */
.book {
  /* Where the photo sits vertically inside the crop. 50% is dead centre and
     left the roofer sitting low; higher numbers pull him up the frame.
     There is only ~92px of travel at a 1512x824 window, so 85% is most of
     what the crop has to give. */
  --book-focus: 85%;

  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  background:
    radial-gradient(125% 105% at 50% 34%,
                    rgba(0, 80, 50, .78) 0%,
                    rgba(0, 52, 33, .92) 55%,
                    rgba(0, 24, 15, .97) 100%),
    url("/assets/img/book-bg.jpg") center var(--book-focus) / cover no-repeat;
  background-color: #001A10;
}

/* logo only, centred, same bar height as the main nav */
/* Same three-column setup as the home nav, so the logo stays dead centre
   with the back link sitting off to the left. */
.book-nav {
  position: relative;
  z-index: 2;
  height: var(--nav-h);
  padding: 0 var(--edge);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 24px;
}

/* Built to the same spec as the home nav links: same size, same pill, same
   200ms fade. The negative margin pulls the pill's padding back out so the
   arrow lines up on the page edge rather than sitting 13px in. */
.book-back {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-left: calc(var(--pill-pad) * -1);
  padding: 8px var(--pill-pad);
  border-radius: 999px;
  font-size: var(--nav-fs);
  font-weight: 400;
  letter-spacing: .01em;
  white-space: nowrap;
  color: #fff;
  opacity: .92;
  transition: background .2s var(--ease),
              color .2s var(--ease),
              opacity .2s var(--ease);
}

.book-back:hover {
  background: #fff;
  color: var(--ink);
  opacity: 1;
}

.book-back-icon {
  width: 15px;
  height: 15px;
  flex: none;
  transition: transform .2s var(--ease);
}
.book-back:hover .book-back-icon { transform: translateX(-2px); }

/* .nav-logo img already inverts to white, which is what this dark green
   ground needs. Nothing to override. */

/* Widget sits hard right against the page gutter, vertically centred in
   whatever is left below the logo bar. .book is a column so this can take
   the remaining height. */
/* Locked to exactly one screen: height, not min-height, so the trust strip
   sits on the bottom edge instead of pushing the page into a scroll. */
.book {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
  min-height: 0;
}

.book-body {
  position: relative;
  z-index: 10;              /* sits above the photo and the green wash */
  flex: 1;
  min-height: 0;            /* lets this shrink rather than force an overflow */
  display: flex;
  align-items: center;

  /* Optical centring, not box centring. The logo is centred inside its bar,
     so it leaves empty bar underneath that reads as part of the gap, while
     the Google mark below has no such slack. The bottom padding cancels it,
     so the visible gap above equals the visible gap below. */
  padding: 0 var(--edge) 27px;
}

/* Process on the left, calendar on the right. The container is capped and
   centred rather than run to the page gutter, which is what keeps the
   calendar sitting IN from the right edge instead of flush against it. */
.book-grid {
  /* The literal space between the step list and the calendar. It only works
     as a literal because the columns below hug their content: with a 1fr
     left column, the leftover width pooled INSIDE that column and the real
     gap was 212px while column-gap read 75.6px. Sizing the column to the
     copy and centring the pair makes this number the one you see. */
  --book-gap: 127px;

  width: 100%;
  max-width: 1240px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: minmax(0, 560px) auto;
  justify-content: center;
  align-items: center;
  gap: var(--book-gap);
}

.book-copy { max-width: 560px; }

/* same type spec as the home page hero, so the two pages feel related.
   The eyebrow and the standfirst that used to sit around this are gone, so
   the bottom margin here is what now sets the gap down to the step list. */
.book-h1 {
  margin: 0 0 34px;
  font-size: clamp(30px, 2.6vw + 8px, 46px);
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: 1.06;
  color: #fff;
}


/* ---- the numbered process list ---- */

.steps {
  margin: 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid rgba(255,255,255,.22);
}

.step { border-bottom: 1px solid rgba(255,255,255,.22); }

/* a button, not a div, so it is keyboard reachable and screen readers
   announce the open/closed state from aria-expanded */
.step-head {
  width: 100%;
  padding: 17px 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 20px;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  font: inherit;
  /* Was .42, which sat under 3:1 against this green and read as disabled
     rather than as a closed row. .66 is legible on its own while still
     clearly stepping back from the open one. */
  color: rgba(255,255,255,.66);
  transition: color var(--menu) var(--ease);
}
.step-head:hover { color: rgba(255,255,255,.9); }
.step.is-open .step-head { color: #fff; }

.step-title {
  font-size: clamp(17px, 1.25vw, 20px);
  font-weight: 500;
  letter-spacing: -.015em;
}

.step-num {
  flex: none;
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  opacity: .8;
}

/* Collapsed by height rather than display:none so it can animate. grid-rows
   0fr -> 1fr is the one way to transition to a content height you cannot
   know in advance. */
.step-body {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--menu) var(--ease);
}
.step.is-open .step-body { grid-template-rows: 1fr; }

.step-body > p {
  overflow: hidden;
  margin: 0;
  font-size: 15px;
  line-height: 1.6;
  color: rgba(255,255,255,.8);
}
.step.is-open .step-body > p { padding-bottom: 20px; }


/* ---- the calendar ---- */

/* Calendly's inline embed ships with hard square corners, so the radius has
   to come from the container clipping the iframe it injects. isolation gives
   that clip its own stacking context, which is what Safari needs before it
   will round an iframe at all. The shadow lifts the card off the photo so it
   reads as a layer on top rather than a panel cut into the background. */
.book-cal .calendly-inline-widget {
  /* 10% smaller overall. zoom rather than shrinking the box, because zoom
     scales the type and spacing too, and it gives Calendly a LARGER internal
     viewport for the same physical space, so it clips less rather than more. */
  zoom: .9;

  width: 520px;
  max-width: 100%;
  /* Whatever the screen has left once the logo bar, the trust strip and the
     padding are subtracted, capped at Calendly's comfortable height.
     Deliberately NOT a percentage: Calendly measures this container when it
     boots, and a % that has not resolved yet reads as zero and the widget
     renders empty. */
  height: min(640px, calc(100dvh - 272px));
  border-radius: 16px;
  overflow: hidden;
  isolation: isolate;
  box-shadow: 0 24px 60px rgba(0, 0, 0, .30);
}

/* one column once there is no room for two */
/* ---- STACKED BOOKING PAGE (Roy, 2026-08-18) ----
   Everything below fixes three defects that only existed under this width.

   1. THE PAGE NOW SCROLLS. Above 1080px the locked 100dvh viewport IS the
      design. Stacked, the content is taller than any phone, and because
      .book-body centres its child, the overflow went BOTH directions while
      body{overflow:hidden} made the top half unreachable. Measured: .book-h1
      sat at y = -201px at 320px, so "Here is exactly what happens next." and
      the whole of step 01 were invisible on every phone.

   2. THE CALENDAR FILLS THE COLUMN. justify-items:center turned the grid item
      shrink-to-fit, so its max-width:100% resolved against itself instead of
      the track and the widget stayed a fixed 468px — hanging 74px off the
      left edge at 320px. That was the only real horizontal overflow on the
      site. Stretch puts it back on the track.

   3. THE CALENDAR IS ON TOP. .book-body and .book-trust both claimed
      z-index:10, so the later element in the DOM won and the trust strip
      painted over 186px of the booking widget. */
@media (max-width: 1080px) {
  .book-grid {
    grid-template-columns: minmax(0, 1fr);
    justify-items: stretch;
  }
  .book-copy { max-width: 640px; margin-inline: auto; }

  body.page-book { overflow: visible; }

  .book {
    height: auto;
    min-height: 100dvh;
  }

  .book-body {
    align-items: flex-start;
    z-index: 20;
    padding-top: 24px;
    padding-bottom: 40px;
  }

  .book-cal { width: 100%; }

  /* zoom is a desktop fit trick; at full width it only shrinks the widget
     away from the column edges again. */
  .book-cal .calendly-inline-widget {
    zoom: 1;
    width: 100%;
    max-width: 560px;
    margin-inline: auto;
    height: 620px;
  }
}


/* ============================================================
   BOOKING PAGE — TRUSTED BY, white on the green
   ============================================================ */

/* Everything is flipped white here rather than restyled from scratch: the
   logo artwork is flat black, so one invert turns the whole row white and
   any real client logo added later comes out matching automatically. */
.book-trust {
  position: relative;
  z-index: 10;
  flex: none;
  padding-bottom: 26px;
  overflow: hidden;
}

.book-trust .rating {
  margin-bottom: 11px;
  gap: 8px;
}

/* the four-colour G, flattened to solid white */
.book-trust .rating-g { filter: brightness(0) invert(1); }
.book-trust .rating-stars svg { fill: #fff; }
.book-trust .rating-score { color: #fff; opacity: .8; }

.book-trust .trusted-label {
  margin: 0 0 22px;
  font-size: clamp(15px, 1.15vw, 17px);
  color: #fff;
  opacity: .92;
}

/* green on green would disappear, so the accent goes white and keeps its
   italic to stay distinguishable */
.book-trust .trusted-label .accent { color: #fff; }

.book-trust .marquee-set img { filter: invert(1); }
.book-trust .brand { color: #fff; }

/* Short windows. The page is locked to one screen, so the logo bar, the trust
   strip and the padding take a fixed 272px no matter the height. Below ~800px
   that leaves the widget under the ~520px Calendly needs and the time-zone row
   clips. Zooming out further buys internal room without touching the layout.
   Measured: 480px of container is where the clip starts. */
/* Short windows. The page is locked to one screen, so the logo bar, the trust
   strip and the padding take a fixed 272px no matter the height. Below ~800px
   that leaves the widget under the ~520px Calendly needs and the time-zone row
   clips. Zooming out further buys internal room without touching the layout.
   Measured: 480px of container is where the clip starts. */
/* Desktop-only. This shrink exists to fit a short LAPTOP window; below 1081px
   the page scrolls and the widget is full-width, where zooming it just pulls
   it back off the column edges. Without the min-width guard this rule also
   won on source order and undid the mobile zoom:1 above. */
@media (max-height: 800px) and (min-width: 1081px) {
  .book-cal .calendly-inline-widget { zoom: .78; }
}


/* ============================================================
   INFO PAIR — two outline cards under the filled one
   ============================================================ */

/* Same width and the same square corners as .info-card above, so the three
   read as one block. The gap between them is also the gap up to the filled
   card, which is what keeps the grouping obvious. */
/* --pair-gap is inherited from .info, so this gap, the gap above these cards
   and the gap beside the video are all literally the same number. */
.info-pair {
  max-width: var(--band-w);
  margin: var(--pair-gap) auto 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--pair-gap);
}

/* Outline, not fill. The white section shows through, which is the whole
   point: next to the solid green card these read as the quiet pair.
   space-between is doing the layout work — label pinned top, the substance
   pinned bottom — so the tall shape looks composed rather than half empty. */
.ocard {
  /* padding moved off the card and onto the text blocks, so a media panel can
     run the full width between the borders */
  --ocard-pad: clamp(26px, 2.4vw, 38px);

  /* Was 5/6. At that height the air between the photo and the headline
     measured 96px; Roy called it too much (2026-08-15), so the card is
     shortened until that gap sits at half, ~48px. */
  aspect-ratio: 9 / 10;
  border: 1.5px solid var(--green);
  background: transparent;
  padding: 0;
  overflow: hidden;             /* keeps the full-bleed image inside the border */
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  transition: background .22s var(--ease);
}

/* Edge to edge across the card. The mask is what turns the bottom of the
   photo into a fade instead of a cut line: it ramps the image's own alpha to
   zero, so it dissolves into whatever the card is sitting on rather than
   being covered by a matching-coloured gradient that would break the moment
   the background changes. */
.ocard-media {
  margin: 0;
  width: 100%;
  aspect-ratio: 3 / 2;
  flex: none;
  -webkit-mask-image: linear-gradient(to bottom, #000 52%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 52%, transparent 100%);
}
.ocard-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: top center;
}
.ocard:hover { background: rgba(0, 80, 50, .04); }

/* the padding the card gave up */
.ocard-foot { padding: 0 var(--ocard-pad) var(--ocard-pad); }

.ocard-eyebrow {
  margin: 0;
  padding: var(--ocard-pad) var(--ocard-pad) 0;
  font-size: 12px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--green);
}

.ocard-title {
  margin: 0 0 14px;
  font-size: clamp(28px, 2.9vw, 44px);
  font-weight: 400;
  letter-spacing: -.03em;
  line-height: 1.08;
  color: var(--ink);
}

/* No max-width. A 40ch cap was holding the copy to about 300px inside a
   500px column, which is what left the empty gutter on the right. The column
   itself is the measure now, and at this size that lands around 58ch, still
   inside the comfortable range for reading. */
.ocard-text {
  margin: 0;
  font-size: clamp(15px, 1.15vw, 17.5px);
  line-height: 1.55;
  color: #5B6560;
}

/* stacked, and the portrait shape is dropped so they are only as tall as
   their copy needs.
   THIS BLOCK WAS DEAD until 2026-08-18: a stray top-level "}" sat directly
   above it, and CSS parsing swallows a lone brace plus whatever follows as an
   invalid selector, so the whole media query was discarded. The symptom was
   silent — .ocard kept its 9/10 portrait shape and overflow:hidden on phones,
   clipping 942px of copy at 320px and leaving a 59px-wide text column. If a
   media query ever "does nothing" again, count the braces above it. */
@media (max-width: 860px) {
  .info-pair { grid-template-columns: 1fr; }
  .ocard { aspect-ratio: auto; min-height: 300px; }
}


/* ============================================================
   FOOTER — colours from this page
   ============================================================

   Hook's shape exactly: a wide brand column carrying the logo and the two
   contact blocks, three link columns beside it, the signup tucked under the
   right two, then a hairline and the legal bar.

   The band is #001A10, the same value as .proof, so the page closes on the
   colour the reasons section opened with instead of introducing a fourth
   dark. Hook's near-black would have read as a hole against it, the same
   problem the comparison chart's black column had. */

.foot {
  /* The C is sized from ONE number. Its height comes off the artwork's own
     177x136 ratio, and the top padding is derived from that height, so the
     band always opens exactly half a mark plus a fixed gap below the seam.
     Change --foot-mark-w and everything else follows.

     HALVED 2026-08-17 because it was soft. mark.png is only 177px wide, so
     the old 280px cap was upscaling it past 3x on a retina screen. At a
     140px cap it renders at or under its native width on 1x and is far
     closer on 2x. If this ever needs to be big again it needs new artwork,
     an SVG or a mark exported at 4x, not a bigger number here.

     The wordmark carries its own width: it is 997px native and stayed sharp,
     so shrinking it alongside the C would have cost size for no reason. */
  --foot-mark-w: clamp(85px, 9.5vw, 140px);
  --foot-mark-h: calc(var(--foot-mark-w) * 136 / 177);
  --foot-word-w: clamp(170px, 19vw, 280px);

  position: relative;
  background: #001A10;
  padding: calc(var(--foot-mark-h) / 2 + 62px) var(--edge) 0;
  color: #fff;
}

/* Centred on the seam: top:0 is the band's edge, and pulling it up half its
   own height puts its middle exactly there. Left edge lines up with the
   content band rather than the viewport, so it sits over the wordmark. */
.foot-mark {
  position: absolute;
  top: 0;
  left: 50%;
  margin-left: calc(var(--band-w) / -2);
  width: var(--foot-mark-w);
  height: var(--foot-mark-h);
  transform: translateY(-50%);
  pointer-events: none;
}

/* Both halves are the SAME artwork used as an alpha mask, so the shape can
   be painted any colour and stays crisp at this size — no second file, and
   no filter tricks that only approximate the green. clip-path then shows
   each layer only on its own side of the seam. */
.foot-mark span {
  position: absolute;
  inset: 0;
  -webkit-mask: url("/assets/img/mark.png") no-repeat center / contain;
          mask: url("/assets/img/mark.png") no-repeat center / contain;
}

/* Each half wears the colour of the side it is NOT standing on, so the mark
   reads as one shape knocked straight through the seam. The top is the
   footer's own #001A10 rather than the brand green: brand green is a
   different, lighter value, and using it here broke the illusion that the
   band was showing through. */
.foot-mark-top {
  background: #001A10;             /* the footer's colour, on the white */
  clip-path: inset(0 0 50% 0);
}

.foot-mark-bot {
  background: #fff;                /* the white section's colour, on the green */
  clip-path: inset(50% 0 0 0);
}

.foot-inner {
  max-width: var(--band-w);
  margin: 0 auto;
  display: grid;
  /* brand column carries two contact blocks, so it needs the extra room */
  grid-template-columns: 1.55fr 1fr 1fr 1.15fr;
  gap: 56px 40px;
  padding-bottom: 72px;
}

/* ---- brand + contact ---- */

.foot-brand { grid-row: span 2; }

/* Wordmark only now, set to the SAME width as the C above it so the two
   stack into one vertical lockup instead of reading as two stray marks. */
.foot-logo {
  display: block;
  margin-bottom: 44px;
}
.foot-logo .logo-word {
  width: var(--foot-word-w);
  height: auto;
  filter: invert(1);            /* black artwork, white on the green */
}

.foot-contact {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  margin-bottom: 26px;
}

/* Mint, the one accent this band already uses elsewhere */
.foot-icon {
  flex: none;
  width: 26px;
  height: 26px;
  margin-top: 2px;
  fill: none;
  stroke: #9FD8BE;
  stroke-width: 1.4;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.foot-contact-copy { display: block; }

.foot-label {
  display: block;
  margin-bottom: 4px;
  font-size: 14px;
  color: rgba(255,255,255,.5);
}

.foot-value {
  display: block;
  font-size: 17px;
  font-weight: 700;
  line-height: 1.4;
  color: #fff;
}
a.foot-value:hover { color: #9FD8BE; }

/* ---- link columns ---- */

.foot-col {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 14px;
}

.foot-head {
  margin: 0 0 8px;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: .02em;
  text-transform: uppercase;
  color: #fff;
}

.foot-col a {
  font-size: 15.5px;
  line-height: 1.3;
  color: rgba(255,255,255,.72);
  transition: color .2s var(--ease);
}
.foot-col a:hover { color: #fff; }

/* ---- signup ----
   Placed by hand under the Services and Resources columns, which is where
   Hook runs theirs. Explicit column lines rather than auto-flow so it can
   never wander into the brand column. */
.foot-signup {
  grid-column: 3 / -1;
  grid-row: 2;
  align-self: end;
}

.foot-signup-copy {
  margin: 0 0 14px;
  font-size: 15px;
  color: rgba(255,255,255,.72);
}

/* The square outlined button from the proof band, not a pill (Roy asked for
   that exact one, 2026-08-17). It carries .proof-btn itself rather than
   copying its rules, so the two can never drift apart. The only difference is
   the gap above: the proof-band one sits under a paragraph, this one sits
   under a single line. */
.foot-btn {
  margin-top: 4px;
  /* Runs the full column so it squares off against the line above it instead
     of sitting as a small tab under one end of it. display:flex rather than
     the inline-flex it inherits, so the width actually takes. */
  display: flex;
  width: 100%;
}

/* Centred to sit over the middle of that button. Left-aligned, it read as
   off-centre against a block running the whole column (Roy, 2026-08-17). */
.foot-signup-copy { text-align: center; }

/* ---- legal bar ---- */

.foot-bar {
  max-width: var(--band-w);
  margin: 0 auto;
  padding: 26px 0 34px;
  border-top: 1px solid rgba(255,255,255,.13);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

.foot-legal {
  margin: 0;
  font-size: 14px;
  color: rgba(255,255,255,.55);
}
.foot-legal a { text-decoration: underline; color: inherit; }
.foot-legal a:hover { color: #fff; }

.foot-social {
  display: flex;
  align-items: center;
  gap: 10px;
}

.foot-follow {
  margin-right: 4px;
  font-size: 14px;
  color: rgba(255,255,255,.55);
}

.foot-soc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 1px solid rgba(255,255,255,.28);
  border-radius: 50%;
  transition: background .2s var(--ease), border-color .2s var(--ease);
}
.foot-soc svg { width: 15px; height: 15px; fill: rgba(255,255,255,.8); }
.foot-soc:hover { background: rgba(255,255,255,.1); border-color: rgba(255,255,255,.55); }
.foot-soc:hover svg { fill: #fff; }

/* ---- responsive ---- */

@media (max-width: 1000px) {
  .foot-inner { grid-template-columns: 1fr 1fr; gap: 48px 32px; }
  .foot-brand { grid-row: auto; grid-column: 1 / -1; }
  .foot-signup { grid-column: 1 / -1; grid-row: auto; }
}

@media (max-width: 600px) {
  /* Still derived from the mark's height, just a tighter gap under it. A
     flat 64px here used to override the calc and let the wordmark ride up
     into the bottom of the C on phones. */
  .foot { padding-top: calc(var(--foot-mark-h) / 2 + 34px); }
  .foot-inner { grid-template-columns: 1fr; gap: 40px; }
  .foot-bar { flex-direction: column; align-items: flex-start; }
}


/* ============================================================
   MOBILE POLISH — added 2026-08-18 after a measured device audit
   ============================================================

   These are corrections, not new design. Each one fixes something that was
   measured as broken on a phone, and each is scoped to a breakpoint so the
   desktop layout is untouched. Kept together at the end of the file on
   purpose: the breakpoints above are scattered across nine widths, and
   burying these among them is how the last round of bugs went unnoticed. */

@media (max-width: 860px) {

  /* THE NAV LOGO WAS CRUSHED TO 21% OF ITS WIDTH.
     `1fr auto 1fr` forces the two outer tracks equal, and the right one has
     to hold a `white-space: nowrap` button (148.9px). That starved the centre
     track, and because the wordmark is `max-width:100%` with a locked
     `height:14px`, it lost its aspect ratio instead of scaling -- an
     unreadable 37.5px smear above the fold at 320px.
     .nav-left is display:none down here, so there are only two real grid
     items. Two tracks, logo left, button right. */
  .nav-inner {
    grid-template-columns: auto 1fr;
    gap: 12px;
  }

  /* TAP TARGETS. Every interactive element on the page measured under the
     44x44 minimum; the two main CTAs were 34px tall. The pill's visual
     proportions are a desktop decision, so the height only grows here. */
  .btn { height: 44px; }
}

/* Below this the full lockup plus the button cannot fit on one line without
   shrinking the type, so the wordmark drops and the C carries the brand.
   Standard compact-header behaviour, and it keeps the mark crisp rather than
   scaling artwork that is only 177px wide to begin with. */
@media (max-width: 480px) {
  .nav-logo .logo-word { display: none; }
}

@media (max-width: 640px) {

  /* --band-w is `min(1190px, 78vw)`, which is right on a desktop and wrong on
     a phone: it was throwing away 22% of a 320px screen (35px of dead gutter
     each side) on top of each section's own padding. A phone wants a fixed
     gutter, not a percentage. */
  :root { --band-w: calc(100vw - 32px); }

  /* The number column was taking 66px of a 249px band, leaving a 131px copy
     column -- about 17 characters per line. */
  .step-row { grid-template-columns: 46px 1fr; }
  .step-n { font-size: 30px; }
}

/* Tap targets, everything that was still under 44px after the .btn fix.
   Padding rather than a bigger gap, so the hit area grows while the lists
   keep their rhythm and nothing moves visually except the spacing.
   Values are set so measured height lands at 44-46px, not 42 -- the first
   pass used 11px and came out one pixel short across the whole footer. */
@media (max-width: 860px) {
  .foot-col { gap: 0; }
  .foot-col a {
    display: block;
    padding: 12px 0;
  }
  .foot-soc {
    width: 44px;
    height: 44px;
  }
  .foot-legal a {
    display: inline-block;
    padding: 15px 0;
  }
  .foot-value {
    display: inline-block;
    padding: 11px 0;
  }
  /* Both logo links are images with no padding of their own: 23x18 in the
     nav, 342x13 in the footer. The nav one also needs a WIDTH floor -- below
     480px the wordmark is hidden and only the 23px C remains. */
  .nav-logo {
    padding: 13px 0;
    min-width: 46px;
    min-height: 46px;
    justify-content: center;
  }
  .foot-logo { padding: 16px 0; }

  /* flex would otherwise shrink these below their set 44px at 320px */
  .foot-soc { flex: none; }

  /* the only control on the booking page */
  .book-back { padding: 6px 0; min-height: 44px; }
}

/* The incoming deck frame is translated up 21% and scaled .86 inside a
   taller-than-square stack, so it escaped the bottom of its box by 0.11x the
   stack width and landed on the headline below -- 31px of overlap at 768px,
   despite the comment upstream promising the frames stay inside the box.
   More gap is the cheap, non-structural fix. */
@media (max-width: 860px) {
  .proof-feature { gap: 64px; }
}

/* 7.6px type. --logo-text is overridden to 19px on the booking page and
   .brand-sub is 40% of it. */
@media (max-width: 1080px) {
  .page-book .brand-sub { font-size: 12px; }
}

/* No phone needs 1920x1080 of hero footage to fill a 390px viewport — that is
   6.3MB pulled over cellular for something the poster already shows. The
   poster is the same image the video's first frame is, so hiding the video
   here is invisible to the visitor. Matches the existing reduced-motion
   treatment further up the file. */
@media (max-width: 860px) {
  .hero-video { display: none; }
  .hero-media {
    background: #000 url("/assets/img/hero-poster.jpg?v=8") center / cover no-repeat;
  }
}

/* The example-month disclaimer under the results list. Deliberately readable
   rather than fine print: it is the sentence that makes the numbers above it
   honest, so hiding it would defeat the purpose. */
.results-note {
  max-width: 46ch;
  margin: 0 0 34px;
  font-size: clamp(13.5px, 1.05vw, 15px);
  line-height: 1.55;
  color: rgba(255,255,255,.6);
}
@media (max-width: 860px) {
  .results-note { margin-bottom: 26px; }
}


/* ============================================================
   ABOUT PAGE — /about/
   Structure modeled on legora.com/about, the same way the home
   page is modeled on legora.com.

   THE ONE IDEA THIS PAGE IS BUILT ON. Legora's about page runs an
   ASYMMETRIC THREE-COLUMN GRID and almost never fills it. Body copy
   lives in the MIDDLE third only, section titles hang out in the
   LEFT third against the page edge, and photographs start at the
   middle third and run right. The empty left column is not leftover
   space, it is the layout: it is what lets four short paragraphs
   hold a full page without looking thin.

   That is exactly the property this page needs. Cornell has two
   people, one client and no published performance numbers, so a
   dense agency about-page (Hook Agency's, with thirty staff bios and
   an Inc 5000 badge) would advertise the gap on every scroll. This
   one carries very little content on purpose and reads deliberate.

   Backgrounds change three times down the page — paper, greige, warm
   grey, paper — and the exact values are sampled off Legora's own
   page. The shifts are what mark the turns in the argument, so the
   reader feels the subject change without a heading announcing it.
   ============================================================ */

.about {
  /* Sampled from legora.com/about. --paper (#FAFAF9) already matches
     their hero white exactly, so only the two new tones are declared. */
  --ab-greige: #ECECE7;   /* the "why we exist" and values band */
  --ab-warm:   #D0CDC8;   /* the quote band, one step darker again */

  /* Every photograph on the page, plus the CTA card, share one radius.
     Matches --proof-radius on the home page's feature row. */
  --ab-radius: 14px;

  /* Vertical rhythm. Airier than the home page's 110/140 because this
     page has perhaps a fifth of its word count and the whitespace is
     doing the work the copy would otherwise do. */
  --ab-pad:  clamp(88px, 8.5vw, 146px);

  /* The grid cap. Below this the three columns are true thirds of the
     viewport; above it they stop growing, so the measure never opens
     past comfortable on a wide monitor. At 1440px this yields ~464px
     per column, which is within a few px of what Legora sets. */
  --ab-max:  1500px;

  --ab-ink-soft: #5B6560;   /* the same secondary grey .pricing-text runs */

  background: var(--paper);
}

/* ---- NAV, LIGHT MODE ----
   The home nav is white type over a black hero video and only turns
   dark once you scroll past it. This page opens on paper, so that
   default would be an invisible nav until the first scroll. Type and
   logo go dark immediately; the white bar and its hairline still
   arrive on scroll through the existing .is-scrolled rules. */
.page-about .nav-left a { color: var(--ink); }
.page-about .nav-logo img { filter: none; }

/* A white hover pill is invisible on paper. This is the same grey the
   nav already switches to once the bar itself is white — it just has
   to apply from the top of this page rather than after 24px. */
.page-about .nav-left a:not(.nav-trigger):hover {
  background: #F1F1F0;
  color: var(--ink);
}

/* Current page. Deliberately quiet: full opacity against the .92 the
   other links sit at, and nothing else. A rule or a pill here would
   be louder than a nav item needs to be. */
.page-about .nav-left a.is-current { opacity: 1; font-weight: 500; }


/* ---- THE GRID ---- */

.ab-band {
  padding-left:  var(--edge);
  padding-right: var(--edge);
}

.ab-grid {
  max-width: var(--ab-max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  column-gap: 0;
}

/* Body copy: the middle third, nothing else. This is the narrow measure
   that gives the page its editorial feel. */
.ab-col { grid-column: 2; }

/* Statements and the team spread: middle third plus the right one. */
.ab-col-wide { grid-column: 2 / span 2; }

/* Section titles, hard against the left page edge, vertically level with
   the first line of the copy beside them. */
.ab-rail-h {
  grid-column: 1;
  margin: 0;
  padding-right: 24px;
  font-size: clamp(21px, 2.15vw, 31px);
  font-weight: 400;
  letter-spacing: -.02em;
  line-height: 1.15;
  color: var(--ink);
}


/* ---- 1. HERO ---- */

.ab-hero {
  /* Clears the fixed nav, then holds Legora's drop to the eyebrow. */
  padding-top: calc(var(--nav-h) + clamp(44px, 5vw, 86px));
  padding-bottom: clamp(52px, 5vw, 82px);
}

/* Brand name in Lemon Milk (an uppercase-only face, so this is the one
   place on the page it earns its keep), then the descriptor in the body
   sans at a lighter tone. Two different jobs, so two different faces. */
.ab-eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  margin: 0 0 clamp(22px, 2.2vw, 34px);
}

.ab-eyebrow-brand {
  font-family: "Lemon Milk", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  font-size: 11px;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--ink);
}

.ab-eyebrow-note {
  font-size: 13.5px;
  letter-spacing: .01em;
  color: #8A8F8C;
}

/* The statement. Regular weight at display size is the whole trick:
   Legora never bolds this, and the restraint is what separates it from
   a headline. Negative tracking keeps a 58px line from opening up. */
.ab-statement {
  margin: 0;
  max-width: 880px;
  font-size: clamp(28px, 4vw, 58px);
  font-weight: 400;
  letter-spacing: -.025em;
  line-height: 1.16;
  color: var(--ink);
}


/* ---- 2. FIGURES ----
   Three widths, used once each, so no two photographs on the page make
   the same gesture:
     .ab-figure-full    page edge to page edge      (the company shot)
     .ab-figure-offset  middle third, stops short   (inside the why band)
     .ab-figure-wide    middle third to the edge    (after the values) */

.ab-figure {
  margin: 0;
}

.ab-figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: var(--ab-radius);
}

.ab-figure-full {
  max-width: var(--ab-max);
  margin: 0 auto;
}
.ab-figure-full img { aspect-ratio: 16 / 7.4; }

.ab-figure-offset {
  grid-column: 2;
  margin-top: clamp(56px, 6vw, 96px);
  /* Runs ~48% past its own column so it stops short of the right edge,
     the way Legora's first offset shot does. */
  width: 148%;
  max-width: 148%;
}
.ab-figure-offset img { aspect-ratio: 4 / 3; }

/* The padding-bottom is not optional. This figure is the last thing before
   the warm-grey quote band, and with no bottom space its rounded corners
   sat exactly on the colour change, which clipped them into square ones
   against the new background. */
.ab-figure-wide {
  max-width: var(--ab-max);
  margin: 0 auto;
  padding-top: clamp(70px, 7vw, 118px);
  padding-bottom: clamp(70px, 7vw, 118px);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.ab-figure-wide img {
  grid-column: 2 / span 2;
  aspect-ratio: 16 / 8.4;
}

@media (max-width: 1040px) {
  .ab-figure-offset { width: 100%; max-width: 100%; }
  .ab-figure-wide { display: block; }
}


/* ---- 3. STATS ----
   Legora's row, in Cornell's serif. Playfair at --serif-wght is already
   the site's display face (see the @font-face note at the top of this
   file for why 450 and not 400) so this costs no new type.

   The numerals are policy, not performance — see the note in the markup
   before changing them. */

.ab-stats {
  padding-top: clamp(78px, 8vw, 132px);
  padding-bottom: clamp(78px, 8vw, 132px);
}

/* NARROWER THAN --ab-max ON PURPOSE. At the full grid width the three
   numerals land at 16% / 50% / 83% of the window and read as three
   unrelated facts pushed into the corners. Legora sets theirs at roughly
   23% / 50% / 77%, close enough to be one group. 1170px is the width
   that reproduces that spacing at 1440. */
.ab-stats-inner {
  max-width: 1170px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(34px, 4vw, 60px);
  text-align: center;
}

.ab-stat-n {
  margin: 0 0 10px;
  font-family: var(--serif);
  font-weight: var(--serif-wght);
  font-size: clamp(46px, 4.9vw, 72px);
  line-height: 1;
  letter-spacing: -.01em;
  color: var(--ink);
  font-variant-numeric: lining-nums;
}

.ab-stat-l {
  margin: 0;
  font-size: clamp(12.5px, 1vw, 14px);
  letter-spacing: .01em;
  color: #8A8F8C;
}

@media (max-width: 620px) {
  .ab-stats-inner { grid-template-columns: 1fr; gap: 40px; }
}


/* ---- 4. WHY WE EXIST ---- */

.ab-why {
  background: var(--ab-greige);
  padding-top: var(--ab-pad);
  padding-bottom: clamp(70px, 7vw, 118px);
}

/* Quieter than the values below it on purpose. Legora sets their mission
   copy SMALLER than their values copy, which makes the mission read as
   the setup and the values as the argument. Same hierarchy here. */
.ab-lede {
  margin: 0 0 1.35em;
  font-size: clamp(16px, 1.3vw, 18.5px);
  line-height: 1.62;
  color: var(--ink);
}
.ab-lede:last-child { margin-bottom: 0; }


/* ---- 5. THE CORNELL WAY ---- */

.ab-way {
  background: var(--ab-greige);
  padding-top: clamp(96px, 10vw, 168px);
  padding-bottom: var(--ab-pad);
}

.ab-value + .ab-value { margin-top: clamp(52px, 5.5vw, 88px); }

/* Tiny caps name, then the descriptor beside it at the same size in grey.
   Legora runs both on one line and lets them wrap together on a phone. */
.ab-value-label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 12px;
  margin: 0 0 16px;
}

.ab-value-name {
  font-family: "Lemon Milk", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  font-size: 11px;
  letter-spacing: .085em;
  text-transform: uppercase;
  color: var(--ink);
}

.ab-value-note {
  font-size: 12.5px;
  letter-spacing: .01em;
  color: #8A8F8C;
}

/* Larger than the mission copy above — see the note on .ab-lede.
   Measured against Legora's equivalent block and raised ~11% to match it;
   the first pass came out visibly smaller than the reference and the values
   lost the weight they need to carry this section. */
.ab-value-text {
  margin: 0;
  font-size: clamp(19px, 1.72vw, 25px);
  line-height: 1.5;
  letter-spacing: -.005em;
  color: var(--ink);
}

/* THE GREY-TO-BLACK REVEAL. This is Legora's signature on this block and
   the reason their three values read as an argument rather than a list:
   the paragraph sits pale until it scrolls up into the page, then settles
   to full ink.

   Same one-shot .is-armed / .is-in pattern as the pain band and the
   headline wipe on the home page — .is-armed is added by main.js, so a
   dead script leaves the copy fully legible instead of permanently grey. */
.ab-way.is-armed .ab-value-text {
  color: rgba(13, 16, 22, .3);
}

.ab-way .ab-value-text {
  transition: color .75s var(--ease);
}

.ab-way.is-armed .ab-value.is-in .ab-value-text {
  color: var(--ink);
}

/* Staggered so the three do not all resolve on the same frame. */
.ab-way.is-armed .ab-value:nth-child(2).is-in .ab-value-text { transition-delay: .08s; }
.ab-way.is-armed .ab-value:nth-child(3).is-in .ab-value-text { transition-delay: .16s; }

@media (prefers-reduced-motion: reduce) {
  .ab-way .ab-value-text { transition: none; }
  .ab-way.is-armed .ab-value-text { color: var(--ink); }
}


/* ---- 6. THE FOUNDER QUOTE ---- */

.ab-quote-band {
  background: var(--ab-warm);
  padding-top: clamp(96px, 10vw, 164px);
  padding-bottom: clamp(96px, 10vw, 164px);
}

.ab-quote {
  max-width: 940px;
  margin: 0 auto;
  text-align: center;
}

/* DEVIATION FROM LEGORA, ON PURPOSE. Their pull quote is set in their
   body sans. This one runs --serif, because Cornell already uses that
   face for exactly this register — .proof-title and .results-value on
   the home page — and because the three stat numerals further up this
   page are set in it too. Borrowing Legora's sans here would put a
   second, unrelated display idea on one page. */
.ab-quote-copy {
  margin: 0;
  font-family: var(--serif);
  font-weight: var(--serif-wght);
  font-size: clamp(23px, 2.5vw, 37px);
  line-height: 1.32;
  letter-spacing: -.01em;
  color: var(--ink);
}

.ab-quote-by {
  margin-top: clamp(30px, 3vw, 46px);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.ab-quote-name {
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
}

.ab-quote-role {
  font-size: 12.5px;
  color: #77807B;
}


/* ---- 7. WHO YOU GET ---- */

.ab-team {
  padding-top: var(--ab-pad);
  padding-bottom: var(--ab-pad);
}

/* Two people across the two right-hand columns. Legora has no team block
   at all, so this is their grammar applied to Hook Agency's content: no
   circular avatars, no cards, no hover states, just two portraits at the
   page's shared radius with the copy set under them. */
.ab-team-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 3vw, 52px);
}

.ab-portrait {
  border-radius: var(--ab-radius);
  overflow: hidden;
  aspect-ratio: 4 / 5;
  margin-bottom: 22px;
}

.ab-portrait img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* NO HEADSHOTS EXIST YET (2026-08-18). This holds the exact box the real
   4:5 portrait will occupy, so dropping the photo in later changes the
   picture and nothing about the layout. A flat tint rather than an icon
   or a silhouette: a placeholder that draws attention to itself is worse
   than one that just holds space quietly. */
.ab-portrait.is-empty {
  background:
    linear-gradient(160deg, #E4E4DF 0%, #D8D8D2 58%, #CFCFC8 100%);
}

.ab-person-name {
  margin: 0 0 4px;
  font-size: clamp(19px, 1.6vw, 24px);
  font-weight: 400;
  letter-spacing: -.015em;
  color: var(--ink);
}

.ab-person-role {
  margin: 0 0 14px;
  font-family: "Lemon Milk", "Helvetica Neue", Arial, sans-serif;
  font-weight: 400;
  font-size: 10.5px;
  letter-spacing: .085em;
  text-transform: uppercase;
  color: #8A8F8C;
}

.ab-person-bio {
  margin: 0;
  font-size: clamp(15px, 1.2vw, 17px);
  line-height: 1.6;
  color: var(--ab-ink-soft);
}


/* ---- 8. CTA CARD ----
   Legora's closing card, in Cornell's green. .proof-btn is already the
   white-outlined pill the home page runs on its dark band, so the button
   needs no new rules at all. */

.ab-cta {
  padding-bottom: clamp(96px, 10vw, 160px);
}

.ab-cta-card {
  max-width: var(--ab-max);
  margin: 0 auto;
  background: var(--green);
  border-radius: 18px;
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(28px, 3vw, 56px);
  padding: clamp(34px, 3.4vw, 58px);
}

.ab-cta-h {
  margin: 0 0 18px;
  font-size: clamp(26px, 2.6vw, 40px);
  font-weight: 400;
  letter-spacing: -.025em;
  line-height: 1.15;
  color: #fff;
  /* Two lines, the way the reference card sets it. 15ch gave four short
     ones and 19ch gave three; this headline is 55 characters, so it needs
     roughly 28 to break in half. Measured, not guessed — if the copy
     changes length, re-measure rather than nudging this. */
  max-width: 28ch;
}

.ab-cta-sub {
  margin: 0;
  font-size: clamp(15px, 1.2vw, 17.5px);
  line-height: 1.6;
  color: rgba(255,255,255,.72);
  max-width: 46ch;
}

.ab-cta-art { margin: 0; }

.ab-cta-art img {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 12px;
}


/* ---- RESPONSIVE ----
   The asymmetric grid is the entire design, and it is also the first
   thing that has to go: a middle-third measure on a 900px window leaves
   ~300px of text, which is too narrow to read. Below 1040px the grid
   collapses and every band runs one full-width column, with the section
   titles sitting above their copy instead of beside it. */

@media (max-width: 1040px) {
  .ab-grid { display: block; }

  .ab-col,
  .ab-col-wide { max-width: 760px; }

  .ab-rail-h {
    padding-right: 0;
    margin-bottom: clamp(28px, 3.4vw, 44px);
  }

  .ab-statement { max-width: 760px; }
}

@media (max-width: 860px) {
  .ab-cta-card { grid-template-columns: 1fr; }

  /* Picture first, the way the home page's feature row stacks. */
  .ab-cta-art { order: -1; }
  .ab-cta-h { max-width: none; }

  .ab-figure-full img { aspect-ratio: 16 / 10; }
  .ab-figure-wide img { aspect-ratio: 16 / 10; }
}

@media (max-width: 700px) {
  .ab-team-grid { grid-template-columns: 1fr; gap: 46px; }

  /* A full-width 4:5 portrait on a phone is most of a screen. */
  .ab-portrait { aspect-ratio: 1 / 1; }
}

@media (max-width: 560px) {
  .about { --ab-radius: 10px; }
  .ab-figure-full img,
  .ab-figure-wide img,
  .ab-figure-offset img { aspect-ratio: 4 / 3; }
}

/* ---- ABOUT: logo strip under the numbers (Roy, 2026-08-18) ----
   The marquee itself is already styled globally; this only handles the gap
   above it and the label, and re-establishes the horizontal clipping that
   .ab-stats-inner does not provide. The strip runs the FULL page width on
   purpose, like the home page's, so the logos travel across the whole screen
   rather than inside the 1170px stat column. */
.ab-strip {
  margin-top: clamp(56px, 6vw, 88px);
}

.ab-strip .trusted-label {
  margin: 0 0 var(--strip-inset);
  text-align: center;
}

/* .marquee is a flex child on the home page (it sits beside the rating), so
   its flex:1 does nothing here and it needs its own width to clip against. */
.ab-strip .marquee {
  width: 100%;
  display: block;
}
