/* ============================================================
   GRASSY VIBES — "Come outside." an experience, not a template.
   The page descends from the screen-world into living grass.

   z-index ladder (do not improvise):
     -2 #atmos (sky)  ·  -1 #sun  ·  6 content (.scene-inner)
      6–7 content overlays (stamp, blanket)  ·  8 #grass canvas
      9 .grain (film grain unifies everything under it)
     10 .foot (readable over the dirt)  ·  50 .nav
   Grass intentionally sits IN FRONT of content so UI rises from
   behind the blades; overlays stay ≤7 so grain + grass cover them.
   ============================================================ */
* { box-sizing: border-box; margin: 0; padding: 0; }
/* CRITICAL: never put overflow-x on <html> (or body). On iOS Safari, `overflow` on the
   root scroller breaks `position: fixed` — the fixed grass + atmosphere layers detach and
   SCROLL AWAY with the page (the "second scroll"). Horizontal overflow is prevented at its
   SOURCE instead: nothing on the page may exceed 100vw (see .covered). */
/* ALSO CRITICAL: never put a background on <html>. The atmosphere layers (#atmos z-2, #sun z-1)
   paint BETWEEN the canvas and in-flow content — that only works while <body>'s background
   PROPAGATES to the canvas (html unset). Give html its own background and body paints #13160f
   over the negative-z layers, killing the whole warmth gradient. */
html { scroll-behavior: smooth;
  /* Stop the rubber-band bounce at both page edges. At the BOTTOM, the bounce slid the footer
     up and exposed the fixed grass fringe (z8) beneath it for a frame — a green flash behind
     the dirt. No bounce = the footer edge is the hard bottom, so the fringe can never peek out;
     the body's soil-brown is all that ever shows past the edge. */
  overscroll-behavior-y: none; }
body {
  font-family: var(--gv-body);
  color: var(--gv-ink);
  /* Propagates to the document canvas = what iOS rubber-band overscroll exposes past both page
     edges. Near-black soil-brown reads as "more dirt" below the footer and as dusk above the
     dark hero, so neither bounce flashes a foreign color. (Was #13160f dusk-green.) */
  background: #170d05;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}
img { display: block; max-width: 100%; }
em { font-style: italic; }
strong { font-weight: 700; }

/* --- Scroll-driven atmosphere: dusk screen-world (top) → golden outdoors (bottom) --- */
/* Typed custom property so the engine can interpolate it smoothly (gradients can't be
   transitioned directly — that caused Safari's jumpy background). */
@property --warmth { syntax: "<number>"; inherits: true; initial-value: 0; }
:root { --warmth: 0; transition: --warmth .28s linear; }   /* 0 at top, 1 at bottom — set by JS */
#atmos {
  position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background:
    linear-gradient(180deg,
      hsl(calc(150 - var(--warmth) * 108) calc(14% + var(--warmth) * 42%) calc(9% + var(--warmth) * 82%)) 0%,
      hsl(calc(150 - var(--warmth) * 86) calc(16% + var(--warmth) * 36%) calc(12% + var(--warmth) * 62%)) 46%,
      hsl(calc(112 - var(--warmth) * 10) calc(34% + var(--warmth) * 30%) calc(13% + var(--warmth) * 46%)) 100%);
}
#sun {
  position: fixed; left: 50%; z-index: -1; pointer-events: none;
  width: 60vmax; height: 60vmax; border-radius: 50%;
  transform: translate(-50%, calc(60vh - var(--warmth) * 78vh));
  background: radial-gradient(circle, rgba(255,224,150,calc(.0 + var(--warmth) * .7)) 0%, rgba(255,210,130,calc(var(--warmth) * .28)) 30%, transparent 62%);
  filter: blur(8px);
}
.grain {
  position: fixed; inset: 0; z-index: 9; pointer-events: none; opacity: .22; mix-blend-mode: soft-light;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.4'/%3E%3C/svg%3E");
}

/* --- Ambient grass canvas — a STATIC fixed fringe pinned to the viewport bottom --- */
/* CRITICAL (iOS): this is TOP-anchored + 100lvh (see @supports block), the SAME configuration
   #atmos/.grain use and that iOS Safari holds rock-steady during scroll/momentum/toolbar-collapse.
   It was previously bottom:0 + per-frame-redrawn, which iOS folded into the scrolling layer so it
   "scrolled away." The grass is drawn into the BOTTOM band of this full-viewport canvas; its content
   NEVER changes with scroll, so iOS keeps it composited & pinned. The DIRT is NOT here — it belongs
   to the footer, which owns it as a static background image. Grass sits in front of content (z 8 > 6);
   the footer is lifted above it (z 10) so the dirt+text rise over the fringe at the very bottom. */
#grass { position: fixed; left: 0; top: 0; bottom: auto; width: 100vw; height: 100vh; z-index: 8; pointer-events: none; display: block; }

/* --- Layout --- */
.scene-inner { width: 100%; max-width: var(--gv-maxw); margin: 0 auto; padding: 0 clamp(22px, 5vw, 56px); position: relative; z-index: 6; }
.narrow { max-width: 820px; }
.center { text-align: center; margin-left: auto; margin-right: auto; }
.scene { position: relative; min-height: 92vh; display: flex; align-items: center; padding: 12vh 0; }
.scene.hero { min-height: 100svh; padding: 0; }

/* Type — Fraunces gets its soft/wonky voice on every display use */
h1, h2, .hero-lead, .lady-says, .latt-title, .brand-word {
  font-optical-sizing: auto; font-variation-settings: 'SOFT' 60, 'WONK' 1;
}
h1, h2 { font-family: var(--gv-display); font-weight: 650; line-height: 1.05; letter-spacing: -0.012em; }
/* ONE section-title scale everywhere (Richard: all titles the same size).
   The floor lets the longest title ("Official merchandise.") fit one line at 375px,
   so grow-underlines always match their text. */
h2 { font-size: clamp(2.02rem, 6vw, 4.6rem); margin-bottom: .5em; }
.lead { font-size: clamp(1.15rem, 1.7vw, 1.5rem); max-width: 56ch; }
.eyebrow { font-family: var(--gv-mono); font-size: .76rem; letter-spacing: .2em; text-transform: uppercase; color: var(--gv-grass-deep); margin-bottom: 1.1rem; }
.eyebrow.light { color: var(--gv-warm-white); }

/* Nav */
.nav { position: fixed; top: 0; left: 0; right: 0; z-index: 50; display: flex; align-items: center; justify-content: space-between;
  padding: 18px clamp(20px, 4vw, 48px); transition: background .3s var(--gv-ease), box-shadow .3s, padding .3s; }
/* Scrolled nav = a continuation of the SKY, not a glass box: the bar's top color is the
   same warmth-driven hsl as #atmos's 0% stop, so it always matches the atmosphere behind
   it, near-opaque, dissolving through a soft gradient edge. No blur, no black glass. */
.nav.scrolled { background: transparent; padding-top: 12px; padding-bottom: 12px; }
/* ONE continuous sky fade for the whole header — not a solid bar + a separate fade (that gave
   the bar a hard bottom edge, a "line" beside the bright Touch Grass button). It's a single
   gradient in ::before that OVERHANGS the bar (150px tall, well past the ~82px bar) so the
   fade passes seamlessly through and below the button. Held near-opaque through the text band
   (0–40%) so the links stay legible, then eased to alpha 0. z-index:-1 keeps it behind the
   nav's links/brand/CTA (which render at auto z in the nav's own stacking context). */
.nav.scrolled::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 150px; z-index: -1; pointer-events: none;
  background: linear-gradient(180deg,
    hsl(calc(150 - var(--warmth) * 107) calc(15% + var(--warmth) * 41%) calc(10% + var(--warmth) * 80%) / .97) 0%,
    hsl(calc(150 - var(--warmth) * 107) calc(15% + var(--warmth) * 41%) calc(10% + var(--warmth) * 80%) / .92) 40%,
    hsl(calc(150 - var(--warmth) * 106) calc(15% + var(--warmth) * 41%) calc(10% + var(--warmth) * 79%) / .52) 58%,
    hsl(calc(150 - var(--warmth) * 105) calc(15% + var(--warmth) * 40%) calc(11% + var(--warmth) * 78%) / .24) 76%,
    hsl(calc(150 - var(--warmth) * 104) calc(15% + var(--warmth) * 40%) calc(11% + var(--warmth) * 77%) / .07) 90%,
    hsl(calc(150 - var(--warmth) * 104) calc(15% + var(--warmth) * 40%) calc(11% + var(--warmth) * 77%) / 0) 100%);
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; color: var(--gv-warm-white); }
.brand-mark { width: 28px; height: 25px; display: block; overflow: visible; flex: none;
  filter: drop-shadow(0 1px 4px rgba(20,40,20,.4)); }
.brand-word { font-family: var(--gv-display); font-weight: 700; font-size: 1.2rem; color: var(--gv-warm-white); text-shadow: 0 1px 9px rgba(0,0,0,.35); }
.nav-links { display: flex; align-items: center; gap: clamp(16px, 2.4vw, 30px); }
.nav-links a { text-decoration: none; color: var(--gv-warm-white); font-weight: 500; opacity: .9; text-shadow: 0 1px 9px rgba(0,0,0,.3); transition: opacity .2s; }
.nav-links a:not(.nav-cta) { padding: 7px 14px; background-size: 100% 100%; background-repeat: no-repeat; }
@media (hover: hover) { .nav-links a:hover { opacity: 1; } }
/* Nav text is BINARY — warm cream on the dark half, ink on the golden half; never gray.
   JS flips .nav-dark once the bar crosses mid-lightness (with hysteresis so it can't
   flicker at the boundary), and the transition makes the flip a smooth cross-fade. */
.nav-links a:not(.nav-cta), .brand-word {
  transition: color .5s var(--gv-ease), text-shadow .5s var(--gv-ease), opacity .2s;
}
.nav.nav-dark .nav-links a:not(.nav-cta), .nav.nav-dark .brand-word {
  color: #14231a; text-shadow: 0 1px 9px rgba(0,0,0,0);
}
.nav-cta { padding: 10px 20px; background: transparent; text-shadow: none !important;
  filter: drop-shadow(0 4px 10px rgba(31,110,60,.4)); color: var(--gv-warm-white) !important; }

/* HERO — the indoor screen-world */
.hero-frame { position: absolute; inset: 0; background-size: cover; background-position: center 38%; z-index: 0;
  background-image: url('assets/hero-duo.jpg?v=2'); }
/* phones get the 9:16 hero regenerated from the ORIGINAL with all the new space ABOVE
   their heads — bottom-anchored so the duo always stays in frame and the text floats
   up into the empty ceiling/wall headroom */
@media (max-width: 700px) {
  .hero-frame { background-image: url('assets/hero-duo-tall.jpg'); background-position: 46% 100%; }
  .hero-inner { margin-bottom: 26vh; }
}
.hero-frame-scrim { position: absolute; inset: 0; z-index: 1;
  background: linear-gradient(180deg, rgba(15,17,12,.5) 0%, rgba(15,17,12,.2) 32%, rgba(15,17,12,.5) 70%, #13160f 100%); }
/* Hero text hugs the left negative space of the image, tight vertical rhythm */
.hero .scene-inner { max-width: 100%; padding-right: 0; padding-left: clamp(22px, 4.4vw, 60px); }
.hero-inner { color: var(--gv-warm-white); display: flex; flex-direction: column; align-items: flex-start; max-width: min(58%, 720px); }
.hero .eyebrow { margin: 0 0 1.1rem; }
/* the brand line leads the page now — bigger, brighter, unmissable */
.hero-brand-line { font-size: clamp(.95rem, 1.5vw, 1.25rem); letter-spacing: .3em; color: var(--gv-sprout) !important; }
.hero-title { font-size: clamp(2.9rem, 8.5vw, 6.6rem); color: var(--gv-warm-white); text-shadow: 0 3px 34px rgba(0,0,0,.5); margin: 0; }
.hero-lead { font-family: var(--gv-display); font-weight: 600; font-size: clamp(1.4rem, 2.6vw, 2.1rem); color: var(--gv-warm-white); margin: 1.5rem 0 0; text-shadow: 0 2px 20px rgba(0,0,0,.5); }
@media (max-width: 820px) { .hero-inner { max-width: 100%; } }
.said-by { font-family: var(--gv-mono); font-weight: 400; font-size: .9rem; color: var(--gv-sprout); vertical-align: middle; letter-spacing: .04em; }
/* per-section attribution colours (from editor) */
.hero .said-by { color: #abe065; }
.outside .said-by { color: #2e3b29; }
/* lifted clear of the grass fringe (which rises ~117px up the viewport) so the label + arrow read */
.scroll-cue { position: absolute; bottom: clamp(132px, 17vh, 165px); left: 50%; transform: translateX(-50%); z-index: 6;
  display: inline-flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer;
  background: none; border: none; color: var(--gv-warm-white); font-family: var(--gv-mono); font-size: .72rem; letter-spacing: .22em; text-transform: uppercase; opacity: .85; }
.cue-arrow { font-size: 1.2rem; animation: bob 2.4s var(--gv-ease) infinite; }
@keyframes bob { 0%,100%{ transform: translateY(0);} 50%{ transform: translateY(7px);} }

/* IDEA */
.idea h2 { color: var(--gv-warm-white); }
.idea .lead { color: rgba(244,241,229,.92); margin-top: .4em; }
/* "THE IDEA" label sits on the dark early-scroll atmosphere — light lime stands out (vs the dark-green default) */
.idea .eyebrow { color: var(--gv-sprout); }

/* "Grow underline" — a hand-drawn squiggle (vector SVG → crisp) that sprouts under headings */
.grow-underline { display: inline-block; position: relative; }
.grow-underline::after { content: ""; position: absolute; left: 0; bottom: -.26em; height: .42em; width: 100%;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 16' preserveAspectRatio='none'%3E%3Cpath d='M3,10 C25,4 45,13 70,8 C95,3 120,13 150,8 C172,5 190,11 197,9' fill='none' stroke='%232FA84F' stroke-width='5' stroke-linecap='round'/%3E%3C/svg%3E") left center / 100% 100% no-repeat;
  transform: scaleX(0); transform-origin: left; transition: transform .8s var(--gv-ease); }
.grow-underline.in::after { transform: scaleX(1); }
/* Meet-the-boss sits on a green atmosphere — green-on-green washed the underline out.
   A dark forest green keeps it monochromatic with the rest of the site but high-contrast.
   The quote's left rule uses the same colour (see .lady .lady-says below). */
.next .grow-underline::after { background-image:
  url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 200 16' preserveAspectRatio='none'%3E%3Cpath d='M3,10 C25,4 45,13 70,8 C95,3 120,13 150,8 C172,5 190,11 197,9' fill='none' stroke='%230d3a1e' stroke-width='5.5' stroke-linecap='round'/%3E%3C/svg%3E"); }
/* Size the idea heading to its content so the squiggle tracks the text instead of the container */
.idea .grow-underline { width: max-content; max-width: 100%; }

/* MEET LADY + SHOW plates */
/* FOUNDER — corporate headshot block. The section sits on the still-dark early atmosphere,
   so it reads in LIGHT ink (Richard: the dark-on-green text didn't stand out). */
.founder-head { display: grid; grid-template-columns: minmax(230px, 350px) 1fr;
  gap: clamp(26px, 4.5vw, 60px); align-items: center; }
.headshot { max-width: 350px; }
/* the bar shot cropped to a headshot — keep her face + the martini in frame */
.headshot img { object-position: 46% 28%; }
.founder-quote { margin-top: 2rem; max-width: 34ch; }
.lady h2 { color: var(--gv-warm-white); }
.lady .eyebrow { color: var(--gv-sprout); font-size: .85rem; letter-spacing: .1em; }
.lady .lead { color: rgba(244,241,229,.9); }
.lady .lady-says { color: #e9e4d0; border-left-color: var(--gv-grass); }
.next h2, .outside h2 { color: var(--gv-ink-on-green); }
/* higher-contrast accents on the green mid-page atmosphere (green-on-green was washing out) */
.next .eyebrow { color: var(--gv-accent-on-green); }
.next .lead, .outside .lead { color: #2e3a28; }
.plate { position: relative; border-radius: 14px 18px 12px 20px; overflow: hidden; filter: none;
  box-shadow: 0 22px 60px rgba(10,20,8,.4); background: #0c0e09; }
.plate img { width: 100%; height: 100%; object-fit: cover; }
.plate.portrait { aspect-ratio: 4/5; }
.plate.wide { aspect-ratio: 16/9; margin: 2.2rem auto 1.6rem; max-width: 880px; }
.show .latt-title { margin: .1em 0 .5em; }

/* THE FLAGSHIP straddles the dark->light boundary: when you first scroll to it the
   atmosphere is still dusk-dark, so the text STARTS warm cream — and cross-fades to
   ink the moment the sky crosses mid-lightness (:root.lit, same signal + hysteresis
   as the nav). Readable at every scroll position, never gray. */
.show .eyebrow { color: var(--gv-sprout); }
.show h2, .show .latt-title { color: var(--gv-warm-white); }
.show .lead { color: rgba(244, 241, 229, .92); }
.show .lady-says { color: #e9e4d0; }
.show .eyebrow, .show h2, .show .latt-title, .show .lead, .show .lady-says {
  transition: color .5s var(--gv-ease);
}
:root.lit .show .eyebrow { color: var(--gv-accent-on-green); }
:root.lit .show h2, :root.lit .show .latt-title { color: var(--gv-ink-on-green); }
:root.lit .show .lead { color: #2e3a28; }
:root.lit .show .lady-says { color: var(--gv-accent-on-green); }

/* "IN DEVELOPMENT" — Lady inked this herself, slightly crooked. No backing patch:
   just dark saturated ink stamped straight on the photo.
   · .stamp     positioning + rotation + reveal
   · .stamp-ink rough border (gvRough) + text + paw, distressed by the fractalNoise
                mask so border and letters break together. */
.stamp {
  position: absolute; top: 7%; right: 5%; z-index: 7;
  color: #8c1d06;   /* dark, saturated stamp red — carries on its own, no backing */
  font-weight: 800; font-size: clamp(.8rem, 1.9vw, 1.15rem);
  letter-spacing: .13em; text-transform: uppercase; white-space: nowrap;
  pointer-events: none;
}
@media (max-width: 640px) { .stamp { font-size: .62rem; top: 6%; right: 4%; } }
.stamp-ink {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: .55em;
  padding: .8em 1.1em;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='160'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3' seed='11'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 2.6 -0.25'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23d)'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='160'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3' seed='11'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 2.6 -0.25'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23d)'/%3E%3C/svg%3E");
  -webkit-mask-size: 240px 160px; mask-size: 240px 160px;
}
.stamp-paw { width: 1.25em; height: auto; flex: none; }
/* thump-in: lands like a real stamp (overrides the generic translateY reveal) */
.stamp[data-reveal] { opacity: 0; transform: rotate(-2deg) scale(1.55); transition: opacity .16s ease-out, transform .45s var(--gv-ease); }
.stamp[data-reveal].in { opacity: 1; transform: rotate(-7deg) scale(1); }

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

/* Lady's voice — handwritten-feeling pull quote */
.lady-says { font-family: var(--gv-display); font-weight: 600; font-style: italic; font-size: clamp(1.3rem, 2.4vw, 1.9rem);
  line-height: 1.25; color: var(--gv-grass-deep); margin-top: 1.6rem; padding-left: 1rem; border-left: 3px solid var(--gv-grass);
  filter: none; }
.lady-says.center { border-left: none; padding-left: 0; max-width: none; margin: 2rem auto 0; }

/* UNANNOUNCED — the blanket. Something tall and vertical under a cloth Lady arranged
   herself. Breathes almost imperceptibly; a hover/tap tugs it but reveals nothing. */
.covered { position: relative; max-width: 800px; margin: 2.4rem auto 0; }
/* phones: the blanket breaks past the text column to feel BIG — via negative margins that
   cancel the section padding down to a 10px viewport gutter. Stays within 100vw (no
   horizontal overflow → no need for the overflow-x that would break iOS fixed layers). */
@media (max-width: 640px) {
  .covered { max-width: none; margin: 2.4rem calc(-1 * (clamp(22px, 5vw, 56px) - 10px)) 0; }
}
.drape { display: block; width: 100%; height: auto;
  filter: drop-shadow(0 22px 44px rgba(10,20,8,.3));
  animation: drape-breathe 7s var(--gv-ease) infinite;
  transition: transform .45s var(--gv-ease); transform-origin: 50% 90%; }
@keyframes drape-breathe { 0%,100% { transform: translateY(0) rotate(0deg); } 50% { transform: translateY(-3px) rotate(.2deg); } }
@media (hover: hover) { .covered:hover .drape { transform: translateY(-7px) rotate(-.5deg); } }
/* tap tug: iOS won't :active a plain div, so JS toggles .tug on touchstart */
.covered:active .drape, .covered.tug .drape { transform: translateY(-7px) rotate(-.5deg); }
/* Lady's note, pinned to the cloth */
.peek-note { position: absolute; left: 31%; top: 27%; transform: rotate(4deg);
  padding: 10px 16px; color: var(--gv-grass-deep);
  font-family: var(--gv-display); font-style: italic; font-weight: 500; font-size: clamp(.95rem, 1.9vw, 1.25rem);
  font-variation-settings: 'SOFT' 80, 'WONK' 1; white-space: nowrap; }
.peek-note::before { content: ""; position: absolute; top: -6px; left: 50%; width: 11px; height: 11px;
  border-radius: 50%; background: radial-gradient(circle at 35% 35%, #f2937a, var(--gv-clay) 60%, #a04a32);
  box-shadow: 0 2px 4px rgba(20,10,5,.4); }
.peek-note .peek-sig { font-family: var(--gv-mono); font-style: normal; font-size: .72em; letter-spacing: .08em; margin-left: .4em; }
@media (hover: hover) { .covered:hover .peek-note { transform: rotate(2.4deg) translateY(-7px); transition: transform .45s var(--gv-ease); } }
.covered:active .peek-note, .covered.tug .peek-note { transform: rotate(2.4deg) translateY(-7px); transition: transform .45s var(--gv-ease); }

/* THE STORE — a calm scroll-snap carousel of paper product tags */
.store .eyebrow { color: var(--gv-accent-on-green); }
/* squiggle hugs the words, never the container (matches the thesis-title fix) */
.store .grow-underline, .next .grow-underline { width: max-content; max-width: 100%; }
.store h2 { color: var(--gv-ink-on-green); }
.store .lead { color: #2e3a28; }
.carousel { position: relative; margin-top: 2.4rem; }
.car-track { display: flex; gap: 18px; overflow-x: auto; scroll-snap-type: x mandatory;
  padding: 12px 8px 26px; list-style: none; overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.car-track::-webkit-scrollbar { display: none; }
.store-card { flex: 0 0 var(--card-w, min(64vw, 252px)); scroll-snap-align: center; position: relative;
  padding: 14px 14px 16px; text-align: center; }
.card-media { border-radius: 10px 14px 9px 15px; overflow: hidden; aspect-ratio: 4/5; background: #efe7d6; }
.card-media img { width: 100%; height: 100%; object-fit: cover; }
.card-name { font-family: var(--gv-display); font-weight: 600; font-size: 1.02rem; margin-top: 12px;
  color: #2a3524; font-variation-settings: 'SOFT' 60, 'WONK' 1; }
/* SOON — a tiny sibling of the IN DEVELOPMENT stamp: same clay ink, rough border
   (gvRough spec in app.js), same fractalNoise distress, crooked. */
.soon-chip { position: absolute; top: 6px; right: 4px; z-index: 2; transform: rotate(7deg);
  font-weight: 800; font-size: .78rem; letter-spacing: .16em; text-transform: uppercase;
  color: #cf5637; padding: 6px 11px; mix-blend-mode: multiply;
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='160'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3' seed='4'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 2.6 -0.25'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23d)'/%3E%3C/svg%3E");
          mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='160'%3E%3Cfilter id='d'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.5' numOctaves='3' seed='4'/%3E%3CfeColorMatrix type='matrix' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 2.6 -0.25'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23d)'/%3E%3C/svg%3E");
  -webkit-mask-size: 140px 90px; mask-size: 140px 90px; }
.car-arrow { position: absolute; top: 50%; transform: translateY(-50%); z-index: 7;
  width: 48px; height: 48px; border: none; background: transparent; cursor: pointer; padding: 10px;
  color: var(--gv-grass-deep); filter: drop-shadow(0 4px 9px rgba(15,45,20,.25));
  transition: transform .15s var(--gv-ease), color .2s; }
.car-arrow.prev { left: -10px; } .car-arrow.next { right: -10px; }
.car-arrow svg { width: 100%; height: 100%; display: block; }
@media (hover: hover) {
  .car-arrow:hover { color: #16623a; transform: translateY(-50%) scale(1.12); }
}
.car-arrow:active { transform: translateY(-50%) scale(.94); }
/* at the edges an arrow has nowhere to go — it fades and stops being a button */
.car-arrow:disabled { opacity: .3; cursor: default; pointer-events: none; }
/* cards drift in one after another */
.car-track .store-card[data-reveal] { transition-delay: calc(var(--i, 0) * 90ms); }
@media (max-width: 640px) { .car-arrow { display: none; } }  /* touch scrolls the track itself */
/* the form reads top-down everywhere: email → message → Stay connected LAST */
.capture input:not(.hp) { flex: 1 1 100%; min-width: 0; order: 1; }
.capture .msg-wrap { order: 2; }
.capture .btn-grass { order: 3; margin-top: 2px; }
@media (max-width: 640px) { .capture .btn-grass { flex: 1 1 100%; } }

/* OUTSIDE — payoff in the grass */
.outside { min-height: 84vh; padding: 9vh 0 22vh; align-items: center; text-align: center; }
/* phones: the payoff section ends well above the tall-lawn zone so the form is never
   buried behind blades (the lawn is also much shorter on phones — see app.js) */
@media (max-width: 640px) { .outside { min-height: 70vh; padding-bottom: 36vh; } }
.outside .big { color: #1a2214; line-height: 1.05; margin-bottom: .5em; }
.outside .lead { margin-bottom: 0; max-width: 52ch; }

/* JOIN — a compact email grab between the hero and the thesis. Sits on the still-dark
   early-scroll atmosphere (like .idea), so its text reads in light ink. Short band by design:
   you drop your email, then scroll on into the thesis and the rest of the site. */
/* asymmetric padding nudges the whole block DOWN the page (more headroom above) and tightens
   the gap to the thesis below it */
.join { min-height: auto; padding: clamp(112px, 20vh, 220px) 0 clamp(44px, 7vh, 84px); text-align: center; position: relative; }
/* Bridge the hero's dark bottom into the join's atmosphere: starts on the SAME #13160f the
   hero scrim ends on (so the seam is invisible) and dissolves to reveal the green gradient —
   kills the hard horizontal line under the hero. */
.join::before { content: ""; position: absolute; top: 0; left: 0; right: 0; height: clamp(160px, 26vh, 260px); z-index: 0; pointer-events: none;
  background: linear-gradient(180deg, #13160f 0%, rgba(19,22,15,.72) 34%, rgba(19,22,15,.32) 66%, rgba(19,22,15,0) 100%); }
.join .join-title { color: var(--gv-warm-white); font-size: clamp(1.9rem, 4.6vw, 3rem); line-height: 1.04; margin: 0; }
.join .join-sub { font-family: var(--gv-body); color: rgba(244,241,229,.82); font-size: clamp(1rem, 1.5vw, 1.15rem); margin: .7rem auto 0; }
/* one tidy row (email grows, Join beside it); stacks under 480px */
.join .capture { display: flex; width: 100%; max-width: 520px; flex-wrap: wrap; margin: 1.7rem auto .4rem; }
.join .capture input:not(.hp) { flex: 1 1 260px; order: 1; }
.join .capture .btn-grass { flex: 0 0 auto; order: 2; margin-top: 0; }
@media (max-width: 480px) { .join .capture .btn-grass { flex: 1 1 100%; } }
/* note reads light here (the shared .capture-note is dark for the green outside section) */
.join .capture-note { color: rgba(244,241,229,.85); min-height: 1.2em; }
.join .capture-note.ok { color: var(--gv-sprout); }

/* Capture — organic grass button */
.capture { display: inline-flex; gap: 12px; flex-wrap: wrap; justify-content: center; margin: 1.5rem auto .8rem; }
.capture input { flex: 1 1 240px; min-width: 240px; padding: 16px 22px; border: none; outline: none;
  font: inherit; font-size: 1rem; background: transparent; color: var(--gv-ink); }
.capture input::placeholder { color: #6b7a63; }
.capture input.focused { filter: drop-shadow(0 2px 9px rgba(47,168,79,.45)); }
/* honeypot — off-screen, never interactive for humans */
.capture .hp { position: absolute; left: -9999px; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
/* optional message: an always-visible paper textarea matching the input */
.msg-wrap { flex-basis: 100%; margin-top: 4px; }
.capture textarea { width: 100%; padding: 14px 18px; border: none; outline: none;
  font: inherit; font-size: 1rem; background: transparent; color: var(--gv-ink);
  resize: vertical; min-height: 88px; display: block; }
.capture textarea::placeholder { color: #6b7a63; }
.capture textarea.focused { filter: drop-shadow(0 2px 9px rgba(47,168,79,.45)); }
.btn-grass { padding: 16px 30px; border: none; font: inherit; font-weight: 700; color: #0f2417; cursor: pointer;
  background: transparent; filter: drop-shadow(0 5px 12px rgba(31,110,60,.36));
  transition: transform .15s var(--gv-ease), filter .2s; }
@media (hover: hover) { .btn-grass:hover { transform: translateY(-2px); filter: drop-shadow(0 9px 18px rgba(31,110,60,.5)); } }
.capture-note { font-size: .95rem; color: #2e3a28; opacity: .85; }
.capture-note.ok { color: var(--gv-grass-deep); font-weight: 600; opacity: 1; }

/* Footer */
/* iOS-PROOF GROUND (v3 — keep it this dumb): the dirt cross-section + grass crest is ONE static
   image (assets/ground.webp, pre-rendered from the original canvas art) painted as a plain CSS
   background on an ordinary in-flow footer. No canvas, no JS, no observers: nothing can clear,
   re-randomize, or resize it mid-scroll (the old live canvas redrew itself when the iOS toolbar
   collapsed at the page bottom — the "refresh flash" — and its safe-area padding changed the
   footer's height right at the scroll limit — the "can't reach the bottom" cutoff).
   FIXED height (border-box) so the document length NEVER moves with toolbar/safe-area state.
   Image is 1800×370 CSS px: top 130px = transparent grass crest, lower 240px = dirt.
   `cover` + bottom-center keeps the soil line at y≈130 at any viewport ≤1800px (center-crop). */
.foot { position: relative; z-index: 10; height: 370px;
  padding: 130px clamp(22px, 5vw, 56px) 12px;
  display: flex; flex-direction: column; justify-content: center; gap: 18px;
  /* NO background-color here: the crest zone (top ~130px) must stay TRANSPARENT so the
     atmosphere + the fixed fringe show through — a solid color puts a black slab behind the
     grass. This gradient is only the image-missing fallback, starting at 160px, safely below
     the wavy soil line's deepest dip (~156px). */
  background: linear-gradient(180deg, rgba(29,17,8,0) 160px, #1d1108 160px); }
/* the swaying blades: painted by app.js footSway() BEHIND the dirt (z0 < z1), rooted at
   y=175 so the dirt image's wavy soil line masks their bases. Fixed 230px tall. */
.foot-sway { position: absolute; left: 0; top: 0; width: 100%; height: 230px; z-index: 0; pointer-events: none; }
/* the dirt cross-section (soil line, roots, pebbles — NO blades): a static image overlay.
   ::after (not the footer's own background) so it stacks ABOVE the sway canvas. */
.foot::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: url('assets/ground-dirt.webp') center bottom / cover no-repeat; }
.foot-inner { position: relative; z-index: 2; }
.foot-links a, .foot-copy, .foot .brand-word { text-shadow: 0 1px 5px rgba(0,0,0,.6); }
/* brand left · links truly centered · copyright right */
.foot-inner { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 14px 28px; }
.foot-brand { display: flex; align-items: center; gap: 9px; text-decoration: none; justify-self: start; }
.foot .brand-word { color: #f3ebd9; text-shadow: 0 1px 6px rgba(0,0,0,.4); }
/* wrap BETWEEN links, never inside one (375px was folding "The show" onto two lines) */
.foot-links { display: flex; flex-wrap: wrap; justify-content: center; gap: 2px clamp(16px, 2.4vw, 28px); justify-self: center; }
.foot-links a { white-space: nowrap; }
.foot-links a { color: #e6dac2; text-decoration: none; font-weight: 500; font-size: .95rem; opacity: .82; transition: opacity .2s; padding: 6px 12px; background-size: 100% 100%; background-repeat: no-repeat; }
@media (hover: hover) { .foot-links a:hover { opacity: 1; } }
.foot-copy { color: #d6c9b0; font-weight: 500; font-size: .92rem; opacity: .85; justify-self: end; text-align: right; }
/* the colophon line is long — go single-column earlier so it never wraps in a squeezed side column */
@media (max-width: 900px) { .foot-inner { grid-template-columns: 1fr; justify-items: center; }
  .foot-brand, .foot-copy { justify-self: center; text-align: center; } }

/* Reveals */
[data-reveal] { opacity: 0; transform: translateY(24px); transition: opacity .9s var(--gv-ease), transform .9s var(--gv-ease); }
[data-reveal].in { opacity: 1; transform: none; }

/* Keyboard affordance — hover effects are pointer-only; this is the keyboard equivalent */
a:focus-visible, button:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--gv-sprout); outline-offset: 3px; border-radius: 8px;
}
/* Touch feedback — hover never fires on iOS, so taps get a press state instead */
.nav-links a:active, .foot-links a:active { opacity: .55; }
.btn-grass:active, .nav-cta:active { transform: translateY(1px) scale(.97); }
.scroll-cue:active { opacity: .5; }

/* Responsive — the nav COMPACTS on phones instead of losing its links
   (with a store on the page, hidden links are a dead end) */
@media (max-width: 820px) {
  .founder-head { grid-template-columns: 1fr; }
  .headshot { max-width: 300px; margin: 0 auto; }
  .nav { padding-left: 14px; padding-right: 14px; }
  .nav-links { gap: 4px; }
  .nav-links a:not(.nav-cta) { padding: 11px 9px; font-size: .88rem; }
  .nav-cta { padding: 10px 14px; font-size: .92rem; }
}
/* the burger + its menu */
.nav-burger { display: none; width: 44px; height: 44px; padding: 11px; margin-left: 4px;
  background: none; border: none; cursor: pointer; color: var(--gv-warm-white);
  transition: color .5s var(--gv-ease); }
.nav.nav-dark .nav-burger { color: #14231a; }
.nav-burger svg { width: 100%; height: 100%; display: block; filter: drop-shadow(0 1px 4px rgba(0,0,0,.25)); }
.nav-burger:active { opacity: .55; }
/* panel tucks UNDER the bar (z -1 inside the header's stacking context) and slides open */
.nav-menu { position: fixed; top: 0; left: 0; right: 0; z-index: -1;
  padding: calc(76px + env(safe-area-inset-top)) max(26px, env(safe-area-inset-left)) 26px;
  display: flex; flex-direction: column; align-items: flex-start;
  background: hsl(calc(150 - var(--warmth) * 108) calc(14% + var(--warmth) * 42%) calc(10% + var(--warmth) * 80%) / .97);
  opacity: 0; transform: translateY(-10px); pointer-events: none;
  transition: opacity .28s var(--gv-ease), transform .28s var(--gv-ease); }
/* the panel dissolves at its bottom edge — same eased fade as the bar, no hard line */
.nav-menu::after {
  content: ""; position: absolute; left: 0; right: 0; top: 100%; height: 48px; pointer-events: none;
  background: linear-gradient(180deg,
    hsl(calc(150 - var(--warmth) * 108) calc(14% + var(--warmth) * 42%) calc(10% + var(--warmth) * 80%) / .97) 0%,
    hsl(calc(150 - var(--warmth) * 107) calc(15% + var(--warmth) * 41%) calc(10% + var(--warmth) * 79%) / .6) 38%,
    hsl(calc(150 - var(--warmth) * 106) calc(15% + var(--warmth) * 41%) calc(10% + var(--warmth) * 78%) / .26) 68%,
    hsl(calc(150 - var(--warmth) * 105) calc(15% + var(--warmth) * 40%) calc(11% + var(--warmth) * 77%) / .07) 88%,
    hsl(calc(150 - var(--warmth) * 104) calc(15% + var(--warmth) * 40%) calc(11% + var(--warmth) * 77%) / 0) 100%);
}
.nav-menu.open { opacity: 1; transform: none; pointer-events: auto; }
.nav-menu a { text-decoration: none; color: var(--gv-warm-white);
  font-family: var(--gv-display); font-weight: 600; font-size: 1.4rem;
  font-variation-settings: 'SOFT' 60, 'WONK' 1;
  padding: 13px 4px; transition: color .5s var(--gv-ease); }
.nav-menu a:active { opacity: .55; }
.nav.nav-dark .nav-menu a { color: #14231a; }
/* the menu's Touch Grass — same rough pill as the header CTA, recolored deep forest
   with a sprout outline so it reads on the sky panel at ANY warmth */
.nav-menu .menu-cta { margin-top: 14px; padding: 12px 24px; font-family: var(--gv-body);
  font-weight: 600; font-size: 1.1rem; color: var(--gv-warm-white);
  background-size: 100% 100%; background-repeat: no-repeat;
  filter: drop-shadow(0 4px 10px rgba(15,45,25,.3)); }
.nav.nav-dark .nav-menu .menu-cta { color: var(--gv-warm-white); }  /* never flips to ink */

/* phones: page links AND the CTA move into the burger menu; the bar is brand + burger */
@media (max-width: 640px) {
  .nav-links a { display: none; }
  .nav-burger { display: block; }
}
@media (min-width: 641px) { .nav-menu { display: none; } }

/* Notch / home-indicator phones. Horizontal insets matter in LANDSCAPE (44–59px notch):
   max() keeps the design constants everywhere else (insets are 0 on desktop + portrait). */
@supports (padding: env(safe-area-inset-top)) {
  .nav { padding-top: calc(18px + env(safe-area-inset-top));
    padding-left: max(clamp(20px, 4vw, 48px), env(safe-area-inset-left));
    padding-right: max(clamp(20px, 4vw, 48px), env(safe-area-inset-right)); }
  .nav.scrolled { padding-top: calc(12px + env(safe-area-inset-top)); }
  /* footer height is FIXED (370px border-box), so this inset only nudges the text up off the
     home indicator — the box, and therefore the document length, never changes size. */
  .foot { padding-bottom: calc(12px + env(safe-area-inset-bottom));
    padding-left: max(clamp(22px, 5vw, 56px), env(safe-area-inset-left));
    padding-right: max(clamp(22px, 5vw, 56px), env(safe-area-inset-right)); }
  .scene-inner { padding-left: max(clamp(22px, 5vw, 56px), env(safe-area-inset-left));
    padding-right: max(clamp(22px, 5vw, 56px), env(safe-area-inset-right)); }
  .hero .scene-inner { padding-left: max(clamp(22px, 4.4vw, 60px), env(safe-area-inset-left));
    padding-right: env(safe-area-inset-right); }
  @media (max-width: 820px) {
    .nav { padding-left: max(14px, env(safe-area-inset-left));
      padding-right: max(14px, env(safe-area-inset-right)); }
  }
}
/* iOS toolbar collapse grows the viewport a frame before fixed layers resize — size the
   backdrops to the LARGE viewport so no dark strip can flash at the bottom edge. */
@supports (height: 100lvh) {
  /* #grass joins the backdrops here: the proven top:0 + 100lvh anchor iOS keeps pinned during
     scroll/momentum/toolbar-collapse. Its fringe draws at the large-viewport bottom, so no gap. */
  #atmos, .grain, #grass { height: 100lvh; bottom: auto; }
}
/* iOS only: full-page soft-light blending re-composites the whole backdrop every scroll
   frame (jank). Plain low-opacity grain looks near-identical and scrolls clean. */
@supports (-webkit-touch-callout: none) {
  .grain { mix-blend-mode: normal; opacity: .09; }
}
@media (prefers-reduced-motion: reduce) { html{scroll-behavior:auto} *{animation:none!important} [data-reveal]{opacity:1;transform:none} .grow-underline::after{transform:scaleX(1)}
  /* the stamp keeps its crooked resting pose (no thump, but never un-rotated) */
  .stamp, .stamp[data-reveal] { opacity: 1; transform: rotate(-7deg); transition: none; } }
