/* ============================================================
   Monsoons — public site.
   Deliberately single-theme: bone paper body, indigo hero. See BRAND.md.
   ============================================================ */

/* Self-hosted variable fonts. System defaults are the loudest "generated"
   tell there is, and a webfont CDN adds a round trip plus a privacy problem.
   Latin subset only: 68KB + 28KB, and both are preloaded on all five pages.
   Fraunces (OFL) carries the display personality; Manrope (OFL) keeps
   navigation, body copy, and calls to action crisp and contemporary.
   Two families and no more. A third face is the point where a page starts
   looking assembled rather than designed. */

@font-face {
  font-family: 'Fraunces';
  src: url('./fonts/Fraunces-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
  font-style: normal;
}
@font-face {
  font-family: 'Manrope';
  src: url('./fonts/Manrope-var.woff2') format('woff2-variations');
  font-weight: 200 800;
  font-display: swap;
  font-style: normal;
}

:root {
  --indigo: #16202b;
  --indigo-2: #1e2b38;
  --bone: #e8e4da;
  --paper: #f6f4ef;
  --paper-2: #eeeae1;
  --ink: #1c1a17;
  --slate: #5a6169;
  /* Verdigris, 8% darker than the original #3f7a6b. Same hue, same family, and
     the difference is not perceptible side by side — but the original measured
     4.54:1 on --paper and only 4.16:1 on --paper-2, so every link, quiet button
     and accent word sitting on a tinted section failed AA for small text. The
     tint is where the answer comparison and the pricing grid live, which is the
     part of the page a buyer actually reads.

     Measured now: 5.18:1 on --paper, 4.75:1 on --paper-2, 5.70:1 for white text
     on a verdigris fill (was 4.99:1, so the buttons improved too).

     One token rather than a separate text variant on purpose: a fill/text split
     is a rule someone has to remember, and the darker value is correct for both. */
  --verdigris: #3a7062;
  /* On indigo. Measured 6.19:1, no change needed. */
  --verdigris-lt: #6aab98;
  /* Small verdigris text on a light ground only. Darkening the shared accent far
     enough to carry 12px type would drag the fills and the large display accent
     down with it, and the display accent has the opposite problem — it sits on
     indigo and needs to be lighter, not darker. Three tokens, one per ground and
     size class, is the honest shape of this:
       --verdigris      fills, borders, large text on light
       --verdigris-lt   anything on indigo
       --verdigris-text small text on light  (5.24:1 on paper, 4.80:1 on tint) */
  --verdigris-text: #34655a;
  --line: #ddd8ce;
  --line-dk: #2c3a49;

  /* Display carries the personality. Body is the quieter companion.
     Labels use the body face in uppercase rather than adding a third family. */
  --display: 'Fraunces', Georgia, serif;
  --body: 'Manrope', system-ui, sans-serif;
  --mono: ui-monospace, 'SF Mono', SFMono-Regular, Consolas, monospace;

  /* One spacing scale. Ad-hoc gaps are what read as assembled. */
  --s1: 0.35rem;
  --s2: 0.7rem;
  --s3: 1.15rem;
  --s4: 1.9rem;
  --s5: 3.1rem;
  --s6: 5rem;

  /* Sharp corners read editorial. Uniform 4px radius on everything is a
     documented slop tell, so only inputs and buttons get any. */
  --r-sm: 2px;
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);

  /* Four steps and nothing between them. Ad-hoc sizes are what makes a page
     read as assembled instead of designed. */
  --t-xs: 0.72rem;
  --t-sm: 0.9rem;
  --t-base: 1.0625rem;
  --t-md: clamp(1.12rem, 1.6vw, 1.28rem);
  --t-lg: clamp(1.5rem, 3.2vw, 2.15rem);
  --t-xl: clamp(2.1rem, 5.6vw, 3.7rem);

  /* Secondary text is the ink at reduced strength, not a separate grey.
     Grey-on-color is the fastest way to look cheap. */
  --ink-2: #43413c;
  /* Three greys on dark ground, scaled BY SIZE not by whim. These were seven
     hardcoded hexes and they ran backwards — the smallest text on the page was
     the dimmest, when small type needs more contrast, not less. */
  --on-dark: #cfd6dd;      /* small type: notes, captions */
  --on-dark-2: #b9c2cb;    /* body and lede */
  --on-dark-3: #93a0ac;    /* footer chrome only */
  --on-dark-line: #3d4b5a; /* borders on indigo */

  --measure: 58ch;
  --pad: clamp(1.15rem, 4vw, 2.75rem);

  /* Height of the floating nav pill, and the gutter above and below it. Both are
     needed in two places — the pill's own min-height and the negative margin that
     pulls the hero up underneath it — so they live here rather than as a number
     written twice. If the nav links ever wrap to a second line the pill grows past
     this and the hero offset is wrong, so keep the link set short enough to fit
     one line at 375px. */
  --nav-h: 66px;
  --nav-gutter: 0.7rem;
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font: 1rem/1.7 var(--body);
  font-optical-sizing: auto;
  font-variant-numeric: lining-nums;
  letter-spacing: -0.006em;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 {
  font-family: var(--display);
  font-weight: 700;
  text-wrap: balance;
  margin: 0;
  /* SOFT rounds the terminals slightly, WONK enables the angled variants.
     Both are what give Fraunces its warmth instead of looking like a
     generic high-contrast serif. */
  font-variation-settings: 'SOFT' 25, 'WONK' 1;
  font-optical-sizing: auto;
}
h1 { font-size: var(--t-xl); line-height: 1.04; letter-spacing: -0.021em; }
h2 { font-size: var(--t-lg); line-height: 1.12; letter-spacing: -0.014em; }
h3 { font-size: var(--t-base); line-height: 1.35; letter-spacing: -0.004em; }
p { margin: 0; max-width: var(--measure); text-wrap: pretty; }
a { color: var(--verdigris); text-underline-offset: 3px; text-decoration-thickness: 1px; }
strong, b { font-weight: 700; }

/* Interface chrome only. */
.label, .btn, .nav, .tier__name, th, .answer__q, input, select, textarea, button {
  font-family: var(--body);
}

.label {
  font-family: var(--body);
  white-space: nowrap;
  font-size: 0.68rem;
  font-weight: 750;
  font-variant-numeric: normal;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0;
  /* Optically align the tracked caps with the flush-left text below them. */
  text-indent: -0.09em;
}
.lede { font-size: var(--t-md); line-height: 1.5; color: var(--ink-2); max-width: 46ch; }
em.mark { font-style: italic; color: var(--verdigris); }
/* The accent line in the hero sits on indigo, where the light variant is the
   correct one. It had been using the shared accent, which measured 2.88:1 against
   a 3:1 requirement for large text — on the single most important line on the
   site. Darkening the shared token for small-text contrast made that worse, which
   is what forced the three-token split above. */
.hero em.mark,
.page-hero em.mark,
.section--dark em.mark,
.plate em.mark,
.band em.mark { color: var(--verdigris-lt); }

/* ---------- nav ---------- */

/* A floating glass bar rather than a full-width band welded to the top. The bar
   is the sticky element and the glass lives on .nav__inner, so the pill has real
   edges on all four sides and the page visibly runs underneath it.

   The two-layer background is what makes it read as glass instead of as a flat
   translucent rectangle: a soft vertical wash for body, plus a hairline
   top-inset highlight for the lit edge. A single flat rgba() with a blur behind
   it reads as frosted plastic. */
.nav {
  position: sticky;
  top: 0;
  z-index: 30;
  padding: var(--nav-gutter) var(--pad);
  /* No background or border on the bar itself: the glass is the pill inside it,
     and a band here would cancel the floating effect. */
  background: transparent;
  pointer-events: none;   /* let clicks through the gutter beside the pill */
}
.nav__inner {
  width: 100%;
  max-width: 68rem;
  min-height: var(--nav-h);
  margin: 0 auto;
  padding: 0 0.55rem 0 0.85rem;
  display: flex;
  align-items: center;
  gap: 1.75rem;
  pointer-events: auto;   /* ...but the pill is still interactive */
  border-radius: 999px;
  background:
    linear-gradient(
      to bottom,
      rgba(255, 255, 255, 0.72),
      rgba(246, 244, 239, 0.58)
    );
  backdrop-filter: blur(20px) saturate(150%);
  -webkit-backdrop-filter: blur(20px) saturate(150%);
  border: 1px solid rgba(255, 255, 255, 0.6);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.7) inset,
    0 10px 30px -12px rgba(22, 32, 43, 0.28),
    0 2px 8px -4px rgba(22, 32, 43, 0.14);
  transition: box-shadow 0.25s var(--ease), background-color 0.25s var(--ease);
}
/* The bar sits deeper once the page has moved. Driven by a scroll timeline
   rather than a scroll listener, so there is no JS on any of the five pages and
   nothing to keep in sync; where scroll timelines are unsupported the bar simply
   keeps its resting shadow, which is a complete look on its own. Shadow only —
   animating size here would reflow the whole page on every scroll frame. */
@supports (animation-timeline: scroll()) {
  @keyframes nav-lift {
    to {
      box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.7) inset,
        0 16px 40px -14px rgba(22, 32, 43, 0.38),
        0 3px 10px -4px rgba(22, 32, 43, 0.18);
      border-color: rgba(255, 255, 255, 0.75);
    }
  }
  .nav__inner {
    animation: nav-lift linear both;
    animation-timeline: scroll(root block);
    animation-range: 0 140px;
  }
  @media (prefers-reduced-motion: reduce) {
    .nav__inner { animation: none; }
  }
}

/* On the home page the bar floats over the hero instead of resting above it.
   Safe here specifically because the hero's copy is bottom-aligned, so there is
   nothing at the top of the hero for the pill to cover. Every other page opens
   with a heading at the top of its first section, so those keep the bar in flow
   rather than dropping it on top of their h1. */
.nav + .hero { margin-top: calc(-1 * (var(--nav-h) + var(--nav-gutter) * 2)); }
/* Where backdrop-filter is unavailable the translucent fill would let text
   collide with whatever scrolls under it, so those browsers get an opaque pill. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .nav__inner { background: var(--paper); border-color: rgba(28, 26, 23, 0.12); }
}
.nav__brand {
  text-decoration: none; color: var(--ink);
  display: inline-flex; align-items: center; gap: 0.68rem;
  min-height: 48px;
}
/* The real wordmark asset, not type set to imitate it. The drawn letterforms are
   a blocky pixel face that no webfont on the site matches, so a text version was
   never the same mark — it was a different one that happened to say the same word.

   Source is logo-lockup-clean.png cropped at x=1055, which is where "MONSOONS"
   ends and the 47px word space before "AI" begins. The nav says "Monsoons"; the
   spoken and written brand drops the AI. See BRAND.md. */
.nav__lockup {
  /* 44px in a 66px pill. The asset is 330x120 cropped tight to the ink, so this
     is ~2.7x device pixels and the roundel lands slightly larger than the 42px
     mark it replaced, which the brand needs to hold its own next to the links. */
  height: 44px;
  width: auto;
  display: block;
}
/* Wrapper inserted by assets/logo-resolve.js so the reveal canvas can sit over
   the lockup. Sized by the lockup itself, so if the script never runs the wrapper
   never exists and the image is unaffected. */
.markfx { position: relative; display: block; line-height: 0; }
.markfx > .markfx__c {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  display: block; pointer-events: none;
}
/* The image only hides while the canvas is actually painting it. */
.markfx--running > .nav__lockup { visibility: hidden; }
.nav__links { display: flex; gap: 1rem; margin-left: auto; align-items: center; flex-wrap: wrap; }

/* ---------- mobile navigation ----------

   Below 860px the links did not fit, so they were simply hidden and the only
   reachable destination in the header was the CTA. Everything else was a 10,000px
   scroll to the footer. This is a real menu: a 44px trigger, aria-expanded and
   aria-controls, Escape and outside-click to close, and focus returned to the
   trigger on close.

   The panel is deliberately plain — a list on the same glass as the bar. The
   header is not somewhere to spend another animated surface. */
.nav__toggle { display: none; }
.nav__panel { display: contents; }

@media (max-width: 860px) {
  .nav__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    margin-left: auto;
    min-height: 44px;
    min-width: 44px;
    padding: 0 0.7rem;
    border: 1px solid rgba(63, 122, 107, 0.4);
    border-radius: 999px;
    background: rgba(63, 122, 107, 0.07);
    color: var(--verdigris-text);
    font-family: var(--body);
    font-size: 0.72rem;
    font-weight: 760;
    letter-spacing: 0.045em;
    cursor: pointer;
  }
  .nav__burger { display: block; width: 15px; height: 9px; position: relative; }
  .nav__burger::before,
  .nav__burger::after {
    content: '';
    position: absolute;
    left: 0; right: 0;
    height: 1.5px;
    background: currentColor;
    transition: transform 0.16s ease-out, opacity 0.16s ease-out;
  }
  .nav__burger::before { top: 0; }
  .nav__burger::after { bottom: 0; }
  [aria-expanded='true'] .nav__burger::before { transform: translateY(3.75px) rotate(45deg); }
  [aria-expanded='true'] .nav__burger::after { transform: translateY(-3.75px) rotate(-45deg); }

  /* The panel drops below the pill rather than covering the page, so the CTA in
     the bar stays visible and reachable while it is open. */
  .nav__panel {
    display: block;
    position: absolute;
    top: calc(100% + 0.45rem);
    left: 0; right: 0;
    padding: 0.4rem;
    border-radius: 1.25rem;
    background: linear-gradient(to bottom, rgba(255,255,255,0.93), rgba(246,244,239,0.9));
    backdrop-filter: blur(20px) saturate(150%);
    -webkit-backdrop-filter: blur(20px) saturate(150%);
    border: 1px solid rgba(255, 255, 255, 0.6);
    box-shadow: 0 18px 44px -16px rgba(22, 32, 43, 0.4);
  }
  .nav__panel[hidden] { display: none; }
  .nav__inner { position: relative; }
  .nav__links {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    margin-left: 0;
    flex-wrap: nowrap;
  }
  .nav__links a {
    min-height: 48px;
    display: flex;
    align-items: center;
    padding: 0 0.85rem;
    font-size: 0.95rem;
    border-radius: 0.9rem;
  }
  /* The animated underline is a hover affordance and there is no hover here. */
  .nav__links a::after { content: none; }
  .nav__links a:active { background: rgba(63, 122, 107, 0.09); }
  /* The CTA lives in the bar on mobile, not in the panel. */
  .nav__links .btn { display: none; }
  .nav__cta { display: inline-flex; margin-left: 0.5rem; }
}

/* Above the breakpoint the trigger and the duplicate CTA are irrelevant.
   Scoped to .nav__inner > for specificity: a bare .nav__cta ties with .btn, and
   .btn is defined later in this file, so the clone stayed visible and the desktop
   bar showed the same button twice. */
@media (min-width: 861px) {
  .nav__inner > .nav__cta { display: none; }
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .nav__panel { background: var(--paper); }
}
.nav__links a {
  font-size: 0.78rem; font-weight: 650; color: var(--ink-2); text-decoration: none;
  letter-spacing: 0.015em;
  padding: 0.5rem 0; position: relative;
  transition: color 0.18s var(--ease);
}
.nav__links a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background: var(--verdigris);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.22s var(--ease);
}
.nav__links a:hover::after, .nav__links a[aria-current='page']::after { transform: scaleX(1); }
.nav__links a:hover, .nav__links a[aria-current='page'] { color: var(--verdigris); }

/* ---------- buttons ---------- */

/* One block, and it transitions only compositor-friendly properties.
   This previously animated box-shadow, which forces the browser to re-rasterise
   a large blurred region on every frame of every hover — the usual reason a
   button feels laggy rather than snappy. The lift shadow now lives on a ::before
   layer whose opacity is faded instead, and opacity and transform are both
   handled on the compositor. */
.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.52rem;
  min-height: 48px;
  font-family: var(--body);
  font-size: 0.76rem;
  font-weight: 760;
  letter-spacing: 0.045em;
  padding: 0.82rem 1.28rem;
  border: 1px solid var(--verdigris);
  border-radius: 999px;
  background: var(--verdigris);
  color: #fff;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 9px 24px rgba(63, 122, 107, 0.18), inset 0 1px rgba(255, 255, 255, 0.16);
  /* Faster in than the old 0.18s, and a near-linear curve. The old ease had a
     long tail, which is what made the hover-out feel like it was dragging. */
  transition:
    background-color 0.13s ease-out,
    border-color 0.13s ease-out,
    color 0.13s ease-out,
    transform 0.13s ease-out;
}
.btn::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  box-shadow: 0 13px 30px rgba(63, 122, 107, 0.3);
  opacity: 0;
  transition: opacity 0.13s ease-out;
}
.btn::after {
  content: '→';
  font-size: 1.08em;
  line-height: 1;
  transition: transform 0.13s ease-out;
}
.btn:hover {
  background: #34655a;
  border-color: #34655a;
  transform: translateY(-2px);
}
.btn:hover::before { opacity: 1; }
.btn:hover::after { transform: translateX(3px); }
/* Press reads as a press: settle back down and drop the lift shadow. */
.btn:active { transform: translateY(0); transition-duration: 0.04s; }
.btn:active::before { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .btn, .btn::before, .btn::after { transition: none; }
  .btn:hover { transform: none; }
  .btn:hover::after { transform: none; }
}

/* Quiet variant. The fill was 4% alpha, which on paper is invisible and on the
   glass nav read as nothing at all, so the primary CTA in the bar did not look
   like a button. Now it carries a real border and a legible fill. */
.btn--quiet {
  background: rgba(63, 122, 107, 0.07);
  border-color: rgba(63, 122, 107, 0.4);
  /* 12px label on a light ground, and the tinted fill behind it drops the
     effective ratio further, so this needs the text token rather than the accent. */
  color: var(--verdigris-text);
  box-shadow: none;
}
.btn--quiet::before { box-shadow: 0 10px 22px rgba(63, 122, 107, 0.18); }
.btn--quiet:hover {
  background: var(--verdigris);
  border-color: var(--verdigris);
  color: #fff;
}
.btn--onDark {
  background: var(--bone);
  border-color: var(--bone);
  color: var(--indigo);
  box-shadow: 0 12px 30px rgba(0, 0, 0, 0.18), inset 0 1px rgba(255, 255, 255, 0.4);
}
.btn--onDark::before { box-shadow: 0 16px 34px rgba(0, 0, 0, 0.3); }
.btn--onDark:hover { background: #fff; border-color: #fff; color: var(--indigo); }

/* Nav buttons sit on glass and in a 66px bar, so they run tighter than a
   body-copy CTA and drop the arrow, which at this size just adds noise. */
.nav .btn {
  min-height: 40px;
  padding: 0.5rem 1rem;
  font-size: 0.72rem;
}
.nav .btn::after { content: none; }
/* One focus treatment for the whole site. The audit form had its own ring at a
   different colour and offset, so tabbing into it visibly changed the ring. */
:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--verdigris-lt);
  outline-offset: 3px;
}

/* Skip link. The sticky nav puts two to five tab stops before the h1 on every
   page, and a keyboard user re-traverses them on each navigation. */
.skip {
  position: absolute;
  left: var(--pad);
  top: 0;
  z-index: 50;
  transform: translateY(-140%);
  padding: 0.7rem 1.1rem;
  background: var(--verdigris);
  color: #fff;
  font-family: var(--body);
  font-size: var(--t-sm);
  font-weight: 600;
  text-decoration: none;
  border-radius: var(--r-sm);
  transition: transform 0.18s var(--ease);
}
.skip:focus, .skip:focus-visible { transform: translateY(0.6rem); }

/* The quiet button on indigo. Verdigris on indigo is only 3.3:1, which is why
   two pages were patching this inline — and because inline declarations beat
   author rules at any specificity, the patch also killed the hover state on a
   phone-number CTA. Rule here, patches removed there. */
.section--dark .btn--quiet,
.plate .btn--quiet,
.band .btn--quiet { color: var(--bone); border-color: var(--on-dark-line); }
.section--dark .btn--quiet:hover,
.plate .btn--quiet:hover,
.band .btn--quiet:hover {
  background: rgba(232, 228, 218, 0.1);
  border-color: var(--bone);
  color: var(--bone);
}

/* Same gap for the pull quote, which about.html was also patching inline. */
.section--dark .pull { color: var(--bone); border-color: var(--verdigris-lt); }

/* ---------- hero ---------- */

.hero {
  padding-inline: var(--pad);
  position: relative;
  min-height: min(86vh, 720px);
  display: flex; align-items: flex-end;
  background: var(--indigo);
  overflow: hidden;
}
/* Fade the dot field itself at the edges so it dissolves into the ground
   instead of ending on a hard rectangle. The scrim handles legibility; this
   handles the seam where the section meets the page. */
.hero__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  pointer-events: none;   /* static video plate */
  /* Fade all four edges, not just top and bottom. Two gradients intersected,
     so the plate dissolves into the ground on every side and there is no
     letterbox seam at any viewport ratio. */
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 12%, #000 80%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 9%, #000 91%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 12%, #000 80%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 9%, #000 91%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.hero video { display: none; }

/* ---------- dithered headings ---------- */

/* Three headings on the home page are rendered as dot fields by
   assets/dither-text.js. The real text stays in the DOM inside .dtext__t and
   only goes transparent once its canvas is confirmed painted, so a JS failure,
   a missing font, or a browser without canvas leaves a normal heading rather
   than a blank space.

   The child combinator is deliberate: `.plate canvas` at (0,1,1) would
   otherwise out-specify a plain `.dtext__c` at (0,1,0) and reposition the
   overlay if one of these ever lands inside a plate. */
.dtext { position: relative; }
/* The dot field bleeds past the line box top and bottom. Descenders render
   outside a line box by design, so a canvas clipped to that box shears the
   bottom off every 'y', 'p' and 'g'. The em-relative inset keeps the bleed
   proportional to the heading, which matters because h2 is a clamp(). */
.dtext > .dtext__bleed {
  position: absolute;
  left: 0; right: 0;
  top: -0.34em; bottom: -0.34em;
  display: block;
  pointer-events: none;
}
.dtext > .dtext__bleed > .dtext__c {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
}
.dtext--on > .dtext__t { color: transparent; }
/* Only claim the pointer once the dots are actually the visible layer. */
.dtext--on > .dtext__bleed { pointer-events: auto; }

/* ---------- entrance motion ----------

   Two mechanisms, no JavaScript in either.

   The hero runs a timed stagger on load, because that is a page-load sequence and
   there is no scroll to drive it. Section heads use a view() scroll timeline. That
   choice matters: the usual approach is an IntersectionObserver adding a class,
   which means the elements must start hidden in CSS, which means a JS error or a
   blocked script leaves the page permanently blank. Here, a browser that does not
   support scroll timelines simply shows the content — the failure mode is "no
   animation", never "no content".

   Kept to eyebrows, headings, ledes and the hero CTA. Revealing every element on
   the page is a documented generated-design tell; revealing the thing you are meant
   to read next is direction. */

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

@media not (prefers-reduced-motion: reduce) {
  /* Load sequence. `both` fill keeps each child hidden through its own delay, and
     because this is declared in the stylesheet it applies before first paint —
     nothing flashes in and then hides. */
  .hero__inner > *,
  .page-hero__inner .head > * {
    animation: rise-in 0.66s var(--ease) both;
  }
  .hero__inner > *:nth-child(1),
  .page-hero__inner .head > *:nth-child(1) { animation-delay: 0.06s; }
  .hero__inner > *:nth-child(2),
  .page-hero__inner .head > *:nth-child(2) { animation-delay: 0.17s; }
  .hero__inner > *:nth-child(3),
  .page-hero__inner .head > *:nth-child(3) { animation-delay: 0.29s; }
  .hero__inner > *:nth-child(4) { animation-delay: 0.41s; }
}

@supports (animation-timeline: view()) {
  @media not (prefers-reduced-motion: reduce) {
    /* Scroll-driven, so no delay applies — the stagger comes from giving each
       child a slightly later slice of the entry phase instead. Anything already
       on screen at load sits at progress 1 and is simply visible. */
    .section .head > *,
    .plate__inner .head > *,
    .band__inner .head > * {
      animation: rise-in linear both;
      animation-timeline: view();
      animation-range: entry 8% entry 46%;
    }
    .section .head > *:nth-child(2),
    .plate__inner .head > *:nth-child(2),
    .band__inner .head > *:nth-child(2) { animation-range: entry 14% entry 54%; }
    .section .head > *:nth-child(3),
    .plate__inner .head > *:nth-child(3),
    .band__inner .head > *:nth-child(3) { animation-range: entry 20% entry 62%; }
  }
}

.hero::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to top, rgba(22,32,43,0.9) 0%, rgba(22,32,43,0.62) 26%,
                    rgba(22,32,43,0.12) 52%, transparent 72%),
    linear-gradient(to right, rgba(22,32,43,0.72) 0%, rgba(22,32,43,0.2) 38%, transparent 60%);
}
/* One shared rail. Sections centre their content in a 68rem wrap, so the
   full-bleed hero and bands must use the same container or nothing aligns. */
.hero__inner,
.band__inner,
.plate__inner {
  position: relative;
  z-index: 2;
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  padding: 4rem 0 3.6rem;
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
}

/* Named elements, not `> *` — same reason as the note on .wrap. `.lede` is
   already 46ch and `.label` is nowrap, so only the headings and the button row
   need saying. `.hero__cta` is explicit because at 36rem the button plus its
   note wrap to two lines, and uncapped they would snap to one. */
.hero__inner h1 { max-width: 22ch; }
.band__inner h2, .plate__inner h2 { max-width: 36rem; }
.hero__cta { max-width: 36rem; }
.hero .label { color: var(--verdigris-lt); }
.hero h1 { color: var(--bone); }
.hero .lede { color: var(--on-dark-2); }
.hero__cta { display: flex; gap: 0.7rem; flex-wrap: wrap; align-items: center; margin-top: 0.4rem; }
.hero__note { font-size: var(--t-xs); color: var(--on-dark); }

/* ---------- interior page heroes ---------- */

.page-hero {
  position: relative;
  min-height: clamp(440px, 64vh, 650px);
  padding: 0 var(--pad);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  isolation: isolate;
  background-color: var(--indigo);
  background-position: center;
  background-size: cover;
}
.page-hero::before {
  content: '';
  position: absolute;
  z-index: -1;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(14, 24, 34, 0.97) 0%, rgba(14, 24, 34, 0.8) 42%, rgba(14, 24, 34, 0.2) 78%),
    linear-gradient(0deg, rgba(14, 24, 34, 0.82) 0%, transparent 58%);
}
/* Web derivatives, not the source PNGs. These are the LCP image on their pages
   and they were pointing at the 1.94MB, 2.35MB and 3.27MB originals — 7.5MB of
   hero background across three pages, behind a scrim dark enough that none of
   that detail was ever visible. The .jpg derivatives are 159KB, 277KB and 373KB.

   The PNGs stay in the repo as source. Do not point production CSS at them
   again; if a plate needs regenerating, make a new derivative. */
.page-hero--about {
  background-image: url('../media/saguaro-forest-web.jpg');
  background-position: center 48%;
}
.page-hero--audit {
  background-image: url('../media/valley-lights-web.jpg');
  background-position: center 40%;
}
.page-hero--work {
  background-image: url('../media/plaza-web.jpg');
  background-position: center 47%;
}
.page-hero__inner {
  width: 100%;
  max-width: 68rem;
  margin: 0 auto;
  padding: clamp(4.5rem, 10vw, 7rem) 0 clamp(3.2rem, 7vw, 5rem);
}
.page-hero .head { max-width: 43rem; }
.page-hero .label { color: var(--verdigris-lt); }
.page-hero h1 {
  max-width: 18ch;
  color: var(--bone);
  font-size: clamp(2.8rem, 7vw, 5.4rem);
  line-height: 0.98;
  letter-spacing: -0.045em;
}
.page-hero .lede {
  color: var(--on-dark);
  font-size: clamp(1rem, 1.7vw, 1.22rem);
  line-height: 1.6;
}

/* ---------- dithered band (second plate, mid page) ---------- */

.band {
  padding-inline: var(--pad);
  position: relative;
  display: flex;
  align-items: center;
  /* Tall enough that a 16:9 plate has real presence instead of reading as a
     letterboxed strip across a short band. */
  min-height: min(94vh, 820px);
  background: var(--indigo);
  overflow: hidden;
}
.band__canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  pointer-events: none;   /* static plate, nothing to interact with */
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 14%, #000 84%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 14%, #000 84%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.band video { display: none; }

.band::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to right, rgba(22,32,43,0.94) 0%, rgba(22,32,43,0.72) 38%,
                    rgba(22,32,43,0.15) 72%, transparent 100%),
    linear-gradient(to top, rgba(22,32,43,0.6) 0%, transparent 40%);
}
.band__inner .label { color: var(--verdigris-lt); }
.band__inner h2 { color: var(--bone); }
.band__inner .lede { color: var(--on-dark-2); }

/* ---------- sections ---------- */

.section { padding: clamp(3.25rem, 7vw, var(--s6)) var(--pad); }
/* The section after a full-bleed plate sits tighter, so the plate and the
   copy that follows it read as one thought rather than two blocks. */
.plate + .section, .band + .section, .hero + .section { padding-top: clamp(2.5rem, 5vw, 3.4rem); }
.section + .section:not(.section--tint):not(.section--dark) {
  border-top: 1px solid var(--line);
}
.section--tint { background: var(--paper-2); }
.section--dark { background: var(--indigo); color: var(--bone); }
.section--dark .label { color: var(--verdigris-lt); }
.section--dark .lede, .section--dark p { color: var(--on-dark-2); }
.section--dark h2, .section--dark h3 { color: var(--bone); }

/* One rail, and only one. Every section centres its content in the same 68rem
   wrap, so the left edge is identical from the hero to the footer. That is the
   container's job and its only job.

   The measure is NOT the container's job — it belongs to the thing being read.
   `p` is 58ch, `.lede` 46ch, `.wont li` 54ch, `.plan__desc` 48ch. A container
   rule like `.wrap--narrow > *` sits at (0,2,0) and out-specifies every one of
   those, so 46rem/736px was overriding 58ch/~493px and making body copy half
   again wider than intended — the single most recognisable amateur-web tell
   there is. A wrapper can only ever floor a measure, never cap one, so there is
   no narrow variant any more. Nothing needs one. */
.wrap { max-width: 68rem; margin: 0 auto; display: flex; flex-direction: column; gap: var(--s4); }

/* The heading group is the one block with no intrinsic measure: h1 and h2 carry
   none of their own, and a 1088px heading is not a heading. */
.head { display: flex; flex-direction: column; gap: 0.5rem; max-width: 46rem; }
.head .lede { margin-top: 0.4rem; }

/* Blocks whose internal geometry breaks before their text does. Past ~46rem the
   FAQ marker and the plan price detach from what they belong to and the row
   stops reading as a row. */
.faq, .plans, .steps, .callout, .wont { max-width: 46rem; }


/* ---------- grids ---------- */

/* Wide minimum on purpose: narrow columns of small grey text is the
   generic feature-grid look. 2x2 gives each column a readable measure. */
.cols {
  display: grid;
  gap: var(--s4) var(--s5);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
}
.col { display: flex; flex-direction: column; gap: var(--s1); }
.col h3 { font-family: var(--display); font-size: var(--t-base); font-weight: 700; }
.col p { font-size: var(--t-sm); color: var(--slate); }
.section--dark .col p { color: var(--on-dark-2); }

/* ---------- numbered steps ---------- */

.steps { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.15rem; }
.steps li { display: grid; grid-template-columns: 2.1rem 1fr; gap: 0.9rem; align-items: start; }
.steps li::before {
  content: counter(step);
  counter-increment: step;
  font-family: var(--display); font-size: 1.25rem; color: var(--verdigris);
  line-height: 1.1;
}
.steps { counter-reset: step; }
.steps h3 { margin-bottom: 0.15rem; }
.steps p { font-size: var(--t-sm); color: var(--slate); }

/* ---------- pricing ---------- */

/* 3 + 2 rather than 4 + 1: five cards on a 4-up grid orphan the last one. */
.tiers { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); }
.tier {
  display: flex; flex-direction: column; gap: 0.6rem;
  padding: 1.25rem;
  background: #fff; border: 1px solid var(--line); border-radius: 0;
}
.tier--lead { border-color: var(--verdigris); box-shadow: inset 0 2px 0 var(--verdigris); }
.tier__name { font-family: var(--body); font-size: 0.7rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--slate); }
.tier__price { font-family: var(--display); font-size: 1.85rem; line-height: 1; }
.tier__price small { font-family: var(--body); font-size: 0.78rem; color: var(--slate); font-weight: 400; }
/* Replaces the price line now that figures are quoted after the audit. */
.tier__note {
  font-family: var(--display);
  font-size: var(--t-base);
  font-style: italic;
  color: var(--verdigris);
  line-height: 1.3;
}
.tier ul { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: 0.35rem; font-size: var(--t-sm); color: var(--slate); }

/* ---------- guarantee callout ---------- */

.callout {
  border-left: 3px solid var(--verdigris-lt);
  padding: 0.3rem 0 0.3rem 1.15rem;
  display: flex; flex-direction: column; gap: 0.6rem;
}
.callout strong { color: var(--bone); }

/* ---------- won't-promise list ---------- */

.wont { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.95rem; }
.wont li {
  display: grid; grid-template-columns: 1.4rem minmax(0, 54ch);
  gap: 0.7rem; font-size: var(--t-sm); line-height: 1.62;
}
.wont li::before { content: '\2014'; color: var(--verdigris); }
.wont b { font-weight: 700; }

/* ---------- faq ---------- */

.faq { display: grid; gap: 0.1rem; }
details { border-bottom: 1px solid var(--line); }
summary {
  cursor: pointer; font-weight: 600; font-size: 0.98rem;
  list-style: none;
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  /* The padding used to sit on `details`, which meant the hit area was only the
     text's own line box: 27px tall on the primary control of this section, well
     under the 44px minimum, and the padded strip around it did nothing when
     tapped. On the control it is both the spacing and the target. */
  min-height: 52px;
  padding: 0.55rem 0;
  transition: color 0.13s ease-out;
}
summary:hover { color: var(--verdigris); }
summary::-webkit-details-marker { display: none; }
summary::after {
  content: '+';
  color: var(--verdigris); font-weight: 400; font-size: 1.3rem; line-height: 1;
  flex: none;
}
details[open] summary::after { content: '\2013'; }
/* Bottom spacing moved here with the padding, so an open answer still clears the
   rule beneath it. */
details p { margin: 0 0 1.15rem; font-size: var(--t-sm); color: var(--slate); }

/* ---------- about ---------- */

.prose { display: flex; flex-direction: column; gap: 1.15rem; }
.prose p { font-size: var(--t-base); }
/* No .prose h2 margin. Flex gaps do not collapse with margins, so a 1.2rem
   top margin summed with the 1.15rem prose gap and pushed every eyebrow
   37.6px from its heading. The .head wrapper owns that spacing now. */
.pull {
  font-family: var(--display); font-size: clamp(1.2rem, 2.6vw, 1.55rem);
  line-height: 1.32; color: var(--ink);
  border-left: 3px solid var(--verdigris); padding-left: 1.15rem;
  max-width: 34rem;
}

/* ---------- footer ---------- */

.footer {
  background: #101923;
  color: var(--on-dark-3);
  padding: clamp(3.5rem, 7vw, 5.5rem) var(--pad) 1.6rem;
  border-top: 1px solid var(--line-dk);
  font-size: 0.82rem;
}
.footer a { color: var(--bone); text-decoration: none; }
.footer a:hover { color: var(--verdigris-lt); }
.footer__inner,
.footer__bottom {
  width: 100%;
  max-width: 68rem;
  margin-inline: auto;
}
.footer__inner {
  display: grid;
  grid-template-columns: minmax(18rem, 1.7fr) minmax(9rem, 0.7fr) minmax(15rem, 1fr);
  gap: clamp(2rem, 6vw, 5rem);
}
.footer__brand { display: flex; flex-direction: column; align-items: flex-start; gap: 1rem; }
.footer__lockup {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--bone);
}
.footer__mark { width: 52px; height: 52px; object-fit: contain; }
.footer__wordmark {
  font-family: var(--display);
  font-size: 1.7rem;
  font-weight: 720;
  letter-spacing: -0.04em;
}
.footer__wordmark span {
  color: var(--verdigris-lt);
  font-family: var(--body);
  font-size: 0.52em;
  font-weight: 800;
  letter-spacing: 0.12em;
  vertical-align: 0.18em;
}
.footer__brand p { max-width: 36ch; color: var(--on-dark-2); line-height: 1.65; }
.footer__eyebrow {
  margin-bottom: 0.85rem;
  color: var(--verdigris-lt);
  font-size: 0.66rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.footer__nav { display: flex; flex-direction: column; align-items: flex-start; gap: 0.55rem; }
.footer__nav a { padding-block: 0.16rem; }
.footer__contact { display: flex; flex-direction: column; align-items: flex-start; gap: 0.65rem; }
.footer__contact strong {
  color: var(--bone);
  font-family: var(--display);
  font-size: 1.3rem;
  line-height: 1.2;
}
.footer__phone { font-size: 1rem; font-weight: 700; }
.footer .btn {
  margin-top: 0.45rem;
  color: #fff;
}
.footer__bottom {
  margin-top: clamp(3rem, 7vw, 5rem);
  padding-top: 1.2rem;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid rgba(232, 228, 218, 0.12);
  color: var(--on-dark-3);
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

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

/* ---------- still plates ---------- */

.plate {
  padding-inline: var(--pad); position: relative; overflow: hidden; background: var(--indigo); }
/* Stills accept the pointer, video plates do not. The lean pairs with the
   wave on a static image; over playing video it just fights the motion. */
.plate canvas { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.plate img { display: none; }

/* Bridge band: wide, cinematic, text on the left. */
.plate--bridge { min-height: min(80vh, 640px); display: flex; align-items: flex-end; }
.plate--bridge canvas {
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 12%, #000 86%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 12%, #000 86%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.plate--bridge::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(22,32,43,0.94) 0%, rgba(22,32,43,0.6) 34%, transparent 68%);
}
.plate__inner .label { color: var(--verdigris-lt); }
.plate__inner h2 { color: var(--bone); }
.plate__inner .lede { color: var(--on-dark-2); }



/* ---------- audit page: form beside a vertical plate ---------- */

.split {
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  grid-template-columns: minmax(30rem, 1fr) minmax(0, 20rem);
  align-items: start;
}
@media (max-width: 1180px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  .split__plate { display: none; }
}
@media (max-width: 900px) {
  .split { grid-template-columns: minmax(0, 1fr); }
  .split__plate { display: none; }   /* decorative; no reason to cost mobile bytes */
}

.split__plate {
  position: relative;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  background: var(--indigo);
}
.split__plate canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 72%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 72%, transparent 100%);
}
.split__plate img { display: none; }
.split__caption {
  position: absolute; z-index: 2; left: 1.1rem; right: 1.1rem; bottom: 1rem;
  font-family: var(--body); font-size: var(--t-xs);
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--on-dark-2);
}

/* Grove plate on the About page. Shorter than the bridge so it reads as a
   passage in the story rather than another full-height statement. */
.plate--grove { min-height: min(66vh, 520px); display: flex; align-items: flex-end; }
.plate--grove canvas {
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 14%, #000 88%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 14%, #000 88%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.plate--grove::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(22,32,43,0.93) 0%, rgba(22,32,43,0.55) 40%, transparent 76%);
}

/* Saguaro forest. Taller than the grove because the subject is vertical, and the
   scrim runs from the left rather than the bottom so the copy sits against the
   dense side of the field instead of washing out the horizon. */
.plate--saguaro { min-height: min(74vh, 580px); display: flex; align-items: flex-end; }
.plate--saguaro canvas {
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 10%, #000 90%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 6%, #000 94%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 10%, #000 90%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 6%, #000 94%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.plate--saguaro::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(to top, rgba(22,32,43,0.9) 0%, rgba(22,32,43,0.42) 46%, transparent 78%),
    linear-gradient(to right, rgba(22,32,43,0.7) 0%, rgba(22,32,43,0.15) 46%, transparent 70%);
}

/* Plaza, on the recruiting page. Shortest of the three plates: it is a break in a
   page of numbers, not a destination. */
.plate--plaza { min-height: min(62vh, 480px); display: flex; align-items: flex-end; }
.plate--plaza canvas {
  -webkit-mask-image:
    linear-gradient(to bottom, transparent 0%, #000 12%, #000 88%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  mask-image:
    linear-gradient(to bottom, transparent 0%, #000 12%, #000 88%, transparent 100%),
    linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}
.plate--plaza::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(to top, rgba(22,32,43,0.93) 0%, rgba(22,32,43,0.5) 42%, transparent 74%);
}


/* ---------- packages as editorial rows ----------
   Five cards on an auto-fit grid always orphaned the last one and mixed three
   type styles per card. Rows scale to any number of packages and give the
   price a single consistent home. */

.plans { display: grid; }
.plan {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 11rem;
  gap: 1.5rem;
  padding: 1.6rem 0;
  border-top: 1px solid var(--line);
  align-items: start;
}
.plans .plan:last-child { border-bottom: 1px solid var(--line); }
.plan__body { display: flex; flex-direction: column; gap: 0.45rem; }
.plan__name {
  font-family: var(--display);
  font-size: 1.16rem;
  font-weight: 700;
  letter-spacing: -0.008em;
}
.plan__desc { font-size: var(--t-sm); line-height: 1.6; color: var(--ink-2); max-width: 48ch; }
.plan__price {
  font-family: var(--display);
  font-size: 1.32rem;
  line-height: 1.2;
  text-align: right;
  font-variant-numeric: tabular-nums;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  max-width: none;
}
.plan__price span {
  font-family: var(--body);
  font-size: var(--t-xs);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  font-variant-numeric: normal;
}
@media (max-width: 640px) {
  .plan { grid-template-columns: minmax(0, 1fr); gap: 0.7rem; }
  .plan__price { text-align: left; flex-direction: row; align-items: baseline; gap: 0.6rem; }
}

/* ---------- packages as a ruled 3x2 grid ----------
   Opt-in modifier, not a change to .plans, because the row layout above is still
   the right answer for the pay-scale tables on work.html: those have short
   descriptions and a number per row, which is what rows are good at.

   Five cards in three columns would orphan the last row, which is the exact
   problem that made these rows originally. The core package spans two columns
   instead, so the grid fills 3x2 with no hole and the thing we actually sell is
   the largest cell on the page.

   Hairlines come from borders on the cells rather than a gap over a coloured
   background, because .plans appears on two different section grounds and a
   background here would be wrong on one of them. */
.plans--grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  border-top: 1px solid var(--line);
  border-left: 1px solid var(--line);
}
.plans--grid .plan {
  grid-template-columns: minmax(0, 1fr);
  gap: 0.55rem;
  padding: 1.35rem 1.3rem 1.2rem;
  border: 0;
  border-right: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.plans--grid .plan:last-child { border-bottom: 1px solid var(--line); }
.plans--grid .plan__body { gap: 0.4rem; }
.plans--grid .plan__desc { font-size: 0.86rem; max-width: 42ch; }
/* Price sits on the baseline of the card, so all three in a row line up however
   long the description above them runs. */
.plans--grid .plan__price {
  margin-top: auto;
  padding-top: 0.9rem;
  text-align: left;
  flex-direction: row;
  align-items: baseline;
  gap: 0.5rem;
  font-size: 1.24rem;
}
.plans--grid .plan--feature { grid-column: span 2; }
.plans--grid .plan--feature .plan__name { font-size: 1.42rem; }
.plans--grid .plan--feature .plan__desc { max-width: 56ch; font-size: var(--t-sm); }
.plans--grid .plan--feature .plan__price { font-size: 1.5rem; }

/* Two columns before one: three narrow columns break down well before the
   viewport is actually phone-sized. The feature card stops spanning at one
   column, or it would be the only full-width cell in a single-column stack. */
@media (max-width: 900px) {
  .plans--grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .plans--grid .plan--feature { grid-column: span 2; }
}
@media (max-width: 560px) {
  .plans--grid { grid-template-columns: minmax(0, 1fr); }
  .plans--grid .plan--feature { grid-column: span 1; }
}

/* Deliberately no global list-measure rule here. An earlier attempt at one left a
   dangling `.answer--after` selector with no comma, so after comment stripping the
   real selector became `.answer--after .answer__list li, .plan__desc, …` — which
   silently gave the two halves of the before/after comparison different measures
   and overrode .plan__desc. Every list already carries its own component-local
   measure (.answer__list li 40ch, .plan__desc 48ch, .wont li 54ch). Do not
   reintroduce a blanket rule; put the measure on the component. */

/* ============================================================
   Mobile. Checked at 375px, which is where most of these buyers
   actually are: on a phone, in a truck, between jobs.
   ============================================================ */

/* The three footer columns declare minimums of 18rem + 9rem + 15rem, which with
   its gaps needs about 764px — so between 721px and 900px the grid was wider than
   the viewport and the page scrolled sideways. Neither the audit nor I had checked
   768px, which is exactly where it showed. Two columns with the brand spanning,
   until the 720px rule below takes it to one. */
@media (max-width: 900px) {
  .footer__inner {
    grid-template-columns: 1fr 1fr;
    gap: 2.2rem 2.5rem;
  }
  .footer__brand { grid-column: 1 / -1; }
}

@media (max-width: 720px) {
  /* The nav was wrapping to two rows and eating 22% of the viewport before the
     hero even started, and the fix at the time was to hide every link except the
     CTA. That left the header with exactly one destination and put everything
     else behind a scroll of up to 10,400px to the footer.
     Those links now live in the menu panel defined near the top of this file, so
     the rules that hid them are gone. Do not reintroduce
     `.nav__links a:not(.btn) { display: none }` — it silently emptied the menu,
     because it sits later in the file than the panel rules and wins. */
  .nav { padding-inline: var(--pad); }
  .nav__inner { min-height: 66px; gap: 0.75rem; }

  /* 44px minimum tap target. Anything smaller is a coin flip with a thumb. */
  .btn { min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }

  /* Hero was pushing its text 58% down the screen. */
  .hero { min-height: min(78vh, 620px); }
  .hero__inner { padding-top: 2.25rem; padding-bottom: 2.25rem; }
  .band__inner, .plate__inner { padding-top: 2.5rem; padding-bottom: 2.5rem; }

  /* Tracked caps at full width had nowhere to break. Let them wrap and ease
     the tracking so they never run off the edge on a narrow phone. */
  .label { white-space: normal; letter-spacing: 0.12em; }

  /* Tighter display sizes: the desktop clamp minimum is still large at 375px. */
  h1 { font-size: clamp(1.95rem, 9vw, 2.6rem); letter-spacing: -0.018em; }
  h2 { font-size: clamp(1.4rem, 6.4vw, 1.8rem); }
  .lede { font-size: 1.05rem; }

  /* Plates get shorter so they punctuate rather than dominate the scroll. */
  .band { min-height: min(62vh, 480px); }
  .plate--bridge { min-height: min(58vh, 440px); }
  .plate--grove { min-height: min(52vh, 380px); }
  .plate--saguaro { min-height: min(58vh, 420px); }
  .plate--plaza { min-height: min(50vh, 360px); }

  .page-hero { min-height: 500px; }
  .page-hero::before {
    background:
      linear-gradient(90deg, rgba(14, 24, 34, 0.94), rgba(14, 24, 34, 0.58)),
      linear-gradient(0deg, rgba(14, 24, 34, 0.92) 0%, rgba(14, 24, 34, 0.16) 75%);
  }
  .page-hero__inner { padding: 5rem 0 3rem; }
  .page-hero h1 { font-size: clamp(2.45rem, 13vw, 3.65rem); }

  .footer__inner { grid-template-columns: 1fr; gap: 2.2rem; }
  .footer__bottom { align-items: flex-start; flex-direction: column; }

  /* Footer links are navigation, not prose, so they get real tap area.
     Inline links inside a sentence stay as they are: they cannot be 44px
     without breaking the line, and the tap-target minimum exempts them. */
  .footer a {
    display: inline-block;
    padding: 0.55rem 0;
    min-height: 44px;
    line-height: 1.9;
  }
  .footer__nav a,
  .footer__phone { min-height: 44px; display: inline-flex; align-items: center; }
}

@media (max-width: 420px) {
  .nav__mark { width: 36px; height: 36px; }
  .nav__wordmark { font-size: 1.15rem; }
  .nav__brand { min-height: 44px; }
}

/* ============================================================
   The answer pair.

   These were plain bordered white boxes, which is the look this whole site is
   trying not to be. They now read as what they represent: an answer surface.
   No outline on the "before" at all, just paper with a hairline rule and the
   query set as real typed input. The "after" gets the weight, because that is
   the state we are selling.
   ============================================================ */

.ba {
  display: grid;
  gap: 0;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 21rem), 1fr));
  align-items: stretch;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}

.answer {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  padding: var(--s4) var(--s4) var(--s4) 0;
  max-width: none;
  background: none;
  border: 0;
  box-shadow: none;
}
/* Divider between the two states, not a box around each. */
.ba .answer + .answer {
  padding-left: var(--s4);
  border-left: 1px solid var(--line);
}

.answer__state {
  font-family: var(--body);
  font-size: var(--t-xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--slate);
}

/* The query is literal typed input, so it looks typed. */
.answer__q {
  font-family: var(--mono);
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--ink);
  padding-left: 1.15rem;
  position: relative;
  max-width: none;
}
.answer__q::before {
  content: '›';
  position: absolute;
  left: 0;
  color: var(--verdigris);
  font-family: var(--body);
  font-size: 1.05rem;
  line-height: 1.2;
}

.answer__body { display: flex; flex-direction: column; gap: var(--s2); font-size: var(--t-sm); }
.answer__body p { color: var(--ink-2); max-width: 40ch; }

/* Ranked names as a real list with figures, not bullets in a card. */
.answer__list {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: ans;
  display: flex;
  flex-direction: column;
  /* The measure lives on the list, not the items. `ch` is font-relative, so a
     bold row resolves wider than its siblings and its bottom rule overhangs
     them — visible as a ragged edge in a component built on hairlines. */
  max-width: 40ch;
}
.answer__list li {
  counter-increment: ans;
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--line);
  font-size: var(--t-sm);
}
.answer__list li:last-child { border-bottom: 0; }
.answer__list li::before {
  content: counter(ans);
  font-family: var(--display);
  font-variant-numeric: lining-nums;
  color: var(--slate);
  font-size: var(--t-sm);
}

/* The one line that carries the whole section. Small bold text on the tinted
   after-panel, so it takes the text token: the accent measured 4.31:1 here. */
.answer__list .is-you {
  font-weight: 700;
  color: var(--verdigris-text);
}
.answer__list .is-you::before {
  color: var(--verdigris-text);
  font-weight: 700;
}

.answer__you {
  font-size: var(--t-xs);
  line-height: 1.6;
  color: var(--slate);
  font-style: italic;
  max-width: 34ch;
  margin-top: auto;          /* pin to the bottom so both columns align */
  padding-top: var(--s2);
}

/* The state we are selling gets the weight: warmer ground, a verdigris rule
   above it, and its label in the accent. */
.answer--after {
  background: var(--verdigris-tint, rgba(63, 122, 107, 0.045));
  box-shadow: inset 0 2px 0 var(--verdigris);
  padding-left: var(--s4);
  padding-right: var(--s4);
}
/* Tracked 11.5px caps on the tinted panel — the smallest verdigris text here. */
.answer--after .answer__state { color: var(--verdigris-text); }
.answer--after .answer__you { color: var(--ink-2); font-style: normal; }

.ba__note {
  font-size: var(--t-xs);
  color: var(--slate);
  font-style: italic;
  max-width: 46ch;
}

@media (max-width: 700px) {
  .answer { padding: var(--s3) 0; }
  .ba .answer + .answer {
    padding-left: var(--s3);
    padding-right: var(--s3);
    border-left: 0;
    border-top: 1px solid var(--line);
  }
  .answer__you { margin-top: 0; }
}
