/* ============================================================
   Domas & Co. — marketing site (joindomas.com), v4 design
   Implemented from the Claude Design project "Domas & Co Site v4.dc.html"
   (2026-07-19). Marketing has no build step and can't import
   frontend/src/theme.css directly, so these are the app's real token
   *values* mirrored literally here — keep this block in sync with
   theme.css's semantic layer (and app_colors.dart, its Flutter mirror)
   if it changes.

   2026-07-19 (later): rebuilt around a small reusable utility/component
   layer (Section 2) instead of one bespoke BEM class per section, mirroring
   frontend/src/base.css's flex/gap/grid utility convention. Section 3 is
   what's left after consolidation — genuinely one-off site chrome (nav,
   hero photo, waitlist panel) that isn't reused elsewhere on a
   single-page site, not a violation of "reusable classes only."

   2026-08-03: re-mirrored after the 2026-08-02 brand refresh (theme.css /
   app_colors.dart moved off the forest-green/putty palette onto British
   green + ink-scale text). Values below are the new primitives; only the
   forest/putty era's underlying hexes changed, no marketing-only tokens.
   ============================================================ */

:root {
  /* ---- Primitives - exact names + hex from the Figma "Domas Colors"
     variable collection, mirrored verbatim so app.joindomas.com and
     joindomas.com read identical primitive names, not just matching
     values under different names. Marketing has no build step and
     can't import theme.css directly - this is the manual mirror; keep
     it in sync with theme.css's own primitive block if it changes. ---- */
  --ink:           #1a1a1a;
  --ink-2:         #2f3a36;
  --ink-3:         #49504e;
  --body:          #7b7b7b;
  --body-light:    #9c9c9c;
  --british-green: #004225;
  --moss:          #4d7b66;
  --light-green:   #89bda5;
  --gold:          #f3b541;
  --sage:          #f2ede1;
  --gray:          #f4f4f4;
  --white:         #ffffff;

  /* ---- Semantic - reads the primitives above via var(), same
     mapping as theme.css. No separate danger/error token: marketing
     has no error/validation states anywhere on this static site
     (confirmed - grepped the whole file). ---- */
  --brand-primary:   var(--ink);
  --brand-secondary: var(--ink-2);
  --accent:          var(--british-green);
  --accent-hover:    var(--moss);

  --bg:              var(--white);
  --bg-alt:          var(--gray);
  --card:            var(--white);
  --text:            var(--ink-3);
  --text-muted:      var(--body);
  --border:          var(--body-light);

  --font-heading:    'Inter', -apple-system, BlinkMacSystemFont, sans-serif; /* theme.css --font-display (2026-08-03: Outfit -> Inter) */
  --font-body:       'Inter', -apple-system, BlinkMacSystemFont, sans-serif;  /* theme.css --font-body */

  --section-pad:     80px 5%;
  --radius:          9px; /* theme.css --radius-md */
  --shadow:          0 1px 2px rgba(61, 67, 76, 0.10), 0 2px 6px rgba(61, 67, 76, 0.14), 0 4px 10px rgba(61, 67, 76, 0.10); /* theme.css --shadow-card */

  /* Reusable type scale - most sections snap to one of these instead of a
     bespoke clamp() each. The three genuinely singular "hero moments"
     (hero headline, membership price, footer wordmark) stay bespoke below -
     forcing those onto a shared scale would mean the biggest, most
     deliberate statement on the page and a tiny price-subtitle competing
     for the same class. */
  --fs-body:  15px;
  --fs-link:  18px; /* one size for every link/button/input on the site */
  --fs-h2:    clamp(28px, 3.5vw, 50px);
  --fs-h3:    clamp(30px, 3.6vw, 48px);

  /* Reusable gap scale */
  --gap-2xs: 8px;
  --gap-xs:  12px;
  --gap-sm:  16px;
  --gap-md:  24px;
  --gap-lg:  32px;
}

* { box-sizing: border-box; }

/* No global smooth-scroll - it fights GSAP ScrollTrigger's own scrub/pin
   math (documented GSAP gotcha) and is what was causing the hard snap
   into the pinned card-stack section. Real fix (2026-08-31): the
   scroll-behavior:smooth rule below survived a past edit despite this
   comment, so it was STILL happening on same-page nav clicks - now
   removed for real. Nav/footer/CTA anchor clicks are handled in
   script.js's click listener instead (GSAP ScrollToPlugin when the
   target is a pinned section, plain scrollIntoView otherwise), which
   coordinates with ScrollTrigger instead of fighting it. */

body {
  margin: 0;
  background: var(--card);
  color: var(--text);
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

body::after {
  content: '';
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 90;
  opacity: 0.05;
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="240" height="240"><filter id="n"><feTurbulence type="fractalNoise" baseFrequency="0.85" numOctaves="2"/></filter><rect width="240" height="240" filter="url(%23n)"/></svg>');
}

a { color: var(--text); text-decoration: none; }
a:hover { color: var(--accent-hover); }
input, button { font-family: var(--font-body); }
input:focus { outline: none; }
::placeholder { color: var(--text-muted); opacity: 0.8; }
::selection { background: var(--accent); color: var(--card); }

/* Custom scrollbar - thin ink-toned pill on a transparent track, accent
   on hover, matching the site's restrained editorial palette instead of
   the default OS chrome (same accent used by ::selection above). */
html { scrollbar-width: thin; scrollbar-color: var(--ink-3) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--ink-3); border-radius: 999px; }
::-webkit-scrollbar-thumb:hover { background: var(--accent); }

button { cursor: pointer; }

em { font-style: normal; font-weight: 600; color: var(--accent-hover); }

/* Global h2 - matches app.joindomas.com's real h2 (base.css) exactly:
   same font family/weight/tracking/line-height/clamp formula, values
   translated to this file's own mirrored tokens (2.75rem here =
   theme.css --text-xl). A bare tag selector, same mechanism as the h3
   rule below. */
h2 {
  font-family: var(--font-body);
  letter-spacing: -1px;
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 2.75rem);
  font-weight: 300;
  line-height: 1.15;
  color: var(--text);
  margin: 0;
}

h3 {
  font-family: var(--font-body);
  letter-spacing: -.5px;
  font-size: clamp(1.3rem, 1.05rem + 1vw, 1.9rem);
  font-weight: 300;
  line-height: 1.15;
  color: var(--text);
  margin: 0;
}

p {
  font-family: var(--font-body);
  letter-spacing: -.3px;
  font-size: clamp(18px, 1.1rem + 1.6vw, 16px);
  font-weight: 400;
  line-height: 1.45;
  color: var(--body);
  margin: 0;
}


/* ============================================================
   2. Reusable utilities / shared components
   Every one of these is used 2+ times across the page. Combine them on
   an element instead of inventing a new one-off class.
   ============================================================ */

/* ---- Layout: flex / gap ---- */
.flex { display: flex; }
.flex-col { display: flex; flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.justify-end { justify-content: flex-end; }
.gap-sm  { gap: var(--gap-sm); }
.gap-md  { gap: var(--gap-md); }
.mt-md   { margin-top: var(--gap-md); }
.mt-lg   { margin-top: var(--gap-lg); }

/* ---- Text ---- */
.text-pretty { text-wrap: pretty; }

/* ---- Solid pill button (nav CTA, membership CTA, step-form nav) ---- */
.btn-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  background: none;
  border: none;
  border-radius: 999px;
  padding: 14px 24px 16px;
  font-size: var(--fs-link);
  letter-spacing: 0.5px;
  text-transform: capitalize;
  font-weight: 400;
  transition: color 0.2s, background-color 0.2s;
}
/* Bug found via real layout check (2026-08-24), not eyeballing: at
   exactly 390px, the hero and pricing CTA rows both sit right at
   the edge of the row's available width (badge + gap + button ≈
   the panel's content width). Without this, flexbox's default
   flex-shrink:1 squeezed the button's own width down first instead
   of wrapping the row - "Join free ↗" wrapped onto 2 lines inside
   the button (computed height jumped 51px -> 72px), confirmed via
   getComputedStyle, not assumed. flex-shrink:0 forces the intended
   behavior instead: the row's own flex-wrap moves the button to a
   new line at its natural size when space runs out. */
.btn-link { flex-shrink: 0; }
.btn-link:hover { color: var(--accent-hover); }
.btn-link-accent { color: var(--bg); background-color: var(--accent-hover); }
.btn-link-accent:hover { color: var(--white); background-color: var(--light-green); }

/* ---- Full-bleed photo + dark scrim (hero, "the standard") ---- */
.photo-overlay { position: relative; overflow: hidden; }
.photo-overlay-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Bleed sizing only matters for whatever still has data-parallax on it
   (pricing's background photo - hero's parallax was removed entirely,
   the attribute is gone from its <img> so this rule no longer matches
   it at all, back to plain .photo-overlay-img inset:0 sizing).
   Travels ±8 yPercent of img height (124% of section = ±9.92%); 12%
   bleed covers it. */
.photo-overlay-img[data-parallax] { top: -12%; left: 0; width: 100%; height: 124%; }
.photo-overlay-img.pricing-img[data-parallax] { top: -20%; height: 140%; will-change: transform; }
.photo-overlay-scrim { position: absolute; inset: 0; pointer-events: none; }

/* height locked to --stable-vh (script.js), not the inherited 100%
   (-> tracks .hero's own dvh height) (2026-08-07, Rocco: "the hero
   background photo is not" [fixed]). object-fit:cover was
   re-scaling/re-cropping the source image every time .hero's dvh
   height changed on browser chrome collapse - box width was genuinely
   stable (confirmed: 390px both ways) but the image's OWN internal
   scale/crop was still recalculating, which reads as "the photo
   changes" even though the box didn't.
   First attempt used CSS 100lvh, assuming spec's "largest viewport"
   semantic - verified directly that didn't hold (same-session resize
   test showed it tracking exactly like dvh, not staying locked).
   --stable-vh is the older, explicit, JS-verified equivalent: only
   ever grows to the largest window.innerHeight actually observed,
   never shrinks. Locking the image to that means it renders at one
   constant scale/crop - .hero (top:0, overflow:hidden per
   .photo-overlay) just reveals more or less of that ONE fixed
   rendering as its own dvh height changes, instead of the image
   re-rendering at a new crop each time. Fallback 100dvh covers the
   instant before script.js's first setStableVh() call sets the real
   value. */
.hero-img { will-change: transform; position: absolute; top: 0; height: calc(var(--stable-vh, 100dvh)); }

/* ---- A link/button styled as one row of a contact list (footer) ---- */
.link-row {
  border-bottom: 1px solid var(--border);
  padding-bottom: 10px;
  display: flex;
  justify-content: space-between;
  background: none;
  border-top: none; border-left: none; border-right: none;
  font-size: var(--fs-link);
  font-family: var(--font-body);
  color: var(--text);
  text-align: left;
}
.link-row:hover { color: var(--accent-hover); }

/* ============================================================
   3. Site chrome - one-off per landmark region, not reused elsewhere
   on a single-page site (equivalent to a component's own scoped BEM
   family - see domas-coding-learnings.md's "reuse before drafting new
   classes" rule, applied here at the section rather than the class level).
   ============================================================ */

/* ---------- Nav ---------- */

/* scroll-padding-top only, no scroll-behavior (see comment near the top
   of this file) - fixed nav is 70px so targets land clear of it, still
   needed by script.js's scrollIntoView fallback path. */
html { scroll-padding-top: 70px; }

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 100;
  padding: 0 24px;
  height: 70px;
  background: transparent;
  color: var(--card);
  transition: background 0.3s ease, color 0.3s ease;
}
/* Transparent-over-hero by default; .nav-scrolled (toggled by
   ScrollTrigger in script.js once the hero's bottom edge passes the
   nav) swaps it to the solid white bar. Text/logo need their own color
   flip too - var(--card) (white) reads on the photo, var(--text)
   (dark) is what's needed once the background goes white. */
.nav-scrolled {
  background: var(--card);
  color: var(--text);
}
.nav-logo { transition: filter 0.3s ease, color 0.3s ease; }
.nav-logo { filter: brightness(0) invert(1); }
.nav-scrolled .nav-logo { filter: none; }
.nav-logo { height: 44px; display: block; }

/* Nav links + CTA slide in together as one group on scroll (no photo-hero
   pages, i.e. legal, render .nav-scrolled from first paint - nothing to
   slide in from since there's no transparent state to leave). Curated
   links only (the same 3 the footer already uses), not one per section. */
.nav-right {
  gap: var(--gap-lg);
  opacity: 0;
  transform: translateX(24px);
  transition: opacity 0.35s ease, transform 0.35s ease;
  pointer-events: none;
}
.nav-scrolled .nav-right {
  opacity: 1;
  transform: translateX(0);
  pointer-events: auto;
}
.nav-links { gap: var(--gap-lg); }
.nav-cta {
  font-size: var(--fs-body);
  background-color: var(--ink-2);
}
.nav-cta:hover { background-color: var(--ink-3); }
.nav-link {
  font-size: var(--fs-body);
  font-weight: 400;
  font-family: var(--font-body);
  letter-spacing: 0.3px;
  color: var(--text-muted);
  transition: color 0.2s ease;
}
.nav-link:hover { color: var(--accent-hover); }

/* ---------- Mobile nav (hamburger + slide-in panel, 2026-08-14) ----------
   Structural pattern only, not a visual clone: a right-anchored ink-2
   card with its own close button and oversized stacked links, inset
   from the edges with rounded corners, dimmed backdrop behind it.
   Hidden entirely on desktop; the 860px query below is what actually
   shows/positions it. */
.nav-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 32px;
  height: 32px;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--white);
  transition: background 0.3s ease;
}
.nav-scrolled .nav-toggle span { background: var(--ink); }

.mobile-nav-backdrop {
  position: fixed;
  inset: 0;
  z-index: 199;
  background: rgba(0, 0, 0, 0.45);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.35s ease;
}
.mobile-nav-backdrop.is-open { opacity: 1; pointer-events: auto; }

.mobile-nav {
  position: fixed;
  top: 12px; right: 12px; bottom: 12px;
  z-index: 200;
  width: min(340px, 82vw);
  background: var(--ink-2);
  border-radius: 20px;
  padding: 20px 28px 32px;
  display: flex;
  flex-direction: column;
  transform: translateX(calc(100% + 24px));
  transition: transform 0.4s ease;
}
.mobile-nav.is-open { transform: translateX(0); }

.mobile-nav-top {
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.mobile-nav-close {
  background: none;
  border: none;
  color: var(--white);
  font-size: 20px;
  line-height: 1;
  padding: 6px;
  cursor: pointer;
}

.mobile-nav-links {
  display: flex;
  flex-direction: column;
  gap: 22px;
  margin: 24px 0 32px;
}
.mobile-nav-links a {
  font-family: var(--font-heading);
  font-size: clamp(28px, 8vw, 36px);
  color: var(--white);
  letter-spacing: -0.5px;
}
.mobile-nav-links a:hover { color: var(--light-green); }

/* Plain, understated - deliberately not the big heading-style links
   above. Same pattern as .nav-link (muted default, accent on hover). */
.mobile-nav-legal {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 50px;
}
.mobile-nav-legal a {
  font-size: var(--fs-body);
  color: var(--body-light);
}
.mobile-nav-legal a:hover { color: var(--light-green); }

.mobile-nav-contact {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.mobile-nav-contact a {
  font-size: var(--fs-body);
  color: var(--white);
}
.mobile-nav-contact a:hover { color: var(--light-green); }

.mobile-nav-social {
  color: var(--white);
  margin-top: 18px;
  display: inline-flex;
  width: fit-content;
}
.mobile-nav-social:hover { color: var(--light-green); }

/* margin-top: auto used to live on .mobile-nav-cta itself. Moved
   onto .mobile-nav-badge below (2026-08-24, Rocco: "put the google
   button in the mobile nav above login") - only one flex item's
   auto margin actually needs to absorb the free space; whichever
   item gets it is what pins to the panel's bottom edge, and
   everything after it in the DOM just follows immediately below. */
.mobile-nav-cta { width: 100%; }

.mobile-nav-badge { display: block; width: fit-content; margin-top: auto; margin-bottom: var(--gap-sm); }

/* No override needed anymore - the base .btn-link-accent is a solid
   filled pill now, not an underline link, so there's no white-on-white
   hover risk against the nav's white scrolled bar (there was when this
   was text-on-transparent). Same fill everywhere. */

/* ---------- Hero ---------- */

/* Hero layout forked from the ZeroDrift reference (2026-07-19): inset
   rounded card below the nav, headline top-left with the CTA (waitlist)
   directly beneath it, description pinned bottom-left. No video block. */
/* Starts at the very top of the page (margin-top: 0, was 60px) so the
   photo runs full-bleed behind the now-transparent nav instead of
   sitting below a separate white bar. Height compensates: -40px instead
   of the old -100px, since the 60px no longer reserved by margin-top
   needs to stay part of the hero's own height for the bottom edge to
   land in the same place as before. */
.hero {
  position: sticky;
  top: 0;
  z-index: 0;
  height: calc(100vh - 0px);
  min-height: 560px;
}
.hero-scrim {
  background:
    linear-gradient(to bottom, hsl(0deg 0% 10% / 10%) 0%, rgb(0 0 0 / 0%) 38%, hsl(0deg 0% 10% / 20%) 60%),
    linear-gradient(to top, var(--brand-primary) 0%, #00000000 40%); /* theme.css --color-text */
}
/* White panel behind the hero copy - flush to the left edge and the
   bottom of the hero section, sized to the copy instead of a fixed
   box: .hero-body is back in normal flow (not inset:0) so its content
   height drives the panel's auto height. */
.hero-panel {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 65%;
  background: var(--ink-2);
}
.hero-panel h1 { color: var(--white); }
.hero-panel .btn-link-accent { color: var(--bg); background-color: var(--accent-hover); }
.hero-panel .btn-link-accent:hover { color: var(--white); background-color: var(--light-green); }
/* Bottom padding was 0 here while .pricing-panel and .vendor-cta both
   give their CTA real breathing room below (48px / 120px) - the hero
   button sat flush against the panel's bottom edge as a result.
   Mirroring the 48px top value fixes it and matches pricing-panel's
   own 48px top/bottom exactly; mobile's 7% 7% was already symmetric. */
.hero-body {
  padding: 48px 40px 48px;
}

/* Google Play badge, left of the hero "Join free" button (2026-08-24).
   Height sized to roughly match the button's own visual weight
   (--fs-link 18px + 14px/16px top/bottom padding ≈ 52px tall) - width
   left to auto so the badge's real aspect ratio isn't distorted.
   flex-wrap so a narrow viewport stacks them instead of overflowing;
   .hero-body's own mobile media query already goes to 7% 7% padding,
   nothing extra needed there. */
.hero-cta-row { flex-wrap: wrap; }
.play-badge { display: block; height: 52px; width: auto; flex-shrink: 0; }
h1 {
  margin: 0;
  font-family: var(--font-heading);
  font-weight: 300;
  font-size: clamp(40px, 6vw, 72px);
  line-height: 0.95;
  letter-spacing: -0.015em;
  color: var(--text);
}
/* Non-h1 elements that need the exact same "hero display" look as the
   real h1 above (2026-08-15, single-H1 SEO fix: several section
   headings were tagged h1 purely for this size - retagged to their
   correct semantic level and given this instead, so the outline is
   real but nothing on screen moved). */
.display-heading {
  font-family: var(--font-heading);
  font-weight: 200;
  font-size: clamp(40px, 6vw, 72px);
  letter-spacing: -0.015em;
}
.hero-sub {
  margin: 0;
  font-size: clamp(16px, 1.6vw, 20px);
  font-family: var(--font-body);
  font-weight: 400;
  line-height: 1.5;
  color: var(--white);
  max-width: 100%;
  opacity: 0.85;
}
/* ---------- Section padding (content differs, spacing is section-specific) ---------- */

/* position/z-index/background: lets this section scroll up and cover the
   sticky hero above (a non-positioned sibling paints below any positioned
   element regardless of DOM order, so it needs its own stacking context
   to actually appear on top - a bare higher z-index alone isn't enough).
   Every section after the hero needs this, not just .value specifically. */
.value { position: relative; z-index: 1; background: var(--ink-2); padding: 5% 3%; }
.value h2, .value .offer-card-title { color: var(--white); }
.value p { color: var(--body-light); }
.section-inside { width: 80%; margin: 0 auto; }
.value-grid {
  display: grid;
  grid-template-columns: .75fr 1fr;
  align-items: center;
  gap: 50px;
  margin: 0 auto;
  padding-top: 3%;
}
.value-list-item { border-bottom: 1px solid var(--light-green); }

/* ---------- .offer-card-title lives on in the .value list ---------- */
.offer-card-title { font-family: var(--font-heading); font-weight: 300; line-height: 1; margin: 0 0 10px; font-size: clamp(20px, 2.2vw, 26px); padding: 4% 0; }

/* ---------- How it works (pinned stepped phone demo) ----------
   Scroll-stepped walkthrough: three real app screens (Figma 47/49/51:652
   2x exports) inside a CSS phone frame; step copy swaps in sync. GSAP
   pins the section and each scroll advances one slide in script.js. .hiw-static
   (reduced motion / no GSAP) falls back to static flow. Overlay
   positions are % of the 414x896 screen; type sizes use cqw so they
   scale with the phone. */
.hiw { position: relative; z-index: 1; background: var(--card); }
/* min-height:100vh -> 100dvh (2026-08-07, Rocco: "when scrolling, the
   top bar going in and out messes with the design elements... it also
   happens on the hero photo" - real, well-known mobile issue: raw vh
   locks to the largest possible viewport and doesn't track the
   address bar showing/hiding, so vh-sized content visibly jumps as
   chrome collapses mid-scroll. .hero was already fixed to dvh for
   this exact reason (see its own comment) - .hiw-pin/.hiw-phone
   weren't, despite being the two other vh-sized elements on the page. */
.hiw-pin { min-height: 100dvh; display: flex; align-items: center; padding: 48px 0; }
.hiw-grid {
  display: grid;
  grid-template-columns: 1fr 0.75fr;
  align-items: center;
  gap: 50px;
  width: 100%;
}
.hiw-copy > h2 { margin: 0 0 44px; }
.hiw-steps { position: relative; min-height: 280px; }
.hiw-step { position: absolute; top: 0; left: 0; right: 0; opacity: 0; visibility: hidden; }
.hiw-step.is-active { opacity: 1; visibility: visible; }
.hiw-step-num { color: var(--accent); font-weight: 600; }
.hiw-step h3 { margin: 0 0 20px; }
.hiw-step p { margin: 0; max-width: 42ch; }

.hiw-phone-wrap { display: flex; justify-content: center; }
.hiw-phone {
  position: relative;
  height: min(84dvh, 900px);
  aspect-ratio: 433 / 903;
  container-type: size;
}
.hiw-frame {
  position: absolute;
  inset: 0;
  border-radius: 16.5cqw;
  background: linear-gradient(155deg, #575c5a 0%, #2b2f2e 18%, #202423 45%, #373c3a 78%, #191c1b 100%);
  box-shadow:
    0 2px 5px rgba(20, 25, 23, 0.06),
    0 26px 50px rgba(20, 25, 23, 0.1),
    inset 0 0.28cqw 0.45cqw rgba(255, 255, 255, 0.38),
    inset 0 -0.28cqw 0.45cqw rgba(0, 0, 0, 0.55),
    inset 0.28cqw 0 0.45cqw rgba(255, 255, 255, 0.14),
    inset -0.28cqw 0 0.45cqw rgba(0, 0, 0, 0.35);
  z-index: 1;
}
.hiw-bezel {
  position: absolute;
  inset: 1.1cqw;
  border-radius: 15.4cqw;
  background: #050706;
  z-index: 1;
}
.hiw-btn {
  position: absolute;
  width: 1.5cqw;
  left: -0.72cqw;
  border-radius: 0.75cqw;
  background: linear-gradient(180deg, #4b504e, #1c1f1e 28%, #2f3331 84%, #141716);
  z-index: 0;
}
.hiw-btn-action { top: 17.5cqh; height: 3.4cqh; }
.hiw-btn-volup { top: 23.4cqh; height: 6cqh; }
.hiw-btn-voldown { top: 30.6cqh; height: 6cqh; }
.hiw-btn-power { left: auto; right: -0.72cqw; top: 25.4cqh; height: 9.6cqh; }
.hiw-screen {
  position: absolute;
  inset: 3.4cqw;
  border-radius: 13.1cqw;
  overflow: hidden;
  background: var(--white);
  container-type: size;
  z-index: 2;
}
.hiw-screen-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; will-change: opacity; transform: translateZ(0); }
.hiw-screen-hidden { opacity: 0; visibility: hidden; }
.hiw-island {
  position: absolute;
  top: 1.5%;
  left: 50%;
  transform: translateX(-50%);
  width: 29%;
  height: 3.8%;
  background: var(--ink);
  border-radius: 999px;
  z-index: 5;
}
.hiw-type {
  position: absolute;
  left: 10.6%;
  top: 21.9%;
  display: flex;
  align-items: center;
  opacity: 0;
  visibility: hidden;
  z-index: 3;
  pointer-events: none;
}
.hiw-type-text {
  font-size: 3.38cqw;
  font-weight: 400;
  color: var(--ink);
  white-space: nowrap;
}
.hiw-caret {
  width: 1.5px;
  height: 4cqw;
  margin-left: 2px;
  background: var(--ink);
  animation: hiw-blink 1s steps(1) infinite;
}
@keyframes hiw-blink { 50% { opacity: 0; } }
.hiw-static .hiw-pin { min-height: 0; padding: 80px 0; }
.hiw-static .hiw-steps { min-height: 0; }
.hiw-static .hiw-step { position: static; opacity: 1; visibility: visible; margin-bottom: 36px; }

/* ---------- Pricing (was .membership - dropped the marquee ticker,
   kept the real price/copy/CTA). ---------- */
.pricing {
  position: relative;
  z-index: 1;
  color: var(--bg);
}
.pricing-scrim {
  background:
    linear-gradient(100deg,
      color-mix(in srgb, var(--ink) 50%, transparent) 0%,
      color-mix(in srgb, var(--ink) 0%, transparent) 48%,
      color-mix(in srgb, var(--ink) 0%, transparent) 100%);
}
.pricing-body {
  position: relative;
  padding: 13% 0 0;
}
.pricing-panel {
  position: relative;
  width: min(100%, 45vw);
  background: var(--ink-2);
  padding: 48px 40px 48px 0;
}
/* full-bleed: extend the panel to the left viewport edge (section clips overflow) */
.pricing-panel::before {
  content: "";
  position: absolute;
  inset: 0 100% 0 -50vw;
  background: var(--ink-2);
}
.pricing-panel h2 { color: var(--card); }
.pricing-panel h3 { color: var(--body-light); }
.pricing-panel .btn-link-accent { color: var(--bg); background-color: var(--accent-hover); }
.pricing-panel .btn-link-accent:hover { color: var(--white); background-color: var(--light-green); }
/* ---------- Vendors (contractor recruitment) ---------- */

.vendors { position: relative; z-index: 1; background: var(--sage); padding: 130px 0; }
.vendors-grid { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 70px; align-items: start; }
.vendors-intro { position: sticky; top: 130px; }
.vendors-intro h2 { margin: 0 0 24px; }
.vendors-intro p { margin: 0; max-width: 44ch; }
.vendor-row { padding: 30px 0; border-bottom: 1px solid color-mix(in srgb, var(--ink) 14%, transparent); }
.vendor-row:first-child { padding-top: 6px; }
.vendor-row h3 { margin: 0 0 10px; color: var(--ink-2); }
.vendor-row p { margin: 0; color: var(--body); }


/* ---------- Footer ---------- */

.footer { position: relative; z-index: 1; padding: 90px 0 30px; background: var(--ink); color: var(--white); }
.footer-top {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  gap: 64px;
  align-items: start;
  margin-bottom: 10px;
  padding-bottom: 56px;
  border-bottom: 1px solid color-mix(in srgb, var(--white) 12%, transparent);
}
/* Wraps the logo + Google Play badge (2026-08-24, Rocco: "put it
   under our logo in footer mt-lg") - needed a real wrapper since
   .footer-top is a grid with the logo as its own column; a bare
   sibling <img> after .footer-logo would've become a 5th grid item,
   not stacked under the logo. flex-col keeps them in one column
   regardless of how many tracks .footer-top's grid resolves to at
   each breakpoint. */
.footer-brand { display: flex; flex-direction: column; align-items: flex-start; }
.footer-logo { height: 40px; display: block; filter: invert(1); }
.footer-col { display: flex; flex-direction: column; gap: 14px; min-width: 200px; }
.footer-col h3 { margin: 0 0 8px; color: var(--body-light); }
.footer-rows { min-width: 300px; }
.footer .link-row { color: var(--white); border-bottom-color: color-mix(in srgb, var(--white) 18%, transparent); }
.footer .link-row:hover { color: var(--light-green); }
.footer-bottom {
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  font-size: 12px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--body-light);
}
.footer-bottom a { color: var(--body-light); transition: color 0.2s; }
.footer-bottom a:hover { color: var(--white); }

/* ---------- Responsive: site chrome ---------- */

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

/* ============================================================
   Responsive - consolidated, MUST stay last in the file: these are
   same-specificity overrides, so any base rule written after them
   in source order would silently win (that exact bug shipped once).
   ============================================================ */

@media (max-width: 1780px) {
  /* footer: logo on its own row, columns share thirds (the 4-track
     desktop grid needs ~1414px of container and only fits above this) */
  .footer-top { grid-template-columns: repeat(3, 1fr); }
  .footer-top .footer-brand { grid-column: 1 / -1; }
  .footer-col, .footer-rows { min-width: 0; }
}

@media (max-width: 860px) {
  .section-inside { width: 100%; }

  /* Mobile nav bar: logo left, hamburger right (the base .nav
     already carries justify-between via its HTML utility classes, so
     no override needed here) - swap the desktop link+CTA group for
     the toggle button that opens .mobile-nav. */
  .nav-right { display: none; }
  .nav-toggle { display: flex; }

  /* Full height on mobile (was calc(100vh - 40px) - a deliberate small
     gap on desktop that just reads as an awkward short-by-40px hero on
     a phone). 100dvh over 100vh for the same reason as the body
     backdrop fix elsewhere - tracks the real mobile viewport instead
     of the address-bar-inclusive one. */
  .hero { height: 100dvh; min-height: 0; }

  .hero-img { object-position: 20% 50%; }

  .hero-panel { width: 100%; }
  .hero-body { padding: 7% 7%; }
  h1 { font-size: clamp(2rem, 6vw, 72px); letter-spacing: -0.5px; }
  .display-heading { font-size: clamp(2rem, 6vw, 72px); letter-spacing: -0.5px; }

  .value { padding: 7% 7%; }
  .value p { margin: 0; }
  .value-grid { gap: 10px; padding-top: 5%; }
  .offer-card-title { font-size: clamp(1.5rem, 2.2vw, 26px); letter-spacing: -0.5px; }
  h2 { letter-spacing: -0.5px; font-size: clamp(1.7rem, 1.1rem + 1.6vw, 2.75rem); }
  h3 { font-size: clamp(1.5rem, 2.2vw, 26px); letter-spacing: -0.5px; }
  p { font-size: clamp(1.2rem, 1.1rem + 1.6vw, 16px); }

  .hiw-pin { padding: 50px 0; }
  .hiw-grid { grid-template-columns: 1fr; gap: 10px; }
  .hiw-copy { padding: 7% 7% 0% 7%; }
  .hiw-copy > h2 { margin-bottom: 3%; }
  .hiw-steps { min-height: 160px; }
  .hiw-step-num { letter-spacing: -1px; }

  /* Larger phone that intentionally bleeds past .hiw-pin's bottom edge
     and gets clipped there, instead of sitting fully contained.
     .hiw-static only applies under prefers-reduced-motion - real
     mobile visitors get the GSAP-pinned layout (same phone markup,
     scroll-driven), so .hiw-pin (not .hiw-static .hiw-step) is the
     actual "section floor" that needs the clip boundary in the normal
     case. Targeting both so it's correct either way. overflow:hidden
     is the clip; the negative margin-bottom on the phone pulls
     following content back up so the extra height doesn't just push
     everything down by the full new size - only the bottom slice that
     pokes past the (now-shorter) boundary actually gets clipped. */
  .hiw-pin, .hiw-static .hiw-step { overflow: hidden; }
  /* height (even dvh) -> width-driven (2026-08-07, Rocco: "the photo
     and phone changes width" while scrolling) - real bug, and one I
     introduced myself converting this to dvh earlier today. .hiw-phone
     uses aspect-ratio:433/903, which DERIVES WIDTH FROM HEIGHT - so a
     height that tracks the browser chrome collapsing (dvh, correctly)
     made the phone's WIDTH visibly resize too, confirmed directly
     (278px vs 334px measured at two different viewport heights, same
     viewport width both times). Viewport WIDTH never changes on
     mobile chrome collapse, only height - so driving size from width
     instead (height:auto lets aspect-ratio derive height FROM the now-
     stable width) makes the phone fully immune to this, not just
     using the "more correct" unit. 81vw/316px chosen to land close to
     the old 660px height cap's resulting size, not a visual change. */
  .hiw-phone {
    width: min(81vw, 316px);
    height: auto;
    margin-inline: auto;
    margin-bottom: -100px;
  }

  /* Desktop's 140%/-20% bleed (set for a larger parallax travel range)
     over-scales the image on a narrow mobile box - cover crops down to
     a ~25% horizontal sliver of a very wide (1920x1072) photo, reading
     as zoomed in. Mobile parallax amplitude is smaller, so less bleed
     is needed; shrinking it here widens the visible slice. */
  .photo-overlay-img.pricing-img[data-parallax] { height: 100%; top: -7.5%; }

  .pricing-panel { width: 100%; padding: 7% 7% 15% 7%; }
  .pricing-body { padding: 20em 0 0em 0; }

  .vendors { padding: 15% 7%; }
  .vendors-grid { grid-template-columns: 1fr; gap: 44px; }
  .vendors-intro { position: static; }

  .footer { padding: 15% 7% 7% 7%; }
  .footer-top {
    grid-template-columns: 1fr;
    gap: 44px;
    margin-bottom: 0px;
    padding-bottom: 40px;
    border-bottom: 0px solid color-mix(in srgb, var(--white) 12%, transparent);
  }
  .footer-rows { min-width: 0; }
}

/* ---------- Legal pages (privacy, terms) - 2026-08-11 ----------
   Plain-content pages, no photo hero, so .nav needs its .nav-scrolled
   state applied statically (see includes/header.html's comment) and
   the page itself needs top padding to clear the fixed 70px nav that
   the homepage's full-bleed hero otherwise sits under automatically. */
/* Sage inset card on white body, same surface as .vendors. Top margin
   clears the fixed 70px nav plus breathing room; later in file than
   .section-inside so this margin wins its 0 auto. */
/* .legal-body scopes the page-level sage flip to privacy/terms only -
   body itself stays white everywhere else (homepage etc). */
body.legal-body { background: var(--sage); }
.legal-page {
  margin: 106px auto 72px;
  background: var(--white);
  border-radius: 8px;
  padding: clamp(36px, 4vw, 64px) clamp(24px, 5vw, 80px) clamp(48px, 5vw, 80px);
}
.legal-page h1 {
  font-size: clamp(34px, 4.5vw, 56px);
  margin-bottom: 10px;
}
.legal-meta {
  color: var(--text-muted);
  font-size: 0.9rem;
  margin: 0 0 56px;
  padding-bottom: 24px;
  border-bottom: 1px solid rgba(26, 26, 26, 0.08);
}
.legal-grid {
  display: grid;
  grid-template-columns: 236px minmax(0, 68ch);
  gap: clamp(40px, 5vw, 88px);
  align-items: start;
}
.legal-toc {
  position: sticky;
  top: 102px;
}
.legal-toc h3 {
  font-size: 0.78rem;
  font-weight: 500;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 18px;
}
.legal-toc a {
  display: block;
  font-size: 0.88rem;
  line-height: 1.35;
  color: var(--text-muted);
  padding: 5px 0 5px 14px;
  border-left: 2px solid rgba(26, 26, 26, 0.1);
  border-bottom: none;
}
.legal-toc a:hover { color: var(--accent-hover); }
.legal-toc a.active {
  color: var(--accent);
  border-left-color: var(--accent);
  font-weight: 500;
}
.legal-section { margin-bottom: 48px; scroll-margin-top: 98px; }
.legal-section h2 {
  font-size: 1.35rem;
  font-weight: 500;
  letter-spacing: -0.3px;
  color: var(--ink-2);
}
.legal-section p { line-height: 1.55; }
.legal-section a { color: var(--accent); border-bottom: 1px solid var(--accent); }
.legal-section a:hover { color: var(--accent-hover); border-bottom-color: var(--accent-hover); }
@media (max-width: 860px) {
  .legal-page { width: auto; margin: 78px 12px 40px; }
  .legal-grid { grid-template-columns: 1fr; }
  .legal-toc { display: none; }
}

/* ============================================================
   4. /pros page + shared FAQ accordion (2026-08-14)
   pp- prefix = pro-page one-offs, same scoping convention as the
   site chrome in Section 3. .faq/.faq-item are shared - the same
   accordion renders the homeowner FAQ on the homepage.
   ============================================================ */

/* Shared eyebrow: /pros hero + homepage hero (both dark panels). */
.pp-eyebrow,
.hero-eyebrow {
  font-size: 14px;
  letter-spacing: 2px;
  text-transform: uppercase;
  color: var(--light-green);
}

.pp-hero { position: relative; z-index: 1; background: var(--ink-2); padding: 180px 0 130px; }
.pp-hero h1 { color: var(--white); }
.pp-hero-sub { color: var(--body-light); max-width: 56ch; }
.pp-hero .btn-link-accent,
.os-wall .btn-link-accent { color: var(--bg); background-color: var(--accent-hover); }
.pp-hero .btn-link-accent:hover,
.os-wall .btn-link-accent:hover { color: var(--white); background-color: var(--light-green); }

.pp-offer { position: relative; z-index: 1; background: var(--sage); padding: 130px 0; }
.pp-offer-grid { display: grid; grid-template-columns: 0.9fr 1.1fr; gap: 70px; align-items: start; }
.pp-offer-copy h2, .pp-apply h2 { color: var(--ink-2); }

/* Public vendor profile CTA band with a MacBook (Figma Device Mockups
   2:12). Frame png has a transparent screen window, so the screenshot
   sits under it; window offsets are % of the 2812x1788 frame. The
   laptop is oversized and pulled past the section's bottom edge so the
   base is cropped, same idea as the homepage phone bleed. */
.pp-profile { position: relative; z-index: 1; background: var(--ink-2); padding: 130px 0 0; overflow: hidden; }
.pp-profile h2, .pp-profile p { color: var(--white); }
.pp-profile p { max-width: 46ch; }
.pp-profile-grid { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: 60px; align-items: center; }
.pp-profile-copy { padding-bottom: 130px; }
.pp-profile-ctas { display: flex; flex-wrap: wrap; gap: 14px; }
.pp-profile-apply { color: var(--white); border: 1px solid color-mix(in srgb, var(--white) 40%, transparent); }
.pp-profile-apply:hover { color: var(--ink-2); background: var(--white); }
.pp-profile-mock { align-self: end; }
.pp-macbook { position: relative; width: 130%; aspect-ratio: 2812 / 1788; margin-bottom: -11%; }
.pp-macbook-frame { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; }
.pp-macbook-screen {
  position: absolute;
  left: 12.675%;
  top: 13.007%;
  width: 74.436%;
  height: 73.016%;
  object-fit: cover;
  object-position: top;
  background: var(--white);
}

.pp-flow { position: relative; z-index: 1; background: var(--card); padding: 130px 0; }
.pp-flow .vendor-row { display: grid; grid-template-columns: 120px 1fr; gap: 28px; align-items: center; }
.pp-flow-num {
  font-family: var(--font-heading);
  font-weight: 500;
  font-size: clamp(40px, 4vw, 56px);
  line-height: 1;
  color: var(--accent);
  text-align: center;
}


.pp-apply { position: relative; z-index: 1; background: var(--sage); padding: 130px 0; }
.pp-apply-inside { max-width: 50%; }
.pp-apply h2 { margin: 0 0 8px; }
.pp-apply-sub { margin-bottom: 40px; }

.pp-form-step { display: none; }
.pp-form-step.is-active { display: block; }
.pp-label { display: block; font-size: 14px; letter-spacing: 1px; text-transform: uppercase; color: var(--ink-3); margin-bottom: 10px; }
.pp-label-gap { margin-top: 28px; }
/* Mirrors the app's real field look (frontend/src/base.css bare-element
   input rule): 2px ink underline, 3px top radius, 48px min-height,
   focus = accent underline + soft gray fill. Values are the app's
   tokens resolved through this file's mirrored primitives. */
.pp-input {
  width: 100%;
  min-height: 48px;
  background: none;
  border: none;
  border-bottom: 2px solid var(--ink);
  border-radius: 3px 3px 0 0;
  padding: 4px 8px;
  font-size: var(--fs-link);
  color: var(--ink-2);
  transition: border-color 0.2s, background-color 0.2s;
}
.pp-input:focus { border-bottom-color: var(--accent); background: var(--gray); }
/* 50/50 first/last name row (2026-08-23, Rocco: "split name into first
   and last name 50/50 split"). min-width:0 stops flex's default
   min-width:auto from letting a long placeholder push either input
   past its 50% share. */
.pp-name-row { display: flex; gap: var(--gap-sm); }
.pp-name-row .pp-input { flex: 1 1 0; min-width: 0; }

/* City autocomplete dropdown (2026-08-18) - real Google Places
   suggestions, positioned under the triggering .pp-input. */
.pp-autocomplete-wrap { position: relative; }
.pp-autocomplete-list {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 5;
  background: var(--white);
  border: 1px solid var(--body-light);
  border-radius: 0 0 3px 3px;
  max-height: 220px;
  overflow-y: auto;
  box-shadow: 0 8px 20px rgba(0,0,0,0.08);
}
.pp-autocomplete-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  font-size: 15px;
  color: var(--ink-2);
  background: none;
  border: none;
  cursor: pointer;
}
.pp-autocomplete-item:hover, .pp-autocomplete-item.is-active { background: var(--gray); }
.pp-chips { display: flex; flex-wrap: wrap; gap: var(--gap-xs); }
.pp-chip {
  background: none;
  border: 1px solid var(--ink-3);
  border-radius: 3px;
  padding: 10px 18px;
  font-size: 15px;
  color: var(--ink-3);
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.pp-chip:hover { border-color: var(--accent); color: var(--accent); }
.pp-chip.is-selected { background: var(--accent); border-color: var(--accent); color: var(--white); }

/* Consent checkboxes on the homeowner + /pros forms (2026-08-18 -
   both forms now submit real signups, not just lead capture, so ToS/
   Privacy/Vendor Agreement acceptance is a real requirement, not
   fine print). Mirrors .pp-input's underline language loosely but as
   a checkbox row rather than a text field. */
.pp-checkbox-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin-top: 28px;
  font-size: 14px;
  color: var(--ink-3);
  cursor: pointer;
}
.pp-checkbox-row input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin-top: 2px;
  flex-shrink: 0;
  accent-color: var(--accent);
}
.pp-checkbox-row a { color: var(--ink-2); text-decoration: underline; }
.pp-checkbox-row a:hover { color: var(--accent); }

.cf-turnstile { margin-top: var(--gap-md); }

.pp-form-error { color: #bf3c34; font-size: 15px; min-height: 20px; margin-top: 20px; }
.pp-form-nav {
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-areas: "dots dots" "back next";
  row-gap: 14px;
  margin-top: 16px;
}
.pp-dots { grid-area: dots; justify-self: center; display: flex; gap: 8px; }
.pp-back { grid-area: back; justify-self: start; align-self: center; }
.pp-form-nav .btn-link-accent { grid-area: next; justify-self: end; align-self: center; }
.pp-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--body-light); }
.pp-dot.is-active { background: var(--accent); }
.pp-form-done h3 { color: var(--ink-2); margin: 0 0 14px; }
.pp-form-done p { margin: 0 0 24px; max-width: 52ch; }

/* ---- FAQ accordion (shared: /pros pro FAQ + homepage homeowner FAQ) ---- */
.faq { position: relative; z-index: 1; background: var(--card); padding: 130px 0; }
.faq h2 { margin-bottom: 40px; }
.faq-list { border-top: 1px solid color-mix(in srgb, var(--ink) 14%, transparent); }
.faq-item { border-bottom: 1px solid color-mix(in srgb, var(--ink) 14%, transparent); }
.faq-item summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--gap-sm);
  padding: 26px 0;
  font-size: var(--fs-link);
  color: var(--ink-2);
  transition: color 0.2s;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after { content: '+'; font-size: 24px; font-weight: 300; color: var(--body); transition: transform 0.25s; }
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item summary:hover { color: var(--accent-hover); }
.faq-item p { margin: 0; padding: 0 0 26px; max-width: 68ch; }

@media (max-width: 860px) {
  /* Same edge treatment as the rest of the site's mobile sections
     (.vendors, .footer both run 7% horizontal): section-
     inside goes full width down here, so each section supplies its own
     gutters - without them content sits flush at x:0 while the footer
     sits at 7%, and nothing lines up. */
  .pp-hero { padding: 140px 7% 90px; }
  .pp-offer, .pp-profile, .pp-flow, .pp-apply, .faq { padding: 15% 7%; }
  .pp-flow .vendor-row { grid-template-columns: 84px 1fr; gap: 16px; }
  .pp-apply-inside { max-width: 100%; }
  .pp-offer-grid, .pp-profile-grid { grid-template-columns: 1fr; gap: 40px; }
  .pp-profile-copy { padding-bottom: 0; }
  .pp-macbook { width: 132%; margin-left: -16%; margin-bottom: -22%; }
}


/* ============================================================
   /our-story - editorial page, v3 (2026-08-31). v2's text-only column
   left half the viewport empty; v3 interlocks each chapter's copy with
   a designed artifact built from its own content (lessons stack, front-
   door diptych, domus dictionary card, family photo) plus one full-bleed
   british-green statement wall. os- prefix, one-off chrome like pp-.
   ============================================================ */
.os-hero {
  position: relative; z-index: 1;
  background: var(--ink-2);
  display: flex; align-items: center;
  padding: 150px 0 90px;
}
.os-hero h1 { color: var(--white); font-size: clamp(52px, 7.5vw, 108px); }
.os-hero .pp-eyebrow { color: var(--light-green); }
.os-manifest {
  --stack-rule: color-mix(in srgb, var(--white) 16%, transparent);
  --stack-pad: 15px;
  margin: 0; max-width: 620px;
}
.os-manifest li { color: var(--body-light); font-size: clamp(17px, 1.9vw, 22px); letter-spacing: -0.01em; }
.os-manifest li:last-child { color: var(--light-green); border-bottom: 1px solid var(--stack-rule); }

/* Reused stacked-rule list: border-top ruled rows, weight 300. Used
   by the hero manifest, the ch1 lessons, and the ch2 door lists -
   previously three separate one-off copies of this exact pattern.
   Each context sets --stack-rule / --stack-pad and its own type size;
   this carries only what's actually identical across all three. */
.os-stack-list, .os-manifest, .os-lessons, .os-door-side ul { margin: 0; padding: 0; list-style: none; }
.os-manifest { padding-top: 3em; }
.os-manifest li, .os-lessons h3, .os-door-side li {
  margin: 0; font-weight: 300;
  border-top: 1px solid var(--stack-rule);
  padding: var(--stack-pad) 0;
}

/* Reused meta label: small caption text, shared by the door diptych
   headers and the domus part-of-speech tag - was two one-off rules
   with only their 14px size in common. */
.os-meta-label { display: block; margin: 0 0 18px; }
.os-door-side h3 { color: var(--ink-2); letter-spacing: 0.01em; }
.os-domus-pos { margin: 14px 0 20px; color: var(--light-green); font-style: italic; }

.os-chapter { position: relative; z-index: 1; background: var(--white); padding: 120px 0; }
.os-chapter--sage { background: var(--sage); }
.os-grid {
  display: grid;
  grid-template-columns: 1.05fr 0.95fr;
  gap: clamp(44px, 6.5vw, 120px);
  align-items: center;
}
.os-grid > * { min-width: 0; }
.os-grid--flip .os-grid-copy { order: 2; }
.os-grid--flip .os-grid-art { order: 1; }


/* Ch 1 artifact - the three lessons */
.os-lessons { --stack-rule: var(--light-green); --stack-pad: 15px; }
.os-lessons h3 { color: var(--ink-2); font-size: clamp(22px, 2.6vw, 34px); letter-spacing: -0.015em; line-height: 1.25; }
.os-lessons h3:last-child { border-bottom: 1px solid var(--stack-rule); }

/* Ch 2 artifact - both sides of the front door, split by the "door" rule */
.os-door { display: grid; grid-template-columns: 1fr 1fr; }
.os-door-side { padding: 6px 26px 6px 0; }
.os-door-side + .os-door-side { padding: 6px 0 6px 26px; }
.os-door-side ul { --stack-rule: color-mix(in srgb, var(--ink-2) 14%, transparent); --stack-pad: 13px; }
.os-door-side li { color: var(--ink-2); font-size: clamp(16px, 1.6vw, 19px); line-height: 1.4; }

/* Statement wall - the page's one bold moment, carries the CTAs */
.os-wall { position: relative; z-index: 1; background: var(--ink-2); padding: 140px 0; }
.os-wall .display-heading { color: var(--white); }
.os-ctas { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin-top: 48px; }
.os-ctas-group { display: flex; flex-wrap: wrap; gap: 10px; }

/* Ch 3 artifact - domus as a dictionary entry */
.os-domus { border-top: 1px solid var(--light-green); border-bottom: 1px solid var(--light-green); padding: 38px 0; }
.os-domus h3 { padding-bottom: 0.5em; }
.os-domus-word {
  display: block;
  color: var(--accent);
  font-weight: 200;
  font-size: clamp(56px, 6vw, 92px);
  letter-spacing: -0.02em;
  line-height: 1;
}

/* Ch 4 artifact - the family photo, finally in the family chapter */
.os-figure { margin: 0; position: relative; height: 100%; overflow: hidden; }
.os-figure img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 25%; }

/* Ch 4 layout: full-bleed photo band, not a boxed image in a grid
   column - photo runs edge to edge and top to bottom of its own
   section (padding:0 overrides .os-chapter's usual 120px), copy sits
   in a normal padded column beside it. */
.os-bleed { display: grid; grid-template-columns: 1fr 1fr; align-items: stretch; padding: 0; min-height: 640px; }
.os-bleed-copy { justify-content: center; padding: 80px clamp(40px, 6vw, 100px); }

@media (max-width: 860px) {
  /* Same gutter treatment as /pros: .section-inside is full width down
     here, so every os- section supplies its own 7% sides. */
  .os-hero { min-height: 0; padding: 140px 7% 90px; }
  .os-hero h1 { font-size: clamp(40px, 11vw, 52px); }
  .os-manifest { padding-top: 2em; }
  .os-chapter { padding: 15% 7%; }
  .os-grid { grid-template-columns: 1fr; gap: 40px; }
  .os-bleed { grid-template-columns: 1fr; min-height: 0; padding: 0; }
  .os-bleed-copy { order: 1; justify-content: flex-start; padding: 15% 7%; }
  .os-bleed .os-figure { order: 2; }
  .os-grid--flip .os-grid-copy { order: 1; }
  .os-grid--flip .os-grid-art { order: 2; }
  .os-door { grid-template-columns: 1fr; }
  .os-door-side { padding: 0 0 26px; }
  .os-door-side + .os-door-side { border-left: 0; border-top: 1px solid var(--light-green); padding: 26px 0 0; }
  .os-wall { padding: 18% 7%; }
  .os-domus { padding: 28px 0; }
  .os-figure { position: static; height: auto; overflow: visible; }
  .os-figure img { position: static; display: block; width: 100%; height: auto; aspect-ratio: 7 / 5; }
  .os-ctas { margin-top: 32px; }
}
