/* ============================================================
   Garden Gnome Harvest Tracker — marketing site
   Pixel-clone pipeline: clone-workspace/garden-gnome-dropset/

   Layout/spacing/type-scale/breakpoints measured directly from
   getdropset.app's real, refetched Webflow CSS (see
   02-extraction/all-styles-webflow.css + 03-design-spec/DESIGN.md):
     - page bg #0A0A0D-role / cards #222226-role, near-black + one flat
       card tone, ZERO box-shadows anywhere (depth = layering, not shadow)
     - kinetic section headlines: 80px/700/100% line-height desktop,
       40px mobile
     - section subtitles: 32px/600/100% desktop, 18px mobile
     - body copy: 26px/400/140% desktop, 22px mobile
     - container: max-width 1240px, 32px/16px inline padding, 128px/80px
       vertical rhythm between sections
     - measured breakpoints: (min-width:768px) / max-width:991px/767px/479px

   Every color/font/radius/motion TOKEN below is copied VERBATIM from
   Garden Gnome's real app (src/assets/styles.css, [data-theme="default"])
   per the design-spec's explicit instruction — Dropset's own hex values
   and Eina03 font are never reused, only its measured layout system.
   ============================================================ */

@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Space+Grotesk:wght@500;700&display=swap');

:root {
  /* ---- copied verbatim from src/assets/styles.css [data-theme="default"] ---- */
  --bg: #0a0f0a;
  --surface: #1a1c18;
  --surface-hover: #232720;
  --surface-input: #2d3129;
  --border: #2a332a;

  --accent: #5cbd4d;
  --accent-hover: #52e84d;
  --accent-soft: rgba(61, 220, 114, 0.1);
  --accent-contrast: #04140a;

  --font-heading: 'Space Grotesk', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-default: 'Inter', 'Helvetica Neue', Helvetica, Arial, system-ui, sans-serif;
  --text: #f2f7f0;
  --text-muted: #a9b8a6;
  --text-dim: #687365;
  --text-accent: #35b866;

  --radius: 8px;
  --radius-pill: 999px;
  --feature-grad: linear-gradient(135deg, #1c241b, #10160f);
  --nav-bg: #0d120c;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in: cubic-bezier(0.7, 0, 0.84, 0);
  --dur-fast: 150ms;
  --dur-base: 220ms;
  --dur-slow: 320ms;

  /* ---- measured from Dropset's real CSS (all-styles-webflow.css) ---- */
  --nav-h: 72px;
  --container-narrow:768px;
  --container-full: 1280px;
  --container-max: 1440px;
  --container-pad: 32px;
  --rhythm: 128px;
  --rhythm-double: 256px;
}

@media (max-width: 767px) {
  :root {
    --container-pad: 16px;
    --rhythm: 80px;
  }
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text-muted);
  font-family: var(--font-default);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--font-heading); font-weight: 700; margin: 0; color: var(--text); }
p { margin: 0; }
a { color: inherit; text-decoration: none; cursor: pointer; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: var(--accent-contrast); padding: 0.75rem 1.25rem; border-radius: var(--radius);
}
.skip-link:focus { left: 1rem; top: 1rem; }

.container { max-width: var(--container-full); margin: 0 auto; padding: 0 var(--container-pad); }
.container--narrow { max-width:var(--container-narrow); }
/* ---------------------------------------------------------- NAV
   72px height (measured), sticky, blurred glass — garden_log's own
   .bottom-nav backdrop-filter recipe (saturate+blur), applied to a top bar. */
.nav {
  position: sticky; top: 0; z-index: 50;
  height: var(--nav-h);
  display: flex; align-items: center;
  background: rgba(13, 18, 12, 0.75);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid var(--border);
}
.nav__inner { display: flex; align-items: center; justify-content: space-between; width: 100%; }
.nav__brand { display: inline-flex; align-items: center; gap: 0.6rem; background: none; border: none; padding: 0; cursor: pointer; font: inherit; color: inherit; }
.nav__mark { color: var(--accent); display: inline-flex; }
.nav__word { font-family: var(--font-heading); font-weight: 700; font-size: 1.1rem; color: var(--text); letter-spacing: -0.02em; }

/* ---------------------------------------------------------- BUTTONS
   Copied verbatim from the real app's .btn-primary / .btn-outline. */
.btn-primary { display: inline-flex; width:fit-content; align-items: center; gap: 0.5rem; border: none; border-radius: var(--radius-pill); background: var(--accent); padding: 0.75rem 2rem; font-size: 0.875rem; font-weight: 700; color: var(--accent-contrast); cursor: pointer; transition: transform var(--dur-fast) ease, background var(--dur-fast) ease; }
.btn-primary:hover, .btn-primary:focus { transform: scale(1.05); background: var(--accent-hover); }
.btn-primary:active { transform: scale(1); }
.btn-outline { display: inline-flex; width:fit-content; align-items: center; gap: 0.5rem; border: 1px solid var(--text-dim); border-radius: var(--radius-pill); background: none; padding: 0.75rem 2rem; font-size: 0.875rem; font-weight: 700; color: var(--text-muted); cursor: pointer; transition: border-color var(--dur-fast) ease, color var(--dur-fast) ease; }
.btn-outline:hover { border-color: var(--text); color: var(--text); }
.btn--small { padding: 0.55rem 1.4rem; font-size: 0.8rem; }

/* ---------------------------------------------------------- CARD (verbatim, no shadow) */
.card { position:relative; border-radius: var(--radius); background: var(--surface); padding: 1.25rem; color: var(--text-muted); box-shadow: none; }
.card__title { font-family: var(--font-heading); font-size: 1.25rem; font-weight: 700; margin: 0 0 1rem; color: var(--text); }
.card__title::before { content:''; display:inline-block; vertical-align: middle; width:0.5rem; height:0.5rem; border-radius:0.5rem; background-color:var(--accent); margin:0 0.5rem 0.185rem 0; }

/* ---------------------------------------------------------- STAT (verbatim) */
.stat { border-radius: var(--radius); background: var(--surface); padding: 1rem; box-shadow: none; }
.stat--feature { background: var(--feature-grad); }
.stat__label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); }
.stat--feature .stat__label { color: var(--accent); }
.stat__value { margin: 0.6rem 0 0; font-size: 1.6rem; font-weight: 700; font-family: var(--font-heading); color: var(--text); }
.stat__value small { font-size: 0.8rem; font-weight: 600; color: var(--text-dim); }

/* ---------------------------------------------------------- PROGRESS BAR (verbatim) */
.progress-bar { flex: 1; height: 6px; border-radius: var(--radius-pill); background: var(--surface-input); overflow: hidden; }
.progress-bar__fill { height: 100%; border-radius: var(--radius-pill); background: var(--accent); transition: width 0.3s ease; }

/* ---------------------------------------------------------- CHIP (verbatim quick-chip) */
.chip {
  display: inline-flex; align-items: center;
  border: 1px solid var(--border); border-radius: var(--radius-pill);
  background: var(--surface); padding: 0.4rem 0.9rem; font-size: 0.75rem;
  color: var(--text-muted);
}

/* ---------------------------------------------------------- TRUST BADGE (laurel)
   Structure only, reused from Dropset's "New Apps We Love" laurel-wreath
   shape (02-extraction/assets/svg/App Store Feature.svg) — new copy,
   accent fill instead of white. */
.laurel-badge { display: inline-flex; align-items: center; gap: 0.6rem; color: var(--text-muted); margin-bottom: 1.5rem; }
.laurel-badge svg { color: var(--accent); flex-shrink: 0; }
.laurel-badge__title { display: block; font-family: var(--font-heading); font-weight: 700; font-size: 0.85rem; color: var(--text); line-height: 1.2; }
.laurel-badge__sub { display: block; font-size: 0.7rem; color: var(--text-dim); }

/* ---------------------------------------------------------- TYPE HELPERS */
.eyebrow {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: 0.75rem; font-weight: 700; letter-spacing: 0.04em;
  color: var(--accent);
  background: var(--accent-soft);
  padding: 0.4rem 0.9rem;
  border-radius: var(--radius-pill);
  margin: 0 0 1.5rem;
}
.eyebrow::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
.is-strong { color: var(--text); }
.is-muted { color: var(--text-muted); }

/* Kinetic section headline — fixed 80px/700/100% desktop, 40px mobile,
   matching Dropset's measured ._3-features-title exactly (not a fluid
   clamp — the source uses a hard breakpoint step, so this does too). */
.section-title, .hero__title {
  font-size: clamp(3rem, 6vw + 1rem ,5rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  margin-bottom: 1.25rem;
}
.section-title--center { text-align: center; }
.section-sub {
  font-family: var(--font-default);
  font-size: 24px;
  font-weight: 400;
  line-height: 1.3;
  color: var(--text-muted);
  max-width: 34rem;
  margin-bottom: 1.5rem;
}
.section-sub--center { margin: 0 auto 3.5rem; text-align: center; }

@media (max-width: 767px) {
  .section-title, .hero__title { font-size: 40px; }
  .section-sub { font-size: 18px; }
}

.body-copy {
  font-family: var(--font-default);
  font-size: 26px;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: -0.5px;
  color: var(--text-muted);
  max-width: 715px;
}
@media (max-width: 767px) {
  .body-copy { font-size: 22px; }
}

/* ---------------------------------------------------------- HERO */
.hero { padding: 4rem 0 var(--rhythm); overflow: hidden; }
.hero__grid { display: grid; grid-template-columns: 1fr; gap: 3rem; align-items: center; max-width: var(--container-max); }
@media (min-width: 900px) {
  .hero__grid { grid-template-columns: 1.05fr 0.95fr; gap: 2rem; }
}

.hero__sub {
  font-family: var(--font-default);
  font-size: 26px;
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: -0.5px;
  color: var(--text-muted);
  max-width: 30rem;
  margin-bottom: 2rem;
}
@media (max-width: 767px) { .hero__sub { font-size: 22px; } }
.hero__tags { display: flex; flex-wrap: wrap; gap: 0.6rem; margin-bottom: 2.25rem; }
.hero__actions { display: flex; gap: 1rem; }

.hero__visual { position: relative; display: flex; justify-content: center; }
.hero__visual figure img { max-width:100%; }

/* -- HERO ENTRANCE (load) ------------------------------------------
   Plain time-based CSS animations, not scroll-driven — this content is
   already on screen at page load, so a view()-timeline wouldn't have
   anywhere to animate from (it'd just render fully "entered" instantly).
   Each direct child of .hero__copy gets the same fade-up, staggered a
   beat apart (eyebrow, title, sub, tags, CTA); the visual comes in a
   little after, so the eye lands on the copy first. Ordinary CSS
   Animations Level 1 — no @supports needed, this works everywhere. */
.hero__copy > * {
  opacity: 0;
  animation-name: hero-in;
  animation-duration: 700ms;
  animation-timing-function: var(--ease-out);
  animation-fill-mode: both;
}
.hero__copy > *:nth-child(1) { animation-delay: 60ms; }
.hero__copy > *:nth-child(2) { animation-delay: 160ms; }
.hero__copy > *:nth-child(3) { animation-delay: 280ms; }
.hero__copy > *:nth-child(4) { animation-delay: 380ms; }
.hero__copy > *:nth-child(5) { animation-delay: 460ms; }

.hero__visual {
  opacity: 0;
  animation: hero-in-visual 900ms var(--ease-out) 260ms both;
}

@keyframes hero-in {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes hero-in-visual {
  from { opacity: 0; transform: translateY(24px) scale(0.97); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* -- HERO SCROLL BEHAVIOR (exit) ------------------------------------
   Scroll-driven, so gated behind @supports. Two distinct effects on two
   distinct elements, deliberately not layered on the same one — a
   single element can't cleanly run a time-based load-in and a
   scroll-driven animation on the *same* property (transform) at once,
   since the later animation in the stack simply wins each frame rather
   than blending with the first. Splitting them onto parent vs. child
   avoids that entirely:
     - .hero__copy (the whole text column) fades and lifts away as you
       scroll past it — animation-range: exit tracks exactly the phase
       where the hero is leaving the top of the viewport.
     - .hero__visual img gets a slow, continuous parallax drift for as
       long as the hero is anywhere on screen (cover 0%–100%), so the
       screenshot feels like it's on a slightly different depth plane
       than the text next to it. */
@supports (animation-timeline: view()) {
  .hero__copy {
    animation-name: hero-exit;
    animation-timing-function: var(--ease-in);
    animation-fill-mode: both;
    animation-timeline: view();
    animation-range: exit 0% exit 100%;
  }
  .hero__visual img {
    animation-name: hero-parallax;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }
}
@keyframes hero-exit {
  from { opacity: 1; transform: translateY(0); }
  to   { opacity: 0; transform: translateY(-40px); }
}
@keyframes hero-parallax {
  from { transform: translateY(-4%); }
  to   { transform: translateY(4%); }
}

/* ---------------------------------------------------------- PHONE MOCKUP
   A believable device frame, not a flat placeholder box: subtle chassis
   gradient (still no drop shadow, per the flat-surface guardrail — the
   depth here comes from the gradient + hairline rim, not a shadow), a
   floating dynamic-island instead of a boxy notch, side buttons, and a
   real in-screen status bar — so each screenshot reads as an actual app
   screen, not a gray rectangle with icons floating in it. */
.phone {
  position: relative; z-index: 1;
  /* Fluid instead of a fixed 400px: capped by viewport height (40vh here
     × the 1:2 aspect-ratio = 80vh tall, so it always fits a short window
     now that it's pinned via position:sticky) and by 100% so it can't
     overflow a narrow mobile column either. */
  width: min(400px, 40vh, 100%);
  max-height: 80vh;
  aspect-ratio: 1/2;
  background: linear-gradient(160deg, #171d14, var(--nav-bg) 45%);
  border: 1px solid var(--border);
  border-radius: 52px;
  padding: 12px;
  box-shadow: none;
}
.phone--small { width: 240px; height: 490px; border-radius: 44px; }
.phone::before, .phone::after {
  content: ''; position: absolute; background: var(--border); border-radius: 2px;
}
.phone::before { right: -1px; top: 110px; width: 3px; height: 56px; } /* power button */
.phone::after { left: -1px; top: 90px; width: 3px; height: 32px; box-shadow: 0 44px 0 var(--border); } /* volume buttons */
.phone__island {
  position: absolute; top: 26px; left: 50%; transform: translateX(-50%);
  width: 28%; height: 3.9%; background: #000; border-radius: 999px;
  z-index: 3;
}
.phone__screen {
  position: relative; width: 100%; height: 100%;
  background: var(--bg);
  border-radius: 42px;
  overflow: hidden;
  padding: 2.6rem 0.9rem 1rem;
  display: flex; flex-direction: column; gap: 0.65rem;
}
.phone--small .phone__screen { padding: 2.3rem 0.8rem 0.9rem; }

/* Real-screenshot phone (callout-section "Fully rooted.") — the mock
   markup above is replaced with an actual dashboard capture that's much
   taller than the frame, so the frame scrolls instead of the page.
   overscroll-behavior keeps that scroll from "leaking" into the page
   scroll once it hits the top/bottom edge; the scrollbar is hidden
   (still fully scrollable via touch/trackpad/wheel) so it reads as a
   real phone screen rather than a webpage with a visible scrollbar. */
.phone__screen--screenshot {
  display: block;
  padding: 0;
  overflow-y: auto;
  overflow-x: hidden;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.phone__screen--screenshot::-webkit-scrollbar { display: none; }
.phone__screenshot {
  display: block;
  width: 100%;
  height: auto;
}

/* Status bar + bottom nav "chrome" overlaid on the real screenshot —
   sticky (not absolute) so each one stays pinned to its edge of the
   phone screen's own scroll container while the dashboard capture
   scrolls underneath, the same way a real app's status bar and tab bar
   stay put while content scrolls. Both are first/last children of the
   scrolling element itself, which is what makes position: sticky pin
   them to that element's viewport rather than the page's. */
.mock-statusbar--overlay {
  position: sticky;
  top: 0;
  z-index: 2;
  padding: 0.85rem 0.9rem 0.5rem;
  background: rgba(13, 18, 12, 0.82);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
}
.phone__navshot-wrap {
  position: sticky;
  bottom: 0;
  z-index: 2;
  line-height: 0;
}
.phone__navshot {
  display: block;
  width: 100%;
  height: auto;
}

/* -- in-screen status bar (time + signal/wifi/battery), sits right under
      the island so each screen reads as a real capture, not a bare card -- */
.mock-statusbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 5% 11% 2%;
  font-family: var(--font-default); font-size: 0.7rem; font-weight: 600; color: var(--text);
}
.mock-statusbar__icons { display: flex; align-items: center; gap: 4px; color: var(--text); }
.mock-statusbar__icons svg { display: block; }

.mock-dash { display: flex; flex-direction: column; gap: 0.65rem; }
.mock-dash__header { display: flex; gap: 6px; margin-bottom: 0.3rem; }
.mock-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--border); }
.mock-dot--a { background: var(--text-dim); }
.mock-dot--b { background: var(--accent); opacity: 0.5; }
.mock-dot--c { background: var(--accent); }

.mock-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.mock-stat { background: var(--surface); border-radius: var(--radius); padding: 0.6rem 0.7rem; display: flex; flex-direction: column; gap: 0.2rem; }
.mock-stat__label { font-size: 0.55rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); }
.mock-stat__value { font-family: var(--font-heading); font-size: 1.05rem; font-weight: 700; color: var(--text); }
.mock-stat__value small { font-size: 0.6rem; font-weight: 600; color: var(--text-muted); }

.mock-card { background: var(--surface); border-radius: var(--radius); padding: 0.7rem 0.8rem; }
.mock-card__title { font-size: 0.65rem; font-weight: 700; color: var(--text); margin-bottom: 0.45rem; }
.mock-card__meta { font-size: 0.55rem; color: var(--text-dim); margin-top: 0.35rem; }
.mock-bar-row { height: 6px; border-radius: 999px; background: var(--surface-input); overflow: hidden; }
.mock-bar { height: 100%; width: var(--w, 50%); background: var(--accent); border-radius: 999px; }

.mock-heat { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.mock-heat span { display: block; aspect-ratio: 1; border-radius: 3px; }
.h0 { background: var(--surface-input); }
.h1 { background: color-mix(in srgb, var(--accent) 30%, var(--surface-input)); }
.h2 { background: color-mix(in srgb, var(--accent) 55%, var(--surface-input)); }
.h3 { background: color-mix(in srgb, var(--accent) 80%, var(--surface-input)); }
.h4 { background: var(--accent); }

.mock-card--badge { display: flex; align-items: center; gap: 0.6rem; }
.mock-badge { font-size: 1.3rem; }

.mock-log { display: flex; flex-direction: column; gap: 0.65rem; }
.mock-log__title { font-family: var(--font-heading); font-weight: 700; font-size: 1rem; color: var(--text); margin-bottom: 0.2rem; }
.mock-field { height: 34px; background: var(--surface); border-radius: var(--radius); }
.mock-field-row { display: flex; gap: 0.5rem; }
.mock-field--half { flex: 1; }
.mock-field--quarter { flex: 0.6; }
.mock-chips { display: flex; gap: 0.4rem; flex-wrap: wrap; }
.mock-chip { font-size: 0.6rem; font-weight: 600; color: var(--accent); background: var(--accent-soft); padding: 0.3rem 0.6rem; border-radius: var(--radius-pill); }
.mock-cta { margin-top: 0.3rem; text-align: center; background: var(--accent); color: var(--accent-contrast); font-weight: 700; font-size: 0.75rem; padding: 0.7rem; border-radius: var(--radius-pill); }

/* -- mock: harvest table w/ tags (used in the callout screen) -- */
.mock-table { display: flex; flex-direction: column; gap: 0.4rem; }
.mock-row { display: flex; justify-content: space-between; align-items: center; background: var(--surface); border-radius: var(--radius); padding: 0.55rem 0.7rem; }
.mock-row__name { font-size: 0.7rem; font-weight: 600; color: var(--text); }
.mock-row__tags { display: flex; gap: 0.25rem; }
.mock-row__tag { font-size: 0.5rem; font-weight: 700; color: var(--accent); background: var(--accent-soft); padding: 0.15rem 0.4rem; border-radius: var(--radius-pill); }
.mock-row__value { font-size: 0.7rem; font-weight: 700; color: var(--text); }

/* -- mock: still-time-to-plant countdown (compact stat, styled like .stat) -- */
.mock-countdown { background: var(--surface); border-radius: var(--radius); padding: 0.7rem 0.8rem; display: flex; align-items: baseline; gap: 0.5rem; }
.mock-countdown__num { font-family: var(--font-heading); font-size: 1.3rem; font-weight: 700; color: var(--text); line-height: 1; }
.mock-countdown__label { font-size: 0.6rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-dim); }

/* -- mock: season picker -- */
.mock-season-row { display: flex; justify-content: space-between; align-items: center; background: var(--surface-hover); border-radius: 6px; padding: 0.65rem 0.75rem; margin-bottom: 0.4rem; }
.mock-season-row.is-active { background: var(--surface-input); }
.mock-season-row__label { font-size: 0.75rem; font-weight: 600; color: var(--text); }
.mock-season-badge { font-size: 0.55rem; font-weight: 700; text-transform: uppercase; color: var(--accent); background: var(--accent-soft); padding: 0.1rem 0.45rem; border-radius: var(--radius-pill); }

/* ---------------------------------------------------------- KINETIC / EMPATHY
   Tight 1.0 line-height and heavy weight, matching Dropset's measured
   80px/700/100%-line-height headline rhythm exactly. Each line fades from
   muted to full strength on scroll — driven entirely by CSS now
   (animation-timeline: view()), no JS/IntersectionObserver involved. Each
   span is its own view-timeline subject, so stacked lines naturally
   reveal a beat apart as they individually cross into the viewport — no
   manual stagger/delay needed to reproduce that effect. */
.empathy { padding: var(--rhythm) 0; }
.kinetic { display: flex; flex-direction: column; margin-bottom: 2rem; }
.kinetic__line {
  font-size: 80px;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--text); /* fallback: fully visible where view() isn't supported yet */
}
@media (max-width: 767px) { .kinetic__line { font-size: 40px; } }
.empathy__body { font-size: 1.15rem; color: var(--text-muted); max-width: 42rem; }

@supports (animation-timeline: view()) {
  .kinetic__line {
    color: var(--text-dim);
    animation-name: kinetic-fade-in;
    animation-timing-function: ease-in-out;
    animation-fill-mode: both;
    animation-timeline: view();
    animation-range: entry 0% cover 40%;
  }
}
@keyframes kinetic-fade-in {
  to { color: var(--text); }
}

/* "Fully rooted." heading (callout-section) — bigger than the standard
   kinetic size, plus the two words physically slide in from opposite
   edges as the section scrolls into view, layered on top of the same
   color fade above (both animations share one view() timeline on the
   element, so they stay perfectly in sync with each other and with
   scroll position — scrub the page and the words track exactly, no
   timer involved). Gated behind @supports since Firefox doesn't
   implement view() yet (~84% global support as of mid-2026 per
   caniuse); unsupported browsers just show the final state directly. */
.callout-section__intro h2 {
  font-size: clamp(3.25rem, 9vw + 1rem, 12rem);
}

@supports (animation-timeline: view()) {
  .kinetic--rooted .kinetic__line--from-left,
  .kinetic--rooted .kinetic__line--from-right {
    animation-name: kinetic-fade-in, kinetic-slide-in;
    animation-timing-function:linear;
    animation-fill-mode: both;
    animation-timeline: view();
    animation-range: entry 0% cover 100%;
  }
  .kinetic--rooted .kinetic__line--from-left { --kinetic-slide-from: -15vw; --kinetic-slide-to:1rem; }
  .kinetic--rooted .kinetic__line--from-right { --kinetic-slide-from: 15vw; --kinetic-slide-to:-1rem; }
}

@keyframes kinetic-slide-in {
  0% { transform: translateX(var(--kinetic-slide-from)); }
  60% { transform: translateX(0); }
  100% { transform: translateX(var(--kinetic-slide-to)); }
}

@keyframes kinetic-move-down {
  0% { transform: translateY(-3rem); }
  100% { transform: translateY(0); }
}

/* ---------------------------------------------------------- PAYOFF
   Single centered column, no phone/chips — a plain, confident narrative
   moment (eyebrow + heading + one paragraph) between the empathy bento and
   the callout showcase. Occupies the same slot/shape the old "Not just
   weight." copy used to. */
.payoff { padding: var(--rhythm) 0; border-top: 1px solid var(--border); }
.text--center-narrow { text-align: center; max-width: 40rem; margin: 0 auto; }
.payoff__body { font-size: 1.1rem; color: var(--text-muted); margin-top: 1.25rem; }
.payoff__cite { font-size: 1.1rem; color: var(--text-accent); margin-top: 1.25rem; } 

/* ---------------------------------------------------------- FEATURE CALLOUT SECTION
   The single "showcase" moment for what's inside the app — same layout
   idea as the hero (kinetic heading, then copy-column + phone-column) but
   the copy column is a short list of callouts pointing at specific things
   on the one screenshot, instead of a paragraph. One dedicated, high-detail
   phone here (plus the hero's), rather than a phone per feature. */
.callout-section { overflow:clip; padding: var(--rhythm-double) 0; border-top: 1px solid var(--border); }
.callout-section__intro { margin-bottom: 2rem; }

@supports (animation-timeline: view()) {
  .callout-section__intro { animation-name:kinetic-move-down; animation-timing-function: linear; animation-fill-mode: both; animation-timeline: view(); animation-range: entry exit; }
}

.callout-section__grid {
  display: grid; grid-template-columns: 1fr; gap: 3rem; align-items: center;
}
@media (min-width: 900px) {
  .callout-section__grid { grid-template-columns: 1fr 1fr; gap: 3rem; }
}
.callout-section__visual { display: flex; justify-content: center; position:relative; }
.callout-section__visual::after { content:''; display:block; position:absolute; inset:0; z-index:2; }

.callout-list { display: flex; flex-direction: column; gap: 3rem; }
.callout-item { display: flex; align-items: center; gap: 0.85rem; }
.callout-item__dot {
  width: 26px; height: 26px; border-radius: 50%; align-content: center;
  background: var(--accent); flex-shrink: 0;
  box-shadow: 0 0 0 4px var(--accent-soft); text-align:center; font-size:0.9rem; font-weight: bold; color: var(--bg);
}
.callout-item__line {
  flex: 0 0 100px; height: 1px; background: var(--border);
  position: relative;
}
.callout-item__line::after {
  content: ''; position: absolute; right: -2px; top: 50%; transform: translateY(-50%);
  width: 5px; height: 5px; border-radius: 50%; background: var(--text-dim);
}
.callout-item__text { font-family: var(--font-heading); font-size: 1.5rem; font-weight: 200; color: var(--text); }
.callout-item__text span { display: block; font-size: 0.9rem; color: var(--text-muted); }

.callout-phone { position:relative; }
.callout-phone-key { position:absolute; z-index: 2; width: 22px; height: 22px; border-radius: 50%; background: var(--accent); color:var(--bg); font-weight: bold; font-size: 0.9rem; text-align: center; }
.callout-phone-key.key__1 { top:23.6%; left:58%; }
.callout-phone-key.key__2 { top:31.55%; left:52.5%; }
.callout-phone-key.key__3 { top:38.85%; left:47%; }
.callout-phone-key.key__4 { top:48.4%; left:35%; }
.callout-phone-key.key__5 { top:56.75%; left:57%; }

.callout-phone-key::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  z-index: -1;
  background-color: var(--accent-hover);
  animation: blip-pulse 1.8s cubic-bezier(0.25, 0, 0, 1) infinite;
}

@keyframes blip-pulse {
  0% {
    transform: scale(1);
    opacity: 0.8;
  }
  100% {
    transform: scale(3.5);
    opacity: 0;
  }
}

@media (max-width: 899px) { .callout-item__line { display: none; } }

/* -- SCRUB-SYNCED CALLOUTS --------------------------------------------
   The whole point: as you scroll, the phone screenshot scrolls too, and
   whichever callout "owns" the bit of the app currently in the frame
   fades in (sliding in from the right) and fades back out once that
   part scrolls out of frame — driven by ONE shared timeline so both
   sides are locked to the exact same scroll position, no JS.

   How it works: .callout-scrub is a very tall track (it's the only thing
   creating extra scroll distance) with view-timeline-name: --callout-scrub
   — that name is just a handle other elements can reference, it doesn't
   have to be the element that animates. .callout-scrub__pin is sticky, so
   the actual list+phone layout stays parked in the viewport for that
   entire tall track, instead of flying by in one screen's worth of
   scroll. The phone image and each callout then reference
   animation-timeline: --callout-scrub and just claim their own %
   slice of it with animation-range.

   The percentages below are hand-tuned against the real dashboard
   screenshot (cumulative harvest ≈ 21–26% down the image, the harvest
   calendar heatmap ≈ 27–32%, achievements ≈ 33–39%, still-time-to-plant
   ≈ 40–45%) — if a future screenshot reorders or resizes those sections,
   these numbers will need re-tuning to match; there's no way to bind
   them to the image content itself since it's a flat screenshot, not
   live DOM.

   Falls back to today's plain layout (manually-scrollable phone,
   always-visible callout list, no pin) wherever animation-timeline:
   view() isn't supported — none of this runs outside @supports. */
@supports (animation-timeline: view()) {
  .callout-scrub {
    height: 400vh;
    view-timeline-name: --callout-scrub;
    view-timeline-axis: block;
  }
  .callout-scrub__pin {
    position: sticky;
    top: calc(var(--nav-h) + 2rem);
  }

  .phone__screen--screenshot { overflow: hidden; }
  .callout-phone {
    animation-name: phone-scrub;
    animation-timing-function: linear;
    animation-fill-mode: both;
    animation-timeline: --callout-scrub;
    animation-range: contain 0% contain 100%;
  }

  .callout-item {
    opacity: 0;
    animation-name: callout-fade;
    animation-timing-function: var(--ease-out);
    animation-fill-mode: both;
    animation-timeline: --callout-scrub;
  }
  /* Each item fades in over a short window right as its feature centers
     in the phone frame, then — thanks to animation-fill-mode: both
     holding the 100% keyframe once animation-range ends — just stays
     visible for the rest of the scroll, so the list builds up instead
     of cycling. cumulative harvest: */
  .callout-item:nth-child(1) { animation-range: contain 0% contain 15%; }
  /* harvest calendar heatmap — the ~30%-down example this was built around */
  .callout-item:nth-child(2) { animation-range: contain 20% contain 35%; }
  /* achievements */
  .callout-item:nth-child(3) { animation-range: contain 40% contain 55%; }
  /* still time to plant */
  .callout-item:nth-child(4) { animation-range: contain 60% contain 75%; }
  /* yield */
  .callout-item:nth-child(5) { animation-range: contain 80% contain 95%; }
  
}

@keyframes phone-scrub {
  from { transform: translateY(-15%); }
  to   { transform: translateY(-46%); }
}
@keyframes callout-fade {
  0%   { opacity: 0; transform: translateX(20px); }
  100% { opacity: 1; transform: translateX(0); }
}

/* ---------------------------------------------------------- BENTO FEATURES
   Flat cards, no shadow, hover only lightens background — matches how
   Dropset's own feature cards behave (no elevation change, just tone). */
.features { padding: var(--rhythm) 0; border-top: 1px solid var(--border); }
.bento { display: grid; grid-template-columns: 1fr; gap: 1rem; margin-top:3rem; }
@media (min-width: 700px) {
  .bento { grid-template-columns: repeat(2, 1fr); }
  .bento__card--wide { grid-column: span 2; }
}
@media (min-width: 1024px) {
  .bento { grid-template-columns: repeat(5, 1fr); }
  .bento__card { grid-column: span 2; }
  .bento__card--wide { grid-column: span 3; }
}
.bento__card {
  background: var(--surface); border-radius: var(--radius);
  padding: 1.75rem;
  box-shadow: none;
  transition: background var(--dur-base) var(--ease-out);
}
.bento__card:hover { background: var(--surface-hover); }
.bento__icon { color: var(--accent); display: block; margin-bottom: 1rem; }
.bento__card h3 { font-size: 1.15rem; margin-bottom: 0.5rem; }
.bento__card p { color: var(--text-muted); font-size: 0.95rem; line-height: 1.55; }

/* ---------------------------------------------------------- REVIEWS
   2-col grid, 60px gap, max-width 940px, margin-top 128px — all measured
   verbatim from Dropset's .reviews (02-extraction/all-styles-webflow.css). */
.reviews-section { padding: var(--rhythm) 0; border-top: 1px solid var(--border); }
.reviews {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 60px;
  max-width: 940px;
  margin: 128px auto 0;
}
@media (max-width: 767px) {
  .reviews { grid-template-columns: 1fr; gap: 2rem; margin-top: 3rem; }
}
.review {
  display: flex; flex-direction: column; gap: 0.75rem;
  background: var(--surface); border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: none;
}
.review__stars { display: flex; gap: 0.2rem; color: var(--accent); }
.review__stars svg { width: 16px; height: 16px; }
.review__title { font-family: var(--font-heading); font-size: 1.05rem; font-weight: 700; color: var(--text); }
.review__body { font-size: 0.9rem; color: var(--text-muted); line-height: 1.6; }
.review__date { font-size: 0.75rem; color: var(--text-dim); }

/* ---------------------------------------------------------- FOUNDER */
.founder { padding: var(--rhythm) 0; border-top: 1px solid var(--border); }
.founder__grid { display: grid; grid-template-columns: 1fr; gap: 2rem; align-items: start; }
@media (min-width: 800px) { .founder__grid { grid-template-columns: auto 1fr; gap: 3rem; align-items: center; } }
.founder__mark { color: var(--accent); flex-shrink: 0; }
.founder__copy p { color: var(--text-muted); font-size: 1.1rem; margin-top: 1rem; max-width: 40rem; }
.founder__copy p:first-of-type { margin-top: 1.25rem; }

/* ---------------------------------------------------------- CTA / NOTIFY */
.cta { padding: var(--rhythm) 0 6rem; border-top: 1px solid var(--border); text-align: center; }
.cta__title { font-size: 80px; font-weight: 700; line-height: 1; margin-bottom: 0.75rem; }
@media (max-width: 767px) { .cta__title { font-size: 40px; } }
.cta__sub { color: var(--text-muted); margin-bottom: 2.5rem; }

.notify-form { display: flex; flex-wrap: wrap; justify-content: center; gap: 0.75rem; max-width: 30rem; margin: 0 auto 3rem; }
.notify-form__input {
  flex: 1; min-width: 220px;
  padding: 0.85rem 1.25rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface-input);
  color: var(--text);
  font-family: var(--font-default); font-size: 0.95rem;
}
.notify-form__input::placeholder { color: var(--text-dim); }
.notify-form__input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }

.store-badges { display: flex; flex-wrap: wrap; justify-content: center; gap: 1rem; }
.store-badge {
  display: inline-flex; align-items: center; gap: 0.6rem;
  background: var(--surface);
  border-radius: var(--radius);
  padding: 0.65rem 1.1rem;
  color: var(--text-muted);
  font-size: 0.7rem; line-height: 1.3;
}
.store-badge strong { color: var(--text); font-size: 0.75rem; }

/* ---------------------------------------------------------- FOOTER */
.footer { padding: 3rem 0 2rem; border-top: 1px solid var(--border); color: var(--text-dim); background: var(--bg); }
.footer__inner { display: flex; flex-direction: column; gap: 1.5rem; padding-bottom: 2rem; }
@media (min-width: 700px) { .footer__inner { flex-direction: row; justify-content: space-between; align-items: flex-start; } }
.footer__tag { margin-top: 0.5rem; font-size: 0.85rem; max-width: 25rem; color: var(--text-dim); }
.footer__links { display: flex; flex-direction: column; gap: 0.6rem; font-size: 0.85rem; align-items: flex-start; color: var(--text-muted); }
.footer__links a:hover { color: var(--text); }
.footer__note { color: var(--text-dim); max-width: 25rem; }
.footer__copyright { font-size: 0.75rem; color: var(--text-dim); }

/* ---------------------------------------------------------- SCROLL REVEAL
   Pure CSS now — no IntersectionObserver, no script.js. animation-timeline:
   view() ties the fade-up directly to each element's own scroll position
   (the "entry" named range = from when it starts entering the viewport to
   when it's fully in), so there's zero JS on the page driving this. Default
   state (outside @supports) is simply visible, so browsers without this API
   yet (Firefox, as of writing) just show content immediately — a fade-up is
   a nice-to-have, not worth a JS fallback for. */
.reveal { opacity: 1; }

@supports (animation-timeline: view()) {
  .reveal {
    opacity: 0;
    transform: translateY(24px);
    animation-name: reveal-in;
    animation-timing-function: var(--ease-out);
    animation-fill-mode: both;
    animation-timeline: view();
    animation-range: entry 0% entry 100%;
  }
}
@keyframes reveal-in {
  to { opacity: 1; transform: translateY(0); }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .kinetic__line,
  .kinetic--rooted .kinetic__line--from-left,
  .kinetic--rooted .kinetic__line--from-right {
    animation: none !important;
    color: var(--text) !important;
    transform: none !important;
  }
  .callout-section__intro { animation: none !important; }
  /* Drop the scrub-synced callout sequence back to the plain fallback
     layout: no tall track, no pin, phone's native scroll returns, and
     every callout is simply visible instead of cycling in/out. */
  .callout-scrub { height: auto !important; view-timeline-name: none !important; }
  .callout-scrub__pin { position: static !important; top: auto !important; }
  .phone__screen--screenshot { overflow-y: auto !important; overflow-x: hidden !important; }
  .phone__screenshot { animation: none !important; transform: none !important; }
  .callout-item {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

@media (max-width: 640px) {
  .hero { padding: 2.5rem 0 4rem; }
}
