/* VisibleRoofers public site.
 *
 * The palette is the site's own, deliberately NOT the operator hub's
 * tokens (owner decision 2026-09-01): the hub is a dense internal
 * cockpit, this is marketing, and one set of variables cannot serve
 * both. Derived from the Caldera reference the owner chose, reduced to
 * ember plus neutrals; the violet and sulfur were dropped as reading
 * crypto rather than trade.
 *
 * Rules the reference is strict about and this file keeps: flat, no
 * shadows anywhere. Surfaces separate by color, not elevation. Pills
 * for every button, 40px on cards. Ember is the ONLY chromatic accent
 * and is reserved for action and for the win.
 *
 * Ember on a light surface is roughly 3:1, so it is never used for
 * small text. Buttons put obsidian ON ember, which is the reference's
 * own pairing and passes comfortably.
 */

/* ---------- font ---------- */

@font-face {
  font-family: 'Archivo';
  src: url('/fonts/archivo-latin-var.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}

/* ---------- tokens ---------- */

:root {
  --pumice: #e2e2df;
  --limestone: #f7f6f2;
  --obsidian: #070607;
  --chalk: #ffffff;
  --ember: #fc5000;
  /* Ember, pressed. Hoisted out of .btn--ember:hover, which carried it as
     a bare literal: the pricing panel's CTA is an ember button too and
     needs the same pressed value, and a second copy of a hex in a
     component rule is how two ember buttons start hovering to different
     oranges. Obsidian on it is 4.91:1, so the 17px/600 label still clears
     AA while pressed. */
  --ember-deep: #e04800;

  /* neutrals derived for this site; the reference names none */
  --ink-soft: #56544f;
  --ink-faint: #78756e;
  --rule: #cdcdc8;
  --rule-soft: #dedeD9;
  --on-dark: #b9b7b1;
  --dark-rule: #24231f;
  --dark-raise: #131211;

  --font: 'Archivo', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --wrap: 1280px;
  --pad: clamp(20px, 5vw, 48px);
  --section: clamp(56px, 8vw, 104px);
  --card-pad: clamp(24px, 3.5vw, 40px);

  --r-card: 40px;
  --r-small: 16px;
  --r-pill: 800px;
}

* { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  /* The skip link and every in-page anchor would otherwise scroll their
     target to y=0, under the pinned header. --nav-h is measured in
     site.js; the fallback covers the frame before it runs. */
  scroll-padding-top: calc(var(--nav-h, 84px) + 16px);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--pumice);
  color: var(--obsidian);
  font-family: var(--font);
  font-size: 17.5px;
  font-weight: 450;
  line-height: 1.6;
  font-variation-settings: 'wdth' 100;
}

img, svg { max-width: 100%; }
a { color: inherit; }
p { margin: 0; }

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 800;
  font-variation-settings: 'wdth' 112;
  letter-spacing: -0.025em;
  line-height: 1;
  text-wrap: balance;
}

:focus-visible {
  outline: 2.5px solid var(--ember);
  outline-offset: 3px;
  border-radius: 3px;
}

.wrap { max-width: var(--wrap); margin: 0 auto; padding-inline: var(--pad); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--obsidian); color: var(--chalk);
  padding: 12px 20px; border-radius: var(--r-pill);
}
.skip:focus { left: 16px; top: 16px; }

.eyebrow {
  font-size: 12.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.1em; color: var(--ink-faint); margin: 0 0 14px;
  font-variation-settings: 'wdth' 100;
}
.eyebrow--ember { color: var(--ember); }

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

.btn {
  display: inline-block; text-decoration: none;
  font-weight: 600; font-size: 16px; font-variation-settings: 'wdth' 100;
  padding: 13px 26px; border-radius: var(--r-pill);
  border: 1.5px solid transparent; cursor: pointer;
}
.btn--ember { background: var(--ember); color: var(--obsidian); }
.btn--ember:hover { background: var(--ember-deep); }
.btn--out { border-color: var(--obsidian); color: var(--obsidian); background: transparent; }
.btn--out:hover { background: rgba(7, 6, 7, 0.06); }
.btn--lg { padding: 16px 32px; font-size: 17px; }

.on-dark .btn--out { border-color: rgba(255, 255, 255, 0.42); color: var(--chalk); }
.on-dark .btn--out:hover { background: rgba(255, 255, 255, 0.1); }

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

/* The header collapses into a floating pill on scroll.
 *
 * The one mechanic worth stating: VERTICAL padding never changes. A
 * sticky element stays in the document flow, so growing or shrinking it
 * while pinned reflows the whole page under the reader's thumb. Only
 * max-width, horizontal padding, background, border and radius animate,
 * which keeps the header exactly as tall in both states. The gap above
 * the floating pill is `top: 12px` rather than padding, for the same
 * reason: it costs no height.
 *
 * `--pill-w` is measured in site.js, because a pill has to hug its
 * contents and `max-content` is not a length CSS can tween from. */

.nav {
  /* top:0, not top:12px. A sticky box whose flow position already
     violates its own constraint is shifted DOWN to satisfy it, so a
     12px offset here would push the header 12px over the top of main on
     every unscrolled page. The gap belongs to the floating pill alone. */
  position: sticky; top: 0; z-index: 60;
  /* The bar is pinned from the first scrolled pixel, so it needs the
     page's own ground under it: without this, content scrolls THROUGH
     the nav text for the first 48px, and forever if site.js never runs. */
  background: var(--pumice);
  border-bottom: 1px solid var(--rule);
  transition: top .34s cubic-bezier(.22, 1, .36, 1),
              border-color .3s ease, background-color .34s ease;
  /* Locked to the UNSTUCK height, measured in site.js. A sticky box is
   * in flow, so a pill shorter than the bar it replaces would pull the
   * whole page up by the difference at the scroll threshold. On a
   * phone, where the bar carries a second row of links, that is an 80px
   * jump under the reader's thumb. The box keeps its height and the pill
   * floats at the top of it. */
  min-height: var(--nav-h, auto);
}
/* Which leaves a transparent strip below and beside the pill. It must
 * not swallow clicks meant for the page scrolling behind it. */
.nav.is-stuck { pointer-events: none; }
.nav.is-stuck .nav__in { pointer-events: auto; }
.nav.nomotion, .nav.nomotion * { transition: none !important; }
.nav__in {
  max-width: var(--wrap); margin: 0 auto; padding: 13px var(--pad);
  display: flex; align-items: center; gap: 10px 26px; flex-wrap: wrap;
  border: 1.5px solid transparent; border-radius: 0;
  transition: max-width .5s cubic-bezier(.22, 1, .36, 1),
              padding .5s cubic-bezier(.22, 1, .36, 1),
              gap .5s cubic-bezier(.22, 1, .36, 1),
              background-color .34s ease, border-color .34s ease,
              border-radius .34s ease;
}
.nav.is-stuck { top: 12px; border-bottom-color: transparent; background: transparent; }
.nav.is-stuck .nav__in,
.nav.measuring .nav__in {
  /* The pill hugs its contents, and keeps a gutter once the viewport is
     narrower than they are. */
  max-width: min(var(--pill-w, 1040px), calc(100% - 24px));
  padding: 13px 13px 13px 22px;
  gap: 10px 22px;
  background: var(--chalk);
  border-color: var(--rule);
  border-radius: var(--r-pill);
}
/* One frame with the pill at its natural width, so JS can read it. */
.nav.measuring .nav__in { max-width: max-content; transition: none; }

.mark {
  display: inline-flex; align-items: center; gap: 9px; text-decoration: none;
  font-weight: 800; font-size: 19px; font-variation-settings: 'wdth' 116;
  letter-spacing: -0.035em; flex: none;
}
.mark svg { display: block; flex: none; }

/* The reference's dotted rule, drawn rather than dashed: a border-style
 * cannot make round dots. */
.nav__sep {
  width: 2px; align-self: stretch; flex: none; margin-block: 3px;
  background-image: radial-gradient(circle, var(--rule) 1px, transparent 1.1px);
  background-size: 2px 7px;
}

.nav__links {
  display: flex; flex-wrap: wrap; gap: 6px 22px;
  font-size: 15px; font-weight: 500;
}
.nav__links a { text-decoration: none; color: var(--ink-soft); padding: 4px 0; }
.nav__links a:hover { color: var(--obsidian); }
.nav__links a[aria-current="page"] {
  color: var(--obsidian); font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--ember);
}
.nav__tel {
  margin-left: auto; flex: none; text-decoration: none; color: var(--ink-soft);
  font-size: 15px; font-weight: 600; font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.nav__tel:hover { color: var(--obsidian); }
.nav .btn { padding: 10px 20px; font-size: 15px; flex: none; }

/* ---------- the narrow-screen menu ---------- */
/* A <details>, not a button and a script. It opens and closes with no
 * JavaScript at all, and the browser gives <summary> the button role and
 * the expanded state for free; site.js only adds the three things markup
 * cannot express (Escape, an outside tap, and closing behind a followed
 * link). The four links are duplicated in the source rather than moved,
 * because a closed <details> hides its contents whatever the CSS says,
 * so one element cannot be both the bar's row and the panel's list. Only
 * one copy is ever displayed, so only one is ever in the accessibility
 * tree: the two <nav> landmarks are never both present. */
.nav__menu { display: none; position: relative; flex: none; }
.burger {
  list-style: none; cursor: pointer; display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 12px;
  border: 1.5px solid var(--rule); background: var(--chalk);
  transition: border-color .2s ease, background-color .2s ease;
}
.burger::-webkit-details-marker { display: none; }
.burger:hover { border-color: var(--ink-faint); }
.burger:focus-visible { outline: 3px solid var(--obsidian); outline-offset: 2px; }
.burger__i { position: relative; display: block; width: 18px; height: 12px; }
.burger__i i {
  position: absolute; left: 0; right: 0; height: 2px; border-radius: 2px;
  background: var(--obsidian);
  transition: transform .26s cubic-bezier(.22, 1, .36, 1), opacity .16s ease;
}
.burger__i i:nth-child(1) { top: 0; }
.burger__i i:nth-child(2) { top: 5px; }
.burger__i i:nth-child(3) { top: 10px; }
.nav__menu[open] .burger__i i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav__menu[open] .burger__i i:nth-child(2) { opacity: 0; }
.nav__menu[open] .burger__i i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

.nav__panel {
  position: absolute; top: calc(100% + 12px); right: 0; z-index: 3;
  min-width: 216px; display: flex; flex-direction: column; gap: 2px;
  padding: 8px; background: var(--chalk);
  border: 1.5px solid var(--rule); border-radius: var(--r-card);
  box-shadow: 0 20px 44px -20px rgba(7, 6, 7, .38);
  animation: menu-in .2s cubic-bezier(.22, 1, .36, 1);
}
@keyframes menu-in { from { opacity: 0; transform: translateY(-6px); } }
.nav__panel a {
  text-decoration: none; color: var(--ink-soft); font-size: 16px;
  font-weight: 500; padding: 11px 14px; border-radius: 10px;
  white-space: nowrap;
}
.nav__panel a:hover { background: var(--limestone); color: var(--obsidian); }
.nav__panel a[aria-current="page"] {
  color: var(--obsidian); font-weight: 600;
  box-shadow: inset 2px 0 0 var(--ember);
}
/* The bar still shows the number down to 820px, so between there and
   the menu's own breakpoint it would be on screen twice at once. */
@media (min-width: 821px) {
  .nav__panel-tel { display: none; }
}
.nav__panel-tel {
  margin-top: 6px; padding-top: 13px !important;
  border-top: 1px solid var(--rule); border-radius: 0 0 10px 10px;
  color: var(--obsidian) !important; font-weight: 600 !important;
  font-variant-numeric: tabular-nums;
}

/* ---------- the audit form ---------- */
/* Three fields stacked on a phone, two-up from 560px where the labels
   still fit. No JavaScript in the path: a plain POST to the door. */
/* The audit form, revealed by the CTA that asks for it.

   Closed is the DEFAULT and open is the :target state, which means the
   opening is done by the anchor itself - no script, on this page or
   arriving from another. The rule lives in the stylesheet rather than
   in the markup so that a reader who gets no CSS gets the form, which
   is the direction this has to fail. */
.askgate { margin: 26px 0 0; }
.askgate:focus { outline: none; }        /* tabindex=-1 is for focus placement, not a ring */
.askgate .ask { display: none; }
.askgate:target .ask { display: block; }
.askgate:target .askgate__open { display: none; }
/* Sized to its words on a wide screen; full width on a phone, where a
   thumb-wide target is the norm and the column is only 520px anyway. */
.askgate__open { justify-content: center; }
@media (max-width: 700px) { .askgate__open { width: 100%; } }

.ask {
  margin: 0; padding: 22px; max-width: 520px;
  background: var(--chalk); border: 1.5px solid var(--rule);
  border-radius: var(--r-card);
}
.ask__t { margin: 0 0 16px; font-size: 16px; font-weight: 600; color: var(--obsidian); }
.ask__f { display: grid; gap: 12px; margin-bottom: 16px; }
.fld { display: grid; gap: 5px; }
.fld > span { font-size: 13px; font-weight: 600; color: var(--ink-soft); }
.fld input {
  font: inherit; font-size: 16px; /* 16px or iOS zooms the page on focus */
  padding: 11px 13px; border: 1.5px solid var(--rule); border-radius: 10px;
  background: var(--limestone); color: var(--obsidian); width: 100%;
  transition: border-color .18s ease, background-color .18s ease;
}
.fld input::placeholder { color: var(--ink-faint); }
.fld input:hover { border-color: var(--ink-faint); }
.fld input:focus {
  outline: none; border-color: var(--ember); background: var(--chalk);
  box-shadow: 0 0 0 3px rgba(252, 80, 0, .16);
}
.ask .btn { width: 100%; justify-content: center; }
.ask__n { margin: 12px 0 0; font-size: 13px; line-height: 1.45; color: var(--ink-faint); }
/* The honeypot. Off-screen rather than display:none, because a bot that
   reads computed style skips what is not rendered and fills what is. */
.ask__hp {
  position: absolute; left: -9999px; width: 1px; height: 1px;
  overflow: hidden; margin: 0;
}
@media (min-width: 560px) {
  .ask__f { grid-template-columns: 1fr 1fr; }
  .fld:first-child { grid-column: 1 / -1; }
}

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

.hero { padding: clamp(40px, 6.5vw, 84px) 0 var(--section); }
.hero__in {
  max-width: var(--wrap); margin: 0 auto; padding-inline: var(--pad);
  display: grid; grid-template-columns: minmax(0, 1fr) 380px;
  gap: clamp(32px, 5vw, 72px); align-items: start;
}
.hero h1 { font-size: clamp(42px, 6.4vw, 92px); max-width: 12ch; }
.hero__lede {
  margin-top: 24px; max-width: 46ch; font-size: clamp(17px, 1.5vw, 19.5px);
  color: var(--ink-soft);
}
.hero__acts { margin-top: 30px; display: flex; flex-wrap: wrap; gap: 12px; }
.hero__note {
  margin-top: 22px; font-size: 15px; color: var(--ink-faint); max-width: 42ch;
  line-height: 1.5;
}
.hero__note a { color: var(--obsidian); }

.hero--inner { padding-bottom: clamp(36px, 5vw, 64px); }
.hero--inner .hero__in { grid-template-columns: 1fr; }
.hero--inner h1 { font-size: clamp(38px, 5.6vw, 76px); max-width: 16ch; }

/* ---------- the stage ---------- */

.stage { position: sticky; top: 96px; }
.stage__cap {
  margin-top: 14px; font-size: 12.5px; color: var(--ink-faint); line-height: 1.5;
  text-align: center;
}
.tabs { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-bottom: 14px; }
.tab {
  appearance: none; border: 0; background: transparent; padding: 0 0 8px;
  cursor: pointer; text-align: left; font-family: var(--font);
  font-size: 12.5px; font-weight: 600; color: var(--ink-faint);
  border-top: 2px solid var(--rule); position: relative;
}
.tab span { display: block; padding-top: 8px; }
.tab::after {
  content: ""; position: absolute; top: -2px; left: 0; height: 2px;
  width: var(--p, 0%); background: var(--ember);
}
.tab[aria-selected="true"] { color: var(--obsidian); }

.phone {
  width: 100%; max-width: 380px; aspect-ratio: 360 / 720;
  background: var(--obsidian); border-radius: 34px; padding: 9px;
  position: relative; margin-inline: auto;
}
.screen {
  position: relative; width: 100%; height: 100%; background: #fff;
  border-radius: 26px; overflow: hidden;
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
}
.scene { position: absolute; inset: 0; opacity: 0; pointer-events: none; transition: opacity .3s; }
.scene.on { opacity: 1; pointer-events: auto; }
.bar {
  height: 34px; background: #f1f3f4; display: flex; align-items: center;
  justify-content: center; font-size: 11px; color: #5f6368;
  border-bottom: 1px solid #e3e6e8;
}

/* scene 1: search */
.g { padding: 26px 16px 0; height: calc(100% - 34px); display: flex; flex-direction: column; }
.g__logo {
  font-weight: 700; font-size: 26px; letter-spacing: -0.04em;
  text-align: center; color: #4285f4; font-variation-settings: 'wdth' 100;
}
.g__logo i { font-style: normal; }
.g__logo i:nth-child(2) { color: #ea4335; } .g__logo i:nth-child(3) { color: #fbbc05; }
.g__logo i:nth-child(4) { color: #4285f4; } .g__logo i:nth-child(5) { color: #34a853; }
.g__logo i:nth-child(6) { color: #ea4335; }
.g__box {
  margin: 16px 0 20px; border: 1px solid #dfe1e5; border-radius: 20px;
  padding: 9px 14px; display: flex; align-items: center; gap: 8px;
  font-size: 12.5px; color: #202124; min-height: 38px;
}
.g__box svg { flex: none; }
.caret {
  display: inline-block; width: 1.5px; height: 14px; background: #202124;
  vertical-align: -2px; animation: blink 1s steps(1) infinite;
}
@keyframes blink { 50% { opacity: 0 } }
.g__res { position: relative; flex: 1; }
/* The resting state is the FINISHED scene: every row at the offset its
 * markup gives it, at full opacity, the winner already highlighted. The
 * script blanks it when it takes the scene over, so a reader without
 * JavaScript keeps the finished frame instead of an empty phone. */
.row {
  --rowh: 54px;
  position: absolute; left: 0; right: 0; top: 0; height: var(--rowh); padding: 7px 8px;
  border-radius: 6px; display: flex; gap: 9px; align-items: center;
  transform: translateY(calc(var(--y, 0) * var(--rowh)));
  transition: transform .62s cubic-bezier(.22, 1, .36, 1), background .4s, box-shadow .4s;
}
.row__f {
  width: 22px; height: 22px; border-radius: 50%; background: #dde1e6; flex: none;
  display: grid; place-items: center; font-size: 10px; font-weight: 700; color: #7a8592;
}
.row__t { font-size: 12.5px; color: #1a0dab; line-height: 1.25; font-weight: 500; }
.row__u { font-size: 10.5px; color: #5f6368; margin-top: 2px; }
.row__p {
  margin-left: auto; font-variant-numeric: tabular-nums; font-size: 11px;
  font-weight: 700; color: #9aa4af; flex: none; transition: color .4s;
}
.row.win { background: rgba(252, 80, 0, .1); box-shadow: inset 0 0 0 1.5px var(--ember); }
.row.win .row__f { background: var(--ember); color: #fff; }
.row.win .row__p { color: var(--ember); }

/* scene 2: maps */
.map { position: relative; height: 60%; background: #e8ebe6; overflow: hidden; }
.map > svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.pin {
  position: absolute; transform: translate(-50%, -100%) scale(.2); opacity: 0;
  transition: transform .5s cubic-bezier(.34, 1.56, .64, 1), opacity .3s;
}
.pin.in { transform: translate(-50%, -100%) scale(1); opacity: 1; }
.pin--win { z-index: 3; }
.halo {
  position: absolute; left: 50%; top: 76%; width: 44px; height: 44px;
  margin: -22px 0 0 -22px; border-radius: 50%; background: var(--ember); opacity: 0;
}
.pin--win.in .halo { animation: halo 2.2s ease-out .25s 2; }
@keyframes halo { 0% { transform: scale(.3); opacity: .5 } 100% { transform: scale(2.4); opacity: 0 } }
.sheet {
  position: absolute; left: 0; right: 0; bottom: 0; height: calc(40% + 14px);
  background: #fff; border-radius: 14px 14px 0 0; padding: 12px 14px;
  transform: translateY(100%); transition: transform .6s cubic-bezier(.22, 1, .36, 1);
}
.sheet.in { transform: translateY(0); }
.sheet__grab { width: 34px; height: 4px; background: #dadce0; border-radius: 2px; margin: 0 auto 11px; }
.biz { display: flex; gap: 10px; padding: 9px 8px; border-radius: 8px; margin: 0 -8px; align-items: flex-start; }
.biz + .biz { border-top: 1px solid #eef0f2; }
.biz__n { font-size: 12.5px; font-weight: 600; color: #202124; }
.biz__m { font-size: 10.5px; color: #5f6368; margin-top: 3px; }
.biz__c {
  margin-left: auto; flex: none; font-size: 10.5px; font-weight: 700; color: #1a73e8;
  border: 1px solid #d2e3fc; border-radius: 14px; padding: 4px 9px; background: #f4f8fe;
}
.biz--win { background: rgba(252, 80, 0, .09); box-shadow: inset 0 0 0 1.5px var(--ember); }
.biz--win .biz__c { color: #fff; background: var(--ember); border-color: var(--ember); }

/* scene 3: ai */
.ai { height: calc(100% - 34px); display: flex; flex-direction: column; padding: 16px 14px; }
.ai__h { font-size: 11px; font-weight: 700; color: #98a2ae; margin-bottom: 14px; }
.ai__b { flex: 1; display: flex; flex-direction: column; gap: 12px; overflow: hidden; }
.msg { max-width: 88%; font-size: 12.5px; line-height: 1.55; border-radius: 14px; padding: 10px 13px; }
.msg--u { align-self: flex-end; background: #edf0f4; color: #202124; border-bottom-right-radius: 4px; }
.msg--a {
  align-self: flex-start; background: #fff; color: #202124; border: 1px solid #e6e9ed;
  border-bottom-left-radius: 4px; opacity: 0; transition: opacity .3s;
}
.msg--a.in { opacity: 1; }
.msg--a b { color: var(--ember); font-weight: 700; }
.dots { display: inline-flex; gap: 4px; align-items: center; height: 14px; }
.dots i { width: 5px; height: 5px; border-radius: 50%; background: #b6bec8; animation: bob 1.1s infinite; }
.dots i:nth-child(2) { animation-delay: .15s } .dots i:nth-child(3) { animation-delay: .3s }
@keyframes bob { 0%, 60%, 100% { transform: translateY(0); opacity: .5 } 30% { transform: translateY(-4px); opacity: 1 } }
.cites { display: flex; gap: 5px; margin-top: 9px; flex-wrap: wrap; }
.cite {
  font-size: 9.5px; font-weight: 600; color: #5f6368; background: #f1f3f5;
  border-radius: 10px; padding: 3px 8px; opacity: 0; transform: translateY(4px); transition: .35s;
}
.cite.in { opacity: 1; transform: none; }

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

.sec { padding: var(--section) 0; }
/* A section running straight on from an inner-page hero, which already
 * carries the top spacing. */
.sec--tight { padding-top: 0; }
.sec h2 { font-size: clamp(30px, 4.4vw, 56px); max-width: 20ch; }
.sec__lede { margin-top: 18px; max-width: 62ch; color: var(--ink-soft); font-size: 18px; }
.sec--sunken { background: var(--limestone); }

.split {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  gap: clamp(24px, 5vw, 72px); align-items: start;
}
.split p + p { margin-top: 16px; }
.split p { color: var(--ink-soft); font-size: 18px; }

/* ---------- cards ---------- */

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(19rem, 100%), 1fr)); gap: 20px; margin-top: 44px; }
/* Four cards auto-fit to three-plus-one, which reads as a mistake. This
 * widens the track so they land two by two instead. */
.cards--pair { grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr)); }
.card { background: var(--limestone); border-radius: var(--r-card); padding: var(--card-pad); }
.card h3 { font-size: 22px; margin-bottom: 16px; font-variation-settings: 'wdth' 106; }
.card p { color: var(--ink-soft); font-size: 15.5px; }
/* Surfaces separate by colour here, not elevation, so a limestone card
 * on a limestone section is an invisible card. Lift it to chalk. */
.sec--sunken .card { background: var(--chalk); }
.dark .card { background: var(--dark-raise); }
.dark .card h3 { color: var(--chalk); }
.dark .card p { color: var(--on-dark); }
.card ul { list-style: none; margin: 0; padding: 0; font-size: 15.5px; color: var(--ink-soft); }
.card li { padding: 6px 0 6px 22px; position: relative; }
.card li::before {
  content: ''; position: absolute; left: 0; top: 14px; width: 9px; height: 5px;
  border-left: 2px solid var(--ember); border-bottom: 2px solid var(--ember);
  transform: rotate(-45deg);
}

/* spec rows */
.spec { border-top: 1.5px solid var(--obsidian); margin-top: 40px; }
.spec__row {
  display: grid; grid-template-columns: 210px minmax(0, 1fr) 160px;
  gap: clamp(14px, 3vw, 40px); padding: 26px 0; border-bottom: 1px solid var(--rule);
  align-items: baseline;
}
.spec__t { font-weight: 700; font-size: 20px; font-variation-settings: 'wdth' 106; letter-spacing: -0.015em; }
.spec__d { color: var(--ink-soft); font-size: 16.5px; max-width: 62ch; }
.spec__m { font-size: 14px; color: var(--ink-faint); text-align: right; font-weight: 600; }

/* numbered steps */
.steps { list-style: none; counter-reset: s; margin: 44px 0 0; padding: 0; display: grid; gap: 20px; }
.sec--sunken .step { background: var(--chalk); }
.step { background: var(--limestone); border-radius: var(--r-card); padding: var(--card-pad); display: grid; grid-template-columns: 76px minmax(0, 1fr); gap: 8px 20px; align-items: start; }
.step::before {
  counter-increment: s; content: counter(s, decimal-leading-zero);
  font-weight: 800; font-size: 40px; font-variation-settings: 'wdth' 112;
  color: var(--ember); line-height: .9; letter-spacing: -0.03em;
}
.step h3 { font-size: 24px; margin-bottom: 10px; }
.step p { color: var(--ink-soft); font-size: 16.5px; max-width: 62ch; }
.step p + p { margin-top: 12px; }
.step > div { grid-column: 2; }

/* ---------- dark band ---------- */

.dark { background: var(--obsidian); color: var(--chalk); padding: var(--section) 0; }
/* .dark is its own band rather than a .sec modifier, so it has to restate
 * the section heading scale or its h2 lands at the base size. */
.dark h2 { color: var(--chalk); font-size: clamp(30px, 4.4vw, 56px); max-width: 20ch; }
.dark .sec__lede, .dark p { color: var(--on-dark); }
.dark .sec__lede + .sec__lede { margin-top: 18px; }
.dark .eyebrow { color: var(--ember); }


.panel { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: clamp(22px, 4vw, 48px); align-items: start; margin-top: 40px; }
/* ---------- the sample report ----------
 *
 * One surface lift off the .dark ground, hairlines inside it. The tiles
 * and the chart carry a border and no fill of their own, because three
 * filled cards on a filled panel inside a filled section is three lifts
 * to say one thing.
 *
 * The three tiles are NOT a chart, deliberately: a search position, a
 * map-pack position and a count of platforms are three different units,
 * and one axis cannot hold them. A number is the right mark for a
 * number. Only the fourth thing, one keyword over twelve months, is a
 * shape, so only that is drawn. */

.rep {
  margin-top: 40px; background: var(--dark-raise);
  border: 1px solid var(--dark-rule); border-radius: var(--r-card);
  padding: clamp(18px, 2.4vw, 28px);
}
.rep__bar {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 8px 20px; flex-wrap: wrap;
  padding-bottom: 14px; border-bottom: 1px solid var(--dark-rule);
}
.rep__t { font-size: 12px; font-weight: 700; letter-spacing: .11em; text-transform: uppercase; color: var(--on-dark); }
.rep__s { font-size: 13px; color: var(--on-dark); font-variant-numeric: tabular-nums; }
.rep__n { margin-top: 16px; font-size: 13.5px; color: var(--on-dark); max-width: 62ch; }

.ba3 {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(14rem, 100%), 1fr));
  gap: 10px; margin: 18px 0;
}
.ba { border: 1px solid var(--dark-rule); border-radius: var(--r-small); padding: 16px 18px; }
.ba__k { font-size: 13px; font-weight: 700; color: var(--chalk); }
.ba__q { font-size: 13px; color: var(--on-dark); margin-top: 3px; }
/* The tiles read "#41 -> #2" visually, which is clear beside a label but
   says nothing on its own to a screen reader. These carry the sense the
   arrow carries by shape. */
.sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }
.ba__v { display: flex; align-items: center; gap: 11px; margin-top: 15px; font-variant-numeric: tabular-nums; }
/* The BEFORE recedes and the AFTER leads: the reader is being shown a
   distance, and the end of it is the point. Ember rides the arrow alone,
   so the accent marks the movement rather than competing with either
   number. Text never wears the series colour. */
.ba__a { font-size: 19px; font-weight: 600; color: var(--on-dark); }
.ba__ar { width: 24px; height: 12px; flex: none; color: var(--ember); }
.ba__b { font-size: 29px; font-weight: 800; font-variation-settings: 'wdth' 108; line-height: 1; color: var(--chalk); }

.rep .box { background: transparent; padding: 0; border-radius: 0; border: 1px solid var(--dark-rule); padding: 16px 14px 8px; }
.box__k { font-size: 13px; color: var(--on-dark); margin-bottom: 6px; }

.chart { width: 100%; height: auto; display: block; overflow: visible; }
.chart .grid { stroke: var(--dark-rule); stroke-width: 1; }
.chart .axis { fill: var(--on-dark); font-family: 'Archivo', sans-serif; font-size: 11px; font-variant-numeric: tabular-nums; }
.chart .ln { fill: none; stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round; }
/* The endpoints are the only direct labels. A rank chart with a number
   on all twelve points is unreadable, and the axis carries the rest. */
.chart .end { font-family: 'Archivo', sans-serif; font-variant-numeric: tabular-nums; font-weight: 800; }
.chart .end--a { fill: var(--on-dark); font-size: 14px; font-weight: 600; }
.chart .end--b { fill: var(--chalk); font-size: 18px; }

/* The reveal FAILS OPEN, which the version this replaces did not: it set
   the clip to scaleX(0) and only ever restored it from a .live class the
   script adds, so with JS off the chart was not slow to appear, it was
   permanently invisible. The resting state is now the finished chart,
   and the script ARMS the animation before playing it. A reader without
   JS, or mid-download, sees the picture. */
/* SVG text scales with the viewBox, so a fixed 11px user-unit label is
   ~5px on a phone. These restate the sizes in user units so the RENDERED
   size stays near constant as the chart shrinks. Two steps rather than
   one, because the scale factor roughly halves between 700px and 390px
   and a single value is either too big at the top or too small at the
   bottom. */
@media (max-width: 700px) {
  .chart .axis { font-size: 15px; }
  .chart .end--a { font-size: 18px; }
  .chart .end--b { font-size: 24px; }
}
@media (max-width: 480px) {
  .chart .axis { font-size: 22px; }
  .chart .end--a { font-size: 26px; }
  .chart .end--b { font-size: 34px; }
}

.chart .reveal { transform: scaleX(1); transform-origin: left; }
.chart.armed .reveal { transform: scaleX(0); }
.chart.armed.live .reveal { transform: scaleX(1); transition: transform 1.9s cubic-bezier(.32, .72, .3, 1); }
.chart.armed .dot, .chart.armed .end { opacity: 0; }
.chart.armed.live .dot, .chart.armed.live .end { opacity: 1; transition: opacity .4s 1.1s; }

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

/* The panel stops using the accent as a ground.
 *
 * The header of this file says ember is the ONLY chromatic accent and is
 * reserved for action and for the win. Every other use obeys that at
 * punctuation scale: a button fill, a 12.5px eyebrow, a 40px step
 * numeral, a 9px card tick. The pricing panel was the one exception, and
 * it spent the largest colour field on the site on a static fact rather
 * than on an action.
 *
 * Underneath the taste problem sat a structural one. On an ember ground
 * the site's own action pill cannot exist, because nothing can be more
 * ember than ember, so the primary conversion control had to invert to
 * an obsidian pill with an orange label: the one click signal four pages
 * teach, running backwards at the point of conversion. The focus ring
 * inverted with it. :focus-visible draws 2.5px of ember at a 3px offset,
 * which on an ember ground is 1.00:1, so keyboard focus on the site's
 * primary CTA was invisible on both pages. The ground change fixes that
 * rather than patching it, which is why nothing below touches
 * outline-color: the same ring lands on obsidian at 6.06:1.
 *
 * The arithmetic rules out a gentler version of the old idea, so do not
 * try one. #fc5000 sits in a value dead zone: chalk on it is 3.34:1 and
 * fails AA, obsidian on it is 6.06:1 and passes. An ember ground does not
 * choose black-on-orange, it is cornered into it, and can then hold only
 * one text value, which is why nothing in the old panel led.
 *
 * Obsidian is not invented here. .phone is already a rounded obsidian
 * object on the light page ground, and .closer is already a full-bleed
 * obsidian band. The panel runs .dark's own recipe inside it and the one
 * ember object left is the button. A light plate was the alternative and
 * fails on the home page: chalk on limestone is 1.08:1, exactly the lift
 * .sec--sunken .card uses, so a pale panel there IS a card, and the
 * page's climax cannot be a card. */

.price {
  background: var(--obsidian); color: var(--chalk);
  border-radius: var(--r-card);
  padding: clamp(28px, 4.5vw, 56px); margin-top: 40px;
  display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px); align-items: center;
}

/* On the home page only, this panel is followed immediately by .closer,
   which is also obsidian. One section of limestone between two dark
   masses is thin. They stay separate objects because one is rounded,
   inset and two-column and the other is square, full-bleed and centred;
   this only widens the gap. The pricing page's panel is four sections
   from any dark band and is deliberately untouched. Same ground
   contextual fork .sec--sunken .card and .sec--sunken .step already use. */
.sec--sunken .price { margin-bottom: clamp(16px, 3vw, 40px); }

.price__n {
  color: var(--chalk);
  font-weight: 800; font-variation-settings: 'wdth' 112;
  font-size: clamp(52px, 8vw, 104px); line-height: .9; letter-spacing: -0.035em;
}
/* The unit is not part of the number, so it steps back and lets "$2,499"
   read as one mass, with the 3px gap that pairing wants.
   --on-dark is 10.09:1 here, which matters because at the 52px floor this
   renders 14.6px and is judged as normal text, not large. */
.price__n small {
  color: var(--on-dark); margin-left: 3px;
  font-size: .28em; font-weight: 700; letter-spacing: -0.01em;
}

/* Was rgba(7, 6, 7, .88), which composited to #240f06 on ember at 5.49:1,
   the only muddy pair on the site. A real token instead. */
.price p { color: var(--on-dark); margin-top: 18px; font-size: 17px; max-width: 62ch; }

.terms { list-style: none; margin: 24px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; }
/* 1px, not the old 1.5px: a heavier outline makes four facts look like
   four buttons competing with the one real control. --ink-faint is 4.40:1
   on obsidian, a shade over what .on-dark .btn--out's 42% white already
   composites to, so this is the established on-dark hairline taken from a
   token rather than from an alpha. Deliberately not ember-outlined: four
   terms are neither an action nor a win, and outlining them would cost
   the CTA its monopoly on colour. */
.terms li {
  border: 1px solid var(--ink-faint); border-radius: var(--r-pill);
  padding: 6px 16px; font-size: 14px; font-weight: 600; color: var(--on-dark);
}

/* Neither panel holds a .btn--out today. The rule is kept rather than
   deleted because deleting it fails open: this panel is obsidian but its
   wrapper is not .on-dark, so a secondary button added here later would
   inherit the light .btn--out and render obsidian on obsidian at 1.00:1,
   with nothing to notice. The values are .on-dark .btn--out's own. */
.price .btn--out { border-color: var(--ink-faint); color: var(--chalk); background: transparent; }
.price .btn--out:hover { background: rgba(255, 255, 255, 0.1); }

.price__cta { text-align: center; }
/* The site's action pill, restored. It is the only chromatic object in
   the panel, so it wins on hue as well as on fill, which is what makes it
   unambiguously the most clickable thing here.
   text-align is set explicitly because .btn is inline-block and inherits
   it: below 1000px .price__cta goes text-align:left, so a full-width pill
   whose label has wrapped renders it ragged against the left inset. That
   shows up at 320px, where the label is 227px in a 224px content box and
   takes two lines. The padding-inline clamp buys most of that back and
   leaves 1100px and up exactly as btn--lg had it. */
.price__cta .btn {
  background: var(--ember); color: var(--obsidian); border-color: var(--ember);
  width: 100%; text-align: center;
  padding-inline: clamp(18px, 2.2vw, 32px);
}
.price__cta .btn:hover { background: var(--ember-deep); border-color: var(--ember-deep); }
.price__cta p { color: var(--on-dark); font-size: 14.5px; font-weight: 600; margin-top: 14px; }
/* .closer's own pairing on this ground. Without it the phone number is an
   unmarked link inside its own caption, since a { color: inherit }. */
.price__cta p a { color: var(--ember); }

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

.faq { max-width: 52rem; margin-top: 40px; }
.faq details { background: var(--limestone); border-radius: var(--r-small); margin-bottom: 10px; }
.faq summary {
  cursor: pointer; padding: 18px 24px; font-weight: 600; font-size: 17px;
  list-style: none; display: flex; justify-content: space-between; align-items: center; gap: 16px;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+' / ''; color: var(--ember); font-size: 20px; font-weight: 700; flex: none; }
.faq details[open] summary::after { content: '\2212' / ''; }
.faq p { padding: 0 24px 20px; color: var(--ink-soft); font-size: 16.5px; }

/* ---------- closer + footer ---------- */

.closer { background: var(--obsidian); color: var(--chalk); padding: var(--section) 0; text-align: center; }
.closer h2 { color: var(--chalk); font-size: clamp(32px, 5vw, 64px); max-width: 18ch; margin-inline: auto; }
.closer p { margin: 18px auto 30px; max-width: 46ch; color: var(--on-dark); font-size: 18px; }
.closer .line { margin-top: 20px; font-size: 15px; color: var(--on-dark); }
.closer .line a { color: var(--ember); }

.foot { border-top: 1px solid var(--rule); padding: 30px 0; font-size: 14.5px; color: var(--ink-faint); }
.foot__in { display: flex; flex-wrap: wrap; gap: 12px 28px; align-items: center; }
.foot__in > :last-child { margin-left: auto; }
.foot a { color: var(--ink-soft); }

/* ---------- legal pages ---------- */

/* /privacy/ and /terms/ are documents, not marketing pages. Every other
 * page here separates its sections with var(--section) padding, and a
 * dozen of those in a row is a page nobody reaches the bottom of, so the
 * clauses stack on a rule instead. The heading sizes come down with them:
 * .sec h2 is built for four words at 56px. */
.legal__s { margin-top: 36px; padding-top: 36px; border-top: 1px solid var(--rule); }
.legal__s:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.legal h2 { font-size: clamp(23px, 2.4vw, 30px); max-width: 18ch; }
.legal ul { margin: 16px 0 0; padding-left: 20px; color: var(--ink-soft); font-size: 18px; }
.legal li { padding: 5px 0; }
/* .split only spaces p-after-p, so a paragraph closing a list sat flush
 * against the last bullet. Both legal pages do that three times over. */
.legal ul + p { margin-top: 16px; }
.legal li::marker { color: var(--ink-faint); }
/* Base `a` inherits its colour, which inside .split p is --ink-soft: a
 * link the same weight and colour as the sentence around it. On a page
 * whose whole job is telling someone where to go next, it gets marked. */
.legal a { color: var(--obsidian); text-decoration: underline; text-underline-offset: 2px; }

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

@media (max-width: 1000px) {
  .hero__in, .panel, .split { grid-template-columns: 1fr; }
  .stage { position: static; margin-top: 8px; }
  .tabs { max-width: 380px; margin-inline: auto; }
  /* .price collapses here too, but its whole collapse lives in one rule
     rather than on the shared line above: the column gap has to become a
     horizontal rule at the same breakpoint, and splitting that across two
     places is how one half gets changed without the other. */
  .price { grid-template-columns: 1fr; gap: 30px; }
  .price__cta {
    text-align: left;
    /* Stacked, the gap alone reads as loose spacing rather than as the
       break between the offer and the act. --dark-rule is the file's own
       dark divider and at full column width it has the length to carry
       1.29:1; the padding above is sized so the separation survives even
       if the rule washes out on a poor screen. */
    border-top: 1px solid var(--dark-rule); padding-top: 28px;
  }
}
@media (max-width: 820px) {
  .spec__row { grid-template-columns: 1fr; gap: 6px; padding: 22px 0; }
  .spec__m { text-align: left; margin-top: 4px; }
  .step { grid-template-columns: 1fr; }
  .step > div { grid-column: 1; }
  /* The links became the menu's at 1100px above. What goes HERE is the
     phone number and the dotted rules, which do fit at 900px and do not
     at 500px. Losing the number is why the menu carries it. */
  .nav__sep, .nav__tel { display: none; }
  /* `.nav__tel` was carrying `margin-left: auto` for the whole row, so
     with it gone the right-hand pair needs its own push. */
  .nav .btn { margin-left: auto; }
}
/* Where the links stop fitting on one line, in BOTH states.
   Measured: the full bar wraps to two rows from about 1060px down, and
   the pill (about 1012px wide plus its gutter) from about 1036px. One
   breakpoint at 1100px clears both with room for a font that loads late.

   It used to be the pill's alone, which left the bar two rows tall from
   1060 to 821 and, worse, made the nav change shape under the reader:
   at 1000px the unscrolled bar showed four inline links and scrolling
   swapped them for a menu button. Same widths, same affordance now. */
@media (max-width: 1100px) {
  .nav__links { display: none; }
  /* The menu takes them, in the bar and in the pill. `measuring` is the
     frame site.js reads the pill's width from, so the button has to be
     in the box being measured or the pill comes out 42px too narrow;
     that frame gets the rule here because it gets every other one. */
  .nav__menu { display: block; }
  .nav.is-stuck .nav__in,
  .nav.measuring .nav__in { flex-wrap: nowrap; }
  .nav.is-stuck .nav__tel,
  .nav.measuring .nav__tel { margin-left: 0; }
  .nav.is-stuck .btn,
  .nav.measuring .btn { margin-left: auto; }
}
@media (max-width: 820px) {
  .nav.is-stuck .nav__in,
  .nav.measuring .nav__in { padding: 11px 11px 11px 16px; }
}
/* Below this the wordmark, the button and the menu cannot share one
   line, and a header that wraps to two rows costs the hero 40px on the
   screens with the least of it. The roof mark carries the identity on
   the narrowest phones; every wider screen keeps the name.

   This was the PILL's rule at 430px until the menu button joined the
   row (2026-09-11). It applies to the bar as well now, and 50px higher,
   because the bar carries the same three children the pill does. */
@media (max-width: 480px) {
  .mark__t { display: none; }
  .nav__in { gap: 10px 14px; }
}
/* Pre-existing, found while verifying the header: at 320px the content
   box is 280px and "Homeowners" at the h1's 42px floor measures 303px,
   so the home page scrolled sideways. The floor only binds below ~530px
   anyway, so lowering it for the narrowest phones leaves every other
   width untouched. */
@media (max-width: 350px) {
  .hero h1 { font-size: 36px; }
  /* At 320px the panel's own inset decides whether the price fits:
     "$2,499/mo" at the 52px floor measures 218px against a 224px content
     box. Trimming the inset here buys it real margin, and buys the CTA
     label back most of the width it needs to stay on one line. */
  .price { padding: 26px 22px; }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }

}
