/* ============================================================================
   DoorYard — marketing site design system.

   Token VALUES here are mirrored from the product's canonical source of truth:
   C:\repos\dooryard\docs\BRAND.md, as resolved in apps/web/app/globals.css.
   If BRAND.md changes, this file is the only place to change on this site.

   PALETTE DIVERGENCE (2026-08-27). This site no longer uses BRAND.md's cobalt
   and amber. It runs a green / orange / teal palette derived from the isometric
   neighborhood illustration now in the hero: terracotta roofs (#f76c39), a deep
   teal ground (#024f6b) and olive foliage. That is a deliberate divergence from
   the product app, decided by the owner — not drift. If the two are to be
   reconciled, BRAND.md is the thing to change, and then this file follows it.

   Two brand rules that drive most of what follows:
     - Orange (#F76C39) means "take action". It is never a warning color. It
       replaces amber in that role and, like amber, it is light enough to need
       DARK text on top (5.55:1) rather than white (2.93:1 — would fail).
     - Elevation is 1px borders, not shadows. No glassmorphism, no blur.
   ========================================================================= */

/* ---------------------------------------------------------------- tokens --
   Every color on this site is defined ONCE below: a light value and a matching
   `--d-*` dark twin. The two blocks that follow do nothing but remap
   `--x: var(--d-x)`, so no color is ever written twice and the themes cannot
   drift apart. Adding a token means adding its --d- twin and one remap line to
   BOTH blocks.

   Why two remap blocks instead of one media query: the theme is switchable.
   `data-theme` on <html> wins in both directions; with no attribute set the
   system preference decides. See .theme-toggle and app.js.
   ------------------------------------------------------------------------- */
:root {
  /* --- brand (petrol teal) --- */
  --brand: #123f40;
  --brand-dark: #0a2627;
  --brand-hover: #0e3233;
  --brand-pressed: #0a2627;

  /* --- action (coral orange = do the thing) --- */
  --action: #e8734a;
  --action-hover: #e46c41;
  --action-pressed: #e0663a;
  /* Dark, not white. White on --action measures 3.01:1 and fails; this is
     5.41:1, and every step of the ramp above stays over 4.5:1. A darker,
     "truer" orange does not, and would force white text instead. */
  --on-action: #172033;

  /* --- surfaces --- */
  /* Light gray, faintly cooled toward the brand teal so it does not read as a
     dead neutral next to it. White cards now actually lift off the page — the
     old warm white sat 1.05:1 from --card, which is nothing. */
  --page: #eef0f0;
  --card: #ffffff;
  --surface-brand: #e6efef;
  --surface-sunk: #e3e8e8;

  /* --- text --- */
  --text: #13292a; /* teal-black, never pure black */
  --text-2: #4a5f60;
  /* Darkened from #5f7374 when the page went gray. Muted is the tightest value
     in the light palette and it carries running text — captions, the hero note,
     the footer. On the old near-white page it measured 4.79:1; on this gray it
     would have been 4.38:1, and 4.05:1 on a sunk section. Now 5.53:1 / 5.11:1. */
  --text-muted: #516364;
  --on-brand: #ffffff;

  /* The CTA band must stay a dark surface in BOTH themes, so it cannot use
     --brand-dark, which inverts to a light tint under dark mode. */
  --cta-surface: #0e3233;
  --cta-text: #ffffff;
  --cta-text-2: #bcd8d6;

  /* --- status --- */
  --success: #168a62;
  --error: #d94141;
  --success-surface: #e4f5ee;
  --warning-surface: #fdeae1; /* orange tint */
  /* Text on the two tint surfaces. Darker than the obvious pick, because the
     ramp value that reads as the tint color fails AA on its own tint at 13px.
     These measure 5.47:1 and 5.38:1 light, 6.98:1 and 5.40:1 dark. */
  --on-warning-surface: #a04220;
  --on-success-surface: #11704f;

  /* --- lines --- */
  /* Darkened one step with the page. Elevation here is 1px borders rather than
     shadows, so the border has to keep reading once the page stops being almost
     white: #e2eaea measured 1.01:1 against a sunk section, which is invisible. */
  --border: #d8e1e1;
  --border-strong: #c5d3d3;

  /* The hero illustration's own ground, used as the <img> decode-time
     background so there is no white flash. Not a UI color. */
  --hero-ground: #024f6b;

  /* ===== dark twins, same order. Never referenced directly. ===== */
  --d-brand: #7fd4cf;
  --d-brand-dark: #b8e8e4;
  --d-brand-hover: #9ae0dc;
  --d-brand-pressed: #7fd4cf;

  --d-action: #e8734a;
  --d-action-hover: #ef8461;
  --d-action-pressed: #e46c41;
  --d-on-action: #172033;

  --d-page: #0e3233;
  --d-card: #123f40;
  --d-surface-brand: #16494a;
  --d-surface-sunk: #113a3b;

  --d-text: #f2f7f6;
  --d-text-2: #b7cbc9;
  --d-text-muted: #93aaa8;
  --d-on-brand: #0e3233;

  --d-cta-surface: #164e4f;
  --d-cta-text: #ffffff;
  --d-cta-text-2: #bcd8d6;

  --d-success: #39b985;
  --d-error: #f06464;
  --d-success-surface: #123528;
  --d-warning-surface: #3d2317;
  --d-on-warning-surface: #f0a184;
  --d-on-success-surface: #39b985;

  --d-border: #1e5657;
  --d-border-strong: #2d6b6c;

  --d-hero-ground: #024f6b;

  /* --- shape --- */
  --r-chip: 8px;
  --r-control: 12px;
  --r-card: 16px;
  --r-feature: 18px;
  --r-sheet: 24px;
  --shadow-card: 0 1px 2px rgba(19, 41, 42, 0.05), 0 6px 18px rgba(19, 41, 42, 0.07);
  --d-shadow-card: 0 1px 2px rgba(0, 0, 0, 0.3), 0 6px 18px rgba(0, 0, 0, 0.3);

  /* --- 8-point spacing --- */
  --s1: 4px;
  --s2: 8px;
  --s3: 12px;
  --s4: 16px;
  --s5: 20px;
  --s6: 24px;
  --s8: 32px;
  --s10: 40px;
  --s12: 48px;
  --s16: 64px;
  --s20: 80px;

  /* --- type --- */
  --font-body: "Inter", -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto,
    Helvetica, Arial, sans-serif;
  /* Manrope is permitted on marketing headlines only, never inside the app. */
  --font-display: "Manrope", "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --content: 1120px;
  --measure: 68ch;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* Illustration-only tints for the archived inline-SVG hero in heroes/.
     Unused on the page; kept so restoring that hero stays a paste. */
  --scene-sky: #eef3ff;
  --scene-ground: #e8efe6;
  --scene-road: #dfe3ea;
  --scene-roof: #c9d0dd;
  --scene-wall: #ffffff;
  --scene-wall-2: #f2f4f8;
  --scene-tree: #cfe0cb;
  --scene-line: #c9d0dd;

  color-scheme: light;
}

/* The remap, twice. Identical bodies on purpose: one for "the system says dark
   and the visitor has not overridden to light", one for "the visitor chose
   dark". Edit one, edit the other. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --brand: var(--d-brand);
    --brand-dark: var(--d-brand-dark);
    --brand-hover: var(--d-brand-hover);
    --brand-pressed: var(--d-brand-pressed);
    --action: var(--d-action);
    --action-hover: var(--d-action-hover);
    --action-pressed: var(--d-action-pressed);
    --on-action: var(--d-on-action);
    --page: var(--d-page);
    --card: var(--d-card);
    --surface-brand: var(--d-surface-brand);
    --surface-sunk: var(--d-surface-sunk);
    --text: var(--d-text);
    --text-2: var(--d-text-2);
    --text-muted: var(--d-text-muted);
    --on-brand: var(--d-on-brand);
    --cta-surface: var(--d-cta-surface);
    --cta-text: var(--d-cta-text);
    --cta-text-2: var(--d-cta-text-2);
    --success: var(--d-success);
    --error: var(--d-error);
    --success-surface: var(--d-success-surface);
    --warning-surface: var(--d-warning-surface);
    --on-warning-surface: var(--d-on-warning-surface);
    --on-success-surface: var(--d-on-success-surface);
    --border: var(--d-border);
    --border-strong: var(--d-border-strong);
    --hero-ground: var(--d-hero-ground);
    --shadow-card: var(--d-shadow-card);
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --brand: var(--d-brand);
  --brand-dark: var(--d-brand-dark);
  --brand-hover: var(--d-brand-hover);
  --brand-pressed: var(--d-brand-pressed);
  --action: var(--d-action);
  --action-hover: var(--d-action-hover);
  --action-pressed: var(--d-action-pressed);
  --on-action: var(--d-on-action);
  --page: var(--d-page);
  --card: var(--d-card);
  --surface-brand: var(--d-surface-brand);
  --surface-sunk: var(--d-surface-sunk);
  --text: var(--d-text);
  --text-2: var(--d-text-2);
  --text-muted: var(--d-text-muted);
  --on-brand: var(--d-on-brand);
  --cta-surface: var(--d-cta-surface);
  --cta-text: var(--d-cta-text);
  --cta-text-2: var(--d-cta-text-2);
  --success: var(--d-success);
  --error: var(--d-error);
  --success-surface: var(--d-success-surface);
  --warning-surface: var(--d-warning-surface);
  --on-warning-surface: var(--d-on-warning-surface);
  --on-success-surface: var(--d-on-success-surface);
  --border: var(--d-border);
  --border-strong: var(--d-border-strong);
  --hero-ground: var(--d-hero-ground);
  --shadow-card: var(--d-shadow-card);
  color-scheme: dark;
}

/* ---------------------------------------------------------------- reset -- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html {
  -webkit-text-size-adjust: none;
  text-size-adjust: none;
  scroll-behavior: smooth;
  scroll-padding-top: 88px;
}

body {
  min-height: 100dvh;
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.6;
  color: var(--text);
  background: var(--page);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

a {
  color: var(--brand);
  text-underline-offset: 3px;
}
a:hover {
  color: var(--brand-hover);
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-display);
  color: var(--text);
  line-height: 1.15;
  letter-spacing: -0.015em;
  font-weight: 700;
  text-wrap: balance;
}

p,
li {
  text-wrap: pretty;
}

ul[role="list"] {
  list-style: none;
}

:focus-visible {
  outline: 3px solid var(--brand);
  outline-offset: 3px;
  border-radius: var(--r-chip);
}

::selection {
  background: rgba(232, 115, 74, 0.32); /* --action at low alpha */
  color: var(--text);
}

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  left: -9999px;
}
.skip-link:focus {
  position: fixed;
  top: var(--s4);
  left: var(--s4);
  z-index: 100;
  width: auto;
  height: auto;
  padding: var(--s3) var(--s5);
  background: var(--action);
  color: var(--on-action);
  font-weight: 600;
  border-radius: var(--r-control);
}

/* -------------------------------------------------------------- layout -- */
.wrap {
  width: 100%;
  max-width: var(--content);
  margin-inline: auto;
  padding-inline: var(--s6);
}

.section {
  padding-block: clamp(var(--s16), 9vw, 112px);
}

.section--sunk {
  background: var(--surface-sunk);
  border-block: 1px solid var(--border);
}

.section-label {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--brand);
  margin-bottom: var(--s4);
}

.section-title {
  font-size: clamp(28px, 3.6vw, 42px);
  margin-bottom: var(--s4);
  max-width: 20ch;
}

.section-intro {
  font-size: clamp(17px, 1.4vw, 19px);
  color: var(--text-2);
  max-width: var(--measure);
  margin-bottom: var(--s10);
}

.lede {
  color: var(--text-2);
  max-width: var(--measure);
}

/* -------------------------------------------------------------- header -- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--page) 88%, transparent);
  border-bottom: 1px solid var(--border);
  backdrop-filter: saturate(1.4) blur(8px);
}

.site-header .wrap {
  display: flex;
  align-items: center;
  gap: var(--s5);
  min-height: 72px;
}

.brand-lockup {
  display: inline-flex;
  align-items: center;
  gap: var(--s3);
  text-decoration: none;
  color: var(--text);
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 20px;
  letter-spacing: -0.02em;
  margin-right: auto;
}
.brand-lockup img {
  width: 34px;
  height: 34px;
  border-radius: 9px;
}

/* Nav, mobile first: a <details> disclosure, so it needs no JS at all. The
   panel is a card anchored to the toggle. At 900px the <details> becomes
   display:contents — its box and its disclosure semantics drop out, and the
   links become direct flex children of the header, sitting inline. */
.nav-disclosure {
  position: relative;
}

.nav-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  min-height: 44px;
  padding: var(--s2) var(--s4);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-control);
  color: var(--text);
  font-size: 15px;
  font-weight: 600;
  cursor: pointer;
  list-style: none; /* Firefox marker */
}
.nav-toggle::-webkit-details-marker {
  display: none; /* Safari marker */
}
.nav-toggle svg {
  width: 20px;
  height: 20px;
}
.nav-toggle:hover {
  border-color: var(--brand);
  color: var(--brand);
}

.site-nav {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  position: absolute;
  right: 0;
  top: calc(100% + var(--s3));
  z-index: 60;
  min-width: 250px;
  padding: var(--s3);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
}
.site-nav a {
  color: var(--text-2);
  text-decoration: none;
  font-size: 16px;
  font-weight: 500;
}
.site-nav a:hover {
  color: var(--text);
}

/* Panel links get a real tap target; the inline desktop nav does not need one. */
.site-nav > a:not(.nav-cta) {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: 0 var(--s3);
  border-radius: var(--r-chip);
}
.site-nav > a:not(.nav-cta):hover {
  background: var(--surface-sunk);
}

.site-nav .nav-cta {
  margin-top: var(--s2);
}

/* The header CTA and the panel CTA are the same action; only one shows at a
   time. Below 560px the header has no room for it, so the panel carries it.
   Both selectors are two-class on purpose — `.btn` sets display below this
   block and would otherwise win on source order. */
.site-header .header-cta {
  display: none;
}

@media (min-width: 560px) {
  .site-header .header-cta {
    display: inline-flex;
  }
  .site-nav .nav-cta {
    display: none;
  }
}

@media (min-width: 900px) {
  .nav-disclosure {
    display: contents;
  }
  .nav-toggle {
    display: none;
  }

  /* Desktop shows the nav whether or not the <details> happens to be open, and
     browsers hide closed-details content two different ways. Older engines put
     display:none on the non-summary children — the `display:flex` on .site-nav
     below overrides that. Newer engines (Chrome 128+) use a ::details-content
     pseudo-element with content-visibility:hidden, which no rule on the child
     can defeat, so it is unset here too. display:contents on the pseudo keeps
     the links as direct flex children of the header rather than boxed inside a
     wrapper. Engines that don't know the pseudo ignore this block harmlessly. */
  .nav-disclosure::details-content {
    content-visibility: visible;
    display: contents;
  }

  .site-nav {
    position: static;
    flex-direction: row;
    align-items: center;
    gap: var(--s6);
    min-width: 0;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
  }
  .site-nav a {
    font-size: 15px;
  }
  .site-nav > a:not(.nav-cta) {
    min-height: 0;
    padding: 0;
  }
  .site-nav > a:not(.nav-cta):hover {
    background: none;
  }
}

/* --------------------------------------------------------- theme toggle -- */
/* Only rendered when JS is present — `html.js` is set inline in <head>. Without
   it the control would be inert, and an inert control is worse than none: the
   page still themes itself correctly from prefers-color-scheme. */
.theme-toggle {
  display: none;
}

.js .theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 1px solid var(--border-strong);
  border-radius: var(--r-control);
  color: var(--text-2);
  cursor: pointer;
  transition: border-color 180ms var(--ease), color 180ms var(--ease);
}
.theme-toggle:hover {
  border-color: var(--brand);
  color: var(--brand);
}
.theme-toggle svg {
  width: 20px;
  height: 20px;
}

/* Which glyph shows is pure CSS, so it is correct on first paint in every one
   of the three states: explicit dark, explicit light, and system default.
   The button shows where you are going, not where you are. */
.theme-toggle .icon-sun {
  display: none;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-toggle .icon-moon {
    display: none;
  }
  :root:not([data-theme="light"]) .theme-toggle .icon-sun {
    display: block;
  }
}
:root[data-theme="dark"] .theme-toggle .icon-moon {
  display: none;
}
:root[data-theme="dark"] .theme-toggle .icon-sun {
  display: block;
}
:root[data-theme="light"] .theme-toggle .icon-moon {
  display: block;
}
:root[data-theme="light"] .theme-toggle .icon-sun {
  display: none;
}

/* ------------------------------------------------------------- buttons -- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  min-height: 56px; /* homeowner-primary floor from BRAND.md */
  padding: var(--s3) var(--s6);
  border-radius: var(--r-control);
  border: 1px solid transparent;
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background 180ms var(--ease), border-color 180ms var(--ease),
    color 180ms var(--ease), transform 180ms var(--ease);
}
.btn:active {
  transform: translateY(1px);
}

.btn--action {
  background: var(--action);
  color: var(--on-action);
}
.btn--action:hover {
  background: var(--action-hover);
  color: var(--on-action);
}
.btn--action:active {
  background: var(--action-pressed);
}

.btn--brand {
  background: var(--brand);
  color: var(--on-brand);
}
.btn--brand:hover {
  background: var(--brand-hover);
  color: var(--on-brand);
}

.btn--quiet {
  background: var(--card);
  color: var(--text);
  border-color: var(--border-strong);
}
.btn--quiet:hover {
  border-color: var(--brand);
  color: var(--brand);
}

.btn--sm {
  min-height: 44px;
  padding: var(--s2) var(--s5);
  font-size: 15px;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s3);
}

/* ---------------------------------------------------------------- hero -- */
.hero {
  padding-top: clamp(var(--s12), 6vw, var(--s20));
  padding-bottom: clamp(var(--s12), 6vw, var(--s16));
}

.hero-grid {
  display: grid;
  gap: clamp(var(--s10), 5vw, var(--s16));
  align-items: center;
}
@media (min-width: 1000px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  }
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--s2);
  padding: 6px var(--s3);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--card);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-2);
  letter-spacing: 0.01em;
  margin-bottom: var(--s5);
}
.badge .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--action);
  flex: none;
}

.hero h1 {
  font-size: clamp(34px, 5.2vw, 58px);
  letter-spacing: -0.025em;
  margin-bottom: var(--s5);
  max-width: 16ch;
}

.hero-sub {
  font-size: clamp(18px, 1.6vw, 21px);
  color: var(--text-2);
  max-width: 52ch;
  margin-bottom: var(--s8);
}

/* The audience line — owners, renters, managers, family. Sits between the CTAs
   and the launch-state note, and carries more weight than the note. */
.hero-support {
  margin-top: var(--s6);
  font-size: 17px;
  font-weight: 600;
  color: var(--text);
  max-width: 48ch;
}

.hero-note {
  margin-top: var(--s4);
  font-size: 14px;
  color: var(--text-muted);
  max-width: 46ch;
}

.hero-figure {
  margin: 0;
}
.hero-figure figcaption {
  margin-top: var(--s3);
  font-size: 13px;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}

/* The hero illustration. Carries the same rounded-sheet silhouette the inline
   SVG scene used, so swapping the art doesn't change the hero's shape. The
   teal background matches the illustration's own ground, so there is no white
   flash while the WebP decodes, and width/height on the <img> reserve the box
   so nothing shifts. */
.hero-art {
  width: 100%;
  height: auto;
  border: 1px solid var(--border);
  border-radius: var(--r-sheet);
  background: var(--hero-ground);
}

/* .scene / .pin below are unused on the page right now — the inline SVG hero
   they style lives in heroes/hero-block-waking-up.html. Kept so restoring it is
   a paste, not a rebuild. Same for the --scene-* tokens. */
.scene {
  border: 1px solid var(--border);
  border-radius: var(--r-sheet);
  background: var(--scene-sky);
  overflow: hidden;
}
.scene svg {
  width: 100%;
  height: auto;
}

/* Pins bloom one house at a time — the offer moving down the block.
   The animated element is always an INNER <g>: a CSS transform replaces an
   element's SVG transform attribute outright, so animating the positioned
   <g> would yank every pin back to the origin. Outer g positions, inner g
   animates. transform-box makes `center bottom` resolve against the pin's
   own box, so it scales from its tip. */
.pin {
  transform-box: fill-box;
  transform-origin: center bottom;
  animation: pin-bloom 6s var(--ease) infinite;
  animation-delay: calc(var(--i) * 0.45s);
  opacity: 0;
}

@keyframes pin-bloom {
  0% {
    opacity: 0;
    transform: translateY(10px) scale(0.6);
  }
  8%,
  62% {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  74%,
  100% {
    opacity: 0;
    transform: translateY(-6px) scale(0.85);
  }
}

@media (prefers-reduced-motion: reduce) {
  .service-tile,
  .service-tile:hover,
  .service-tile:active {
    transform: none;
  }
  html {
    scroll-behavior: auto;
  }
  .pin {
    animation: none;
    opacity: 1;
  }
  .js .reveal {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* --------------------------------------------------------------- cards -- */
.grid {
  display: grid;
  gap: var(--s5);
}
.grid--2 {
  grid-template-columns: 1fr;
}
.grid--3 {
  grid-template-columns: 1fr;
}
@media (min-width: 760px) {
  .grid--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .grid--3 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 1040px) {
  .grid--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--s6);
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.card h3 {
  font-size: 20px;
  letter-spacing: -0.01em;
}
.card p {
  color: var(--text-2);
  font-size: 16px;
}
.card--feature {
  border-radius: var(--r-feature);
  box-shadow: var(--shadow-card);
  padding: var(--s8);
  gap: var(--s4);
}

.card-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--r-chip);
  background: var(--surface-brand);
  color: var(--brand);
  display: grid;
  place-items: center;
  flex: none;
}
.card-icon svg {
  width: 22px;
  height: 22px;
}

.step-num {
  font-family: var(--font-display);
  font-size: 14px;
  font-weight: 800;
  letter-spacing: 0.06em;
  color: var(--brand);
}

/* Numbers are the product — tabular everywhere.
   Selector is doubled up because `.card p` (0,1,1) would otherwise beat a bare
   `.stat` (0,1,0) and render the headline figure at body size. */
.card .stat,
.stat {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
  font-size: clamp(30px, 3.4vw, 40px);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--text);
}
.card .stat-label,
.stat-label {
  color: var(--text-2);
  font-size: 15px;
  margin-top: var(--s2);
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s2);
  margin-top: var(--s2);
}
/* ------------------------------------------------------- service grid -- */
/* Twelve categories, two-up on the narrowest phone and four-up on desktop.
   Each tile is a real link, not decoration — so the whole tile is the target
   and it comfortably clears the 44px floor at every width. */
.service-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s3);
}

@media (min-width: 620px) {
  .service-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s4);
  }
}

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

.service-tile {
  display: flex;
  flex-direction: column;
  gap: var(--s3);
  min-height: 108px;
  padding: var(--s4);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  color: var(--text);
  text-decoration: none;
  font-size: 16px;
  font-weight: 600;
  line-height: 1.3;
  transition: border-color 180ms var(--ease), background 180ms var(--ease),
    transform 180ms var(--ease);
}
.service-tile:hover {
  border-color: var(--brand);
  background: var(--surface-brand);
  color: var(--text);
  transform: translateY(-2px);
}
.service-tile:active {
  transform: translateY(0);
}

.service-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--r-chip);
  background: var(--surface-brand);
  color: var(--brand);
  display: grid;
  place-items: center;
  flex: none;
}
.service-icon svg {
  width: 22px;
  height: 22px;
}
/* On hover the tile takes the icon's tint, so the chip needs to separate again. */
.service-tile:hover .service-icon {
  background: var(--card);
}

.chip {
  padding: 6px var(--s3);
  border: 1px solid var(--border);
  border-radius: var(--r-chip);
  background: var(--card);
  font-size: 14px;
  font-weight: 500;
  color: var(--text-2);
}

.ticks {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.ticks li {
  position: relative;
  padding-left: 30px;
  color: var(--text-2);
  font-size: 16px;
}
.ticks li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--surface-brand);
  border: 1px solid var(--brand);
}
.ticks li::after {
  content: "";
  position: absolute;
  left: 5px;
  top: 11px;
  width: 8px;
  height: 4px;
  border-left: 2px solid var(--brand);
  border-bottom: 2px solid var(--brand);
  transform: rotate(-45deg);
}

/* --------------------------------------------------- offer illustration -- */
.offer-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-feature);
  box-shadow: var(--shadow-card);
  padding: var(--s5);
  max-width: 380px;
}
.offer-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  margin-bottom: var(--s3);
}
.offer-head img {
  width: 36px;
  height: 36px;
  border-radius: 9px;
}
.offer-app {
  font-size: 13px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: 0.02em;
}
.offer-title {
  font-family: var(--font-display);
  font-size: 19px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--s1);
}
.offer-body {
  color: var(--text-2);
  font-size: 15px;
  margin-bottom: var(--s4);
}
.offer-actions {
  display: flex;
  gap: var(--s2);
}
.offer-actions .btn {
  flex: 1;
}

/* ---------------------------------------------------- two mechanics -- */
/* Nearby Offers and Quote Opportunities, side by side. They are deliberately
   the same weight — neither is the "real" DoorYard. */
.mech-grid {
  display: grid;
  gap: var(--s6);
}

@media (min-width: 860px) {
  .mech-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--s8);
  }
}

.mech-panel {
  display: flex;
  flex-direction: column;
  gap: var(--s4);
  padding: var(--s6);
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-feature);
}

.mech-label {
  display: inline-flex;
  align-self: flex-start;
  padding: 5px var(--s3);
  border-radius: var(--r-chip);
  background: var(--surface-brand);
  color: var(--brand);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

.mech-panel h3 {
  font-family: var(--font-display);
  font-size: clamp(20px, 2vw, 24px);
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.25;
}

.mech-body {
  color: var(--text-2);
  font-size: 17px;
}

/* Pushes the mockup to the bottom so both cards line up when the copy above
   them runs to different lengths. */
.mech-panel .offer-card {
  margin-top: auto;
  max-width: none;
}

.mech-caption {
  font-size: 14px;
  color: var(--text-muted);
}

.offer-who {
  font-size: 15px;
  font-weight: 700;
  margin-bottom: var(--s3);
}
.offer-who span {
  display: block;
  font-weight: 500;
  color: var(--text-muted);
}

.offer-price {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--text-2);
  margin-bottom: var(--s4);
}

/* A quote request is not a priced offer, so it does not get the orange-money
   treatment — the accent is brand green and the card leads with the ask. */
.offer-card--quote .offer-app {
  color: var(--brand);
}

/* A single positioning line, set large. Not a testimonial — no attribution,
   because there is no customer to attribute it to. */
.pull-quote {
  font-family: var(--font-display);
  font-size: clamp(21px, 2.6vw, 30px);
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -0.015em;
  max-width: 26ch;
  padding-left: var(--s5);
  border-left: 3px solid var(--action);
  margin-bottom: var(--s10);
}

/* Section sub-heading, a step down from .section-title. */
.subhead {
  font-size: clamp(20px, 2vw, 24px);
  margin-bottom: var(--s4);
}

/* Sub-label above a chip group inside a mechanics panel. */
.mech-sub {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s3);
}

/* The revenue illustration. Deliberately NOT the .stat treatment — a 42px
   figure in a feature card reads as a reported metric, and this is arithmetic.
   The label leads, the number sits inline in the sentence that qualifies it. */
.scenario-label {
  display: inline-block;
  padding: 4px var(--s3);
  border-radius: var(--r-chip);
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s4);
}

.stat-line {
  font-size: 17px;
  color: var(--text-2);
}
.stat-line strong {
  font-family: var(--font-display);
  font-size: 19px;
  font-variant-numeric: tabular-nums;
  color: var(--text);
}

/* Secondary audience hand-off at the foot of the CTA band. */
.cta-aside {
  margin-top: var(--s6);
  font-size: 15px;
  color: var(--cta-text-2);
}
.cta-aside a {
  color: var(--cta-text);
  font-weight: 600;
}

/* --------------------------------------------------- service profile -- */
.profile-mock {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-feature);
  box-shadow: var(--shadow-card);
  padding: var(--s5);
  max-width: 420px;
}

.profile-head {
  display: flex;
  align-items: center;
  gap: var(--s3);
  padding-bottom: var(--s4);
  border-bottom: 1px solid var(--border);
}
.profile-head img {
  width: 32px;
  height: 32px;
  border-radius: 8px;
}
.profile-title {
  font-family: var(--font-display);
  font-size: 16px;
  font-weight: 700;
}
.profile-sub {
  font-size: 14px;
  color: var(--text-muted);
}

.profile-rows {
  list-style: none;
  padding: 0;
}
.profile-rows li {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  min-height: 48px;
  border-bottom: 1px solid var(--border);
}
.profile-rows li:last-child {
  border-bottom: 0;
}

.pref-name {
  font-size: 16px;
  font-weight: 500;
}

.pref-pill {
  flex: none;
  padding: 4px var(--s3);
  border-radius: var(--r-chip);
  border: 1px solid transparent;
  font-size: 13px;
  font-weight: 600;
}
/* Orange is DoorYard's "take action" color and a bookable offer is the one that
   carries a price, so offers get it. Quotes take a brand tint, and "both" takes
   the brand SOLID — same hue, more weight, which reads as the stronger state and
   avoids two near-identical teal tints sitting next to each other. Off is a
   plain outline that reads as switched off, not as an error. */
.pref-pill--offers {
  background: var(--warning-surface);
  color: var(--on-warning-surface);
}
.pref-pill--quotes {
  background: var(--surface-brand);
  color: var(--brand);
}
.pref-pill--both {
  background: var(--brand);
  color: var(--on-brand);
}
.pref-pill--off {
  color: var(--text-muted);
  border-color: var(--border-strong);
}

/* --------------------------------------------------------------- split -- */
.split {
  display: grid;
  gap: var(--s10);
  align-items: center;
}
@media (min-width: 960px) {
  .split {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  }
  /* Source order is always the reading order (heading, then its supporting
     card) so the single-column mobile layout makes sense. This only pulls the
     card to the left once there are two columns to put it in. */
  .split--reverse > *:last-child {
    order: -1;
  }
}

/* ----------------------------------------------------------------- cta -- */
/* The closing band carries a photograph of a service van on a residential
   street at golden hour — the app's own hero-bg, reused here. It sits under a
   --cta-surface scrim rather than raw, for two reasons: the band has to stay a
   dark surface in BOTH themes, and the photo's brightest pixels are near-white.
   At 0.88 the worst case anywhere in the image is 9.5:1 for --cta-text and
   6.3:1 for --cta-text-2, so no crop or focal point can break the text.
   background-color stays set, so a failed image request degrades to the flat
   band it was before. */
.cta-band {
  position: relative;
  isolation: isolate;
  background-color: var(--cta-surface);
  background-image: url("/assets/cta-street.webp");
  background-size: cover;
  background-position: center 58%;
  border-radius: var(--r-sheet);
  padding: clamp(var(--s10), 6vw, var(--s16));
  text-align: center;
  color: var(--cta-text);
}
.cta-band::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1; /* above the band's own background, below its content */
  border-radius: inherit;
  background: var(--cta-surface);
  opacity: 0.88;
  pointer-events: none;
}
.cta-band h2 {
  color: var(--cta-text);
  font-size: clamp(26px, 3.2vw, 38px);
  margin-bottom: var(--s4);
  margin-inline: auto;
  max-width: 22ch;
}
.cta-band p {
  color: var(--cta-text-2);
  max-width: 56ch;
  margin: 0 auto var(--s8);
}
.cta-band .btn-row {
  justify-content: center;
}

/* -------------------------------------------------------------- footer -- */
.site-footer {
  border-top: 1px solid var(--border);
  padding-block: var(--s12) var(--s10);
  background: var(--page);
}
.footer-grid {
  display: grid;
  gap: var(--s8);
  margin-bottom: var(--s10);
}
@media (min-width: 820px) {
  .footer-grid {
    grid-template-columns: 1.6fr repeat(3, 1fr);
  }
}
.footer-about p {
  color: var(--text-2);
  font-size: 15px;
  max-width: 40ch;
  margin-top: var(--s4);
}
.footer-col h3 {
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: var(--s4);
}
.footer-col ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--s3);
}
.footer-col a {
  color: var(--text-2);
  text-decoration: none;
  font-size: 15px;
}
.footer-col a:hover {
  color: var(--brand);
}
.footer-legal {
  border-top: 1px solid var(--border);
  padding-top: var(--s6);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s4) var(--s6);
  align-items: center;
  justify-content: space-between;
  font-size: 14px;
  color: var(--text-muted);
}
.footer-legal a {
  color: var(--text-muted);
}

/* ------------------------------------------------------------ doc page -- */
.doc {
  padding-block: var(--s12) var(--s16);
}
.doc-inner {
  max-width: 760px;
  margin-inline: auto;
}
.doc h1 {
  font-size: clamp(30px, 4vw, 44px);
  margin-bottom: var(--s3);
}
.doc .updated {
  color: var(--text-muted);
  font-size: 15px;
  margin-bottom: var(--s8);
}
.doc h2 {
  font-size: 22px;
  margin-top: var(--s10);
  margin-bottom: var(--s3);
  padding-top: var(--s6);
  border-top: 1px solid var(--border);
}
.doc h2:first-of-type {
  border-top: 0;
  padding-top: 0;
  margin-top: var(--s8);
}
.doc h3 {
  font-size: 18px;
  margin-top: var(--s6);
  margin-bottom: var(--s2);
}
.doc p,
.doc li {
  color: var(--text-2);
  font-size: 17px;
  margin-bottom: var(--s4);
}
.doc ul,
.doc ol {
  padding-left: var(--s6);
  margin-bottom: var(--s4);
}
.doc li {
  margin-bottom: var(--s2);
}
.doc strong {
  color: var(--text);
}
.callout {
  background: var(--surface-brand);
  border: 1px solid var(--border);
  border-left: 3px solid var(--brand);
  border-radius: var(--r-control);
  padding: var(--s5);
  margin-block: var(--s6);
}
.callout p:last-child {
  margin-bottom: 0;
}
.doc-toc {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--s5) var(--s6);
  margin-bottom: var(--s10);
}
.doc-toc h2 {
  border: 0;
  padding: 0;
  margin: 0 0 var(--s3);
  font-size: 13px;
  font-family: var(--font-body);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.doc-toc ol {
  margin: 0;
  padding-left: var(--s5);
}
.doc-toc li {
  margin-bottom: var(--s2);
  font-size: 15px;
}

.contact-grid {
  display: grid;
  gap: var(--s4);
  margin-bottom: var(--s8);
}
@media (min-width: 720px) {
  .contact-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
.contact-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--r-card);
  padding: var(--s5);
}
.contact-card h3 {
  font-size: 17px;
  margin-bottom: var(--s1);
}
.contact-card p {
  font-size: 15px;
  margin-bottom: var(--s3);
}
.contact-card a {
  font-weight: 600;
  word-break: break-word;
}

/* ------------------------------------------------------- scroll reveal --
   Gated on .js, which a one-line inline script in <head> sets. If scripting
   is off — or app.js simply never arrives — the selector never matches and
   every section renders visible. Content must never depend on JS to be seen. */
.js .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
}
