/* ==========================================================================
   Sizzlo — marketing site, in the app's "Hot Pan" design.

   Tokens are docs/DESIGN.md §3 in the app repo, named after SZColor in
   ios/Sizzlo/Design/SizzloTheme.swift (404.css uses the same names). Light is
   Cream, dark is Cast Iron, and the page follows the visitor's system setting
   like the app does. Type is §4: Bricolage Grotesque for display and titles,
   the system face (SF Pro on Apple devices) for everything else.

   Elevation is §5: mostly flat. Primary buttons and the highlighted plan get
   the sticker edge (a 1.5px outline and a hard 3px shadow, no blur) in light
   mode; dark mode drops shadows and lets the outline do the work.
   ========================================================================== */

:root {
  color-scheme: light dark;

  --background:      #FFF8F0;
  --background-alt:  #F6EDE3;
  --surface:         #FFFFFF;
  --surface-alt:     #F6EDE3;
  --text-primary:    #1C1512;
  --text-secondary:  #5E524B;
  /* Not in DESIGN.md: the page needs a third, quieter step for fine print.
     5.4:1 on Cream, so it still passes AA at small sizes. */
  --text-tertiary:   #6E6259;
  --primary:         #FF5A1F;
  --on-primary:      #1C1512;
  /* Ember: orange text and links. Never #FF5A1F for text on a light page. */
  --primary-text:    #BE3F0B;
  --accent:          #FFD23F;
  --on-accent:       #1C1512;
  --outline:         #1C1512;

  --line:            rgba(28, 21, 18, .10);
  --line-2:          rgba(28, 21, 18, .18);
  --tint:            rgba(28, 21, 18, .04);
  --tint-2:          rgba(28, 21, 18, .08);
  --primary-tint:    rgba(255, 90, 31, .12);
  --primary-line:    rgba(190, 63, 11, .32);
  --header-bg:       rgba(255, 248, 240, .97);
  --scrim:           rgba(28, 21, 18, .4);

  /* Heat scale (§3). Shown as icon plus word, never colour alone. */
  --fresh:       #2563EB;  --fresh-tint:    #E6EEFD;
  --use-soon:    #B45309;  --use-soon-tint: #FDF0DC;
  --going-bad:   #C81E1E;  --going-bad-tint:#FCE4E2;
  --expired:     #5C3A2E;  --expired-tint:  #EFE6E2;

  --shadow-hard: 0 3px 0 var(--outline);
  --shadow-soft: 0 4px 12px rgba(0, 0, 0, .08);
  --shadow-panel: 0 24px 48px -24px rgba(28, 21, 18, .45);

  --radius-sm:  10px;
  --radius:     16px;
  --radius-lg:  24px;
  --maxw:       1180px;

  --font-display: "Bricolage Grotesque", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-body: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* The sticky header's height: 62px of .wrap plus the 1px border. Used to
     keep the pinned 3D scene out from under the bar. */
  --header-h: 63px;
}

/* Dark, twice: once for a system set to dark, once for an explicit
   data-theme="dark", so a future toggle needs no new rules. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --background:      #14100E;
    --background-alt:  #1A1512;
    --surface:         #1E1815;
    --surface-alt:     #2A221E;
    --text-primary:    #FFF4EA;
    --text-secondary:  #C9B8AC;
    --text-tertiary:   #A8978B;
    --primary:         #FF6A33;
    --on-primary:      #14100E;
    --primary-text:    #FF6A33;
    --outline:         #FFF4EA;
    --line:            rgba(255, 244, 234, .10);
    --line-2:          rgba(255, 244, 234, .18);
    --tint:            rgba(255, 244, 234, .05);
    --tint-2:          rgba(255, 244, 234, .10);
    --primary-tint:    rgba(255, 106, 51, .14);
    --primary-line:    rgba(255, 106, 51, .40);
    --header-bg:       rgba(20, 16, 14, .97);
    --scrim:           rgba(0, 0, 0, .6);
    --fresh:     #6EA8FF;  --fresh-tint:    #16233A;
    --use-soon:  #FFB547;  --use-soon-tint: #33240F;
    --going-bad: #FF6B5B;  --going-bad-tint:#3A1714;
    --expired:   #B89A8E;  --expired-tint:  #2E2420;
    --shadow-hard: none;
    --shadow-soft: none;
    --shadow-panel: 0 30px 70px -20px rgba(0, 0, 0, .75);
  }
}
:root[data-theme="dark"] {
  --background:      #14100E;
  --background-alt:  #1A1512;
  --surface:         #1E1815;
  --surface-alt:     #2A221E;
  --text-primary:    #FFF4EA;
  --text-secondary:  #C9B8AC;
  --text-tertiary:   #A8978B;
  --primary:         #FF6A33;
  --on-primary:      #14100E;
  --primary-text:    #FF6A33;
  --outline:         #FFF4EA;
  --line:            rgba(255, 244, 234, .10);
  --line-2:          rgba(255, 244, 234, .18);
  --tint:            rgba(255, 244, 234, .05);
  --tint-2:          rgba(255, 244, 234, .10);
  --primary-tint:    rgba(255, 106, 51, .14);
  --primary-line:    rgba(255, 106, 51, .40);
  --header-bg:       rgba(20, 16, 14, .97);
  --scrim:           rgba(0, 0, 0, .6);
  --fresh:     #6EA8FF;  --fresh-tint:    #16233A;
  --use-soon:  #FFB547;  --use-soon-tint: #33240F;
  --going-bad: #FF6B5B;  --going-bad-tint:#3A1714;
  --expired:   #B89A8E;  --expired-tint:  #2E2420;
  --shadow-hard: none;
  --shadow-soft: none;
  --shadow-panel: 0 30px 70px -20px rgba(0, 0, 0, .75);
}

/* Bricolage at the optical sizes the app ships (DESIGN.md §4): ExtraBold
   cut at 36 for display, Bold cut at 24 for titles. Never the variable
   font's default 96, the tight poster cut. */
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("fonts/BricolageGrotesque-ExtraBold.woff2") format("woff2");
  font-weight: 800;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Bricolage Grotesque";
  src: url("fonts/BricolageGrotesque-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

* { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  overflow-x: clip;
}
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--background);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 17px;
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  /* `clip`, never `hidden`. overflow-x:hidden computes overflow-y to `auto`,
     which turns <body> into a scroll container — and position:sticky then
     anchors to body's scrollport instead of the viewport, so the 3D phone
     stage silently stops pinning. */
  overflow-x: clip;
}

/* `height: auto` pairs with the width/height attributes on every screenshot,
   so the aspect ratio holds even if a later rule sets a width. */
img { max-width: 100%; height: auto; display: block; }
figure { margin: 0; }
a { color: var(--primary-text); }

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

/* The sections config.js has to switch on (proof strip, founder note,
   founding cooks, the badge) start `hidden`. Without !important, any rule
   that sets `display` (.btn, .section-head, a grid) quietly wins over the
   browser's own [hidden] rule and shows them half-built. */
[hidden] { display: none !important; }

.wrap {
  width: 100%;
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: 24px;
}

/* Bricolage ExtraBold carries the big headings, Bold the card titles. Both
   are static cuts at small optical sizes, so they stay readable rather than
   crowding like the poster cut. */
.display {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -.02em;
}

/* --------------------------------------------------------------- wordmark */

.wordmark { display: block; height: auto; }
.wordmark--header { width: 108px; }
.wordmark--hero   { width: min(420px, 74vw); margin-inline: auto; }
.wordmark--footer { width: 104px; opacity: .85; }

/* ----------------------------------------------------------------- header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* Nearly opaque, and deliberately no backdrop-filter. A blur behind a
     sticky bar is redrawn on every scroll frame, over the 3D canvas too, and
     on phones that was the single most expensive thing on the page. */
  background: var(--header-bg);
  border-bottom: 1px solid var(--line);
}
.site-header .wrap {
  position: relative;
  display: flex;
  align-items: center;
  gap: 28px;
  min-height: 62px;
}

.site-nav {
  margin-left: auto;
  display: flex;
  align-items: center;
  --nav-gap: 30px;
  gap: var(--nav-gap);
}
/* The Instagram and X buttons are one group, not two more nav items: 8px
   apart whatever the nav's own gap is at this width. */
.site-nav .icon-btn + .icon-btn { margin-left: calc(8px - var(--nav-gap)); }
/* :not(.btn) matters. As a plain `.site-nav a` this outweighed .btn-primary
   on specificity and repainted the header's "Join the beta" button in muted
   cream on orange — 1.9:1, the worst contrast anywhere on the site. */
.site-nav a:not(.btn) {
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 15px;
  font-weight: 500;
  white-space: nowrap;
}
/* Just above the menu breakpoint six links, the Instagram button and "Join
   the waitlist" only fit on one line with tighter spacing. Without this the
   links wrapped to two lines ("What it / does") between 881 and ~1040px. */
@media (min-width: 881px) and (max-width: 1080px) {
  .site-header .wrap { gap: 20px; }
  .site-nav { --nav-gap: 18px; }
  .site-nav a:not(.btn) { font-size: 14.5px; }
}
.site-nav a:not(.btn):hover { color: var(--text-primary); }

/* set by the scroll spy in assets/js/site.js */
.nav-link { position: relative; }
.nav-link.is-current::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 1.5px;
  background: var(--primary-text);
}
.site-nav .nav-link.is-current { color: var(--text-primary); }

/* ------------------------------------------------------------------ button */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 9px;
  padding: 14px 28px;
  border-radius: 999px;
  border: 1.5px solid transparent;
  font-family: var(--font-body);
  font-size: 15px;
  font-weight: 600;
  letter-spacing: .01em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background-color .2s ease, border-color .2s ease, transform .12s ease, box-shadow .12s ease;
}
/* The sticker edge (DESIGN.md §5): Sizzle orange, dark text, a 1.5px outline
   and a hard shadow that the button sinks into when pressed. Dark text on
   orange is 5.8:1; white would be 3.1:1, which fails. */
.btn-primary {
  background: var(--primary);
  color: var(--on-primary);
  border-color: var(--outline);
  box-shadow: var(--shadow-hard);
}
.btn-primary:hover { transform: translateY(-1px); box-shadow: 0 4px 0 var(--outline); }
.btn-primary:active { transform: translateY(3px); box-shadow: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .btn-primary:hover { box-shadow: none; }
}
:root[data-theme="dark"] .btn-primary:hover { box-shadow: none; }
@media (prefers-reduced-motion: reduce) {
  .btn-primary:hover, .btn-primary:active { transform: none; }
}
.btn-arrow { width: 17px; height: 17px; flex: none; margin-right: -3px; }

.btn-ghost {
  background: var(--surface-alt);
  border-color: var(--line-2);
  color: var(--text-primary);
}
.btn-ghost:hover { background: var(--tint-2); }
.btn-sm { padding: 10px 21px; font-size: 14px; }

/* The waitlist button carries a phone glyph. Deliberately not the Apple logo:
   Apple does not license it for third-party buttons (see index.html). */
.btn-glyph { width: 18px; height: 18px; flex: none; margin-left: -3px; }
.btn-sm .btn-glyph { width: 16px; height: 16px; margin-left: -4px; }
.btn-lg { padding: 11px 28px 11px 22px; gap: 11px; font-size: 16px; }
.btn-lg .btn-glyph { width: 24px; height: 24px; }
.btn-stack { display: grid; text-align: left; line-height: 1.15; }
.btn-kicker {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .72;
}

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

.hero {
  padding: clamp(40px, 6vw, 72px) 0 clamp(64px, 8vw, 104px);
  text-align: center;
}
.hero .eyebrow { margin-bottom: 22px; }

.hero h1 {
  font-size: clamp(2.6rem, 7.2vw, 4.5rem);
  line-height: 1.04;
  margin: 22px auto 0;
  max-width: 15ch;
}
/* nowrap so the hyphen never becomes a line break: "Your AI sous-" / "chef."
   read as two words on a phone. The whole compound wraps instead. */
.hot { color: var(--primary-text); white-space: nowrap; }

.hero .subhead {
  margin: 22px auto 0;
  max-width: 56ch;
  color: var(--text-secondary);
  font-size: clamp(1.02rem, 2.1vw, 1.2rem);
}
.hero-ctas {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 14px;
  margin-top: 34px;
}

.hero-shot {
  width: min(340px, 76vw);
  margin-inline: auto;
}

/* The screen recording, once it exists (TODO in index.html). Reduced motion
   gets the still poster and never the moving picture. */
.hero-video { display: block; width: 100%; height: auto; }
.hero-video-poster { display: none; }
@media (prefers-reduced-motion: reduce) {
  .hero-video { display: none; }
  .hero-video-poster { display: block; }
}

/* app_store mode only. Apple's badge exactly as supplied: 40px tall, never
   recoloured, tilted or animated, with at least a quarter of its height
   clear all round, and quieter than the orange button above it. */
.store-badge {
  display: inline-block;
  margin: 18px auto 0;
  padding: 10px;
}
.store-badge img { height: 40px; width: auto; }

/* Only for visitors inside Instagram's or TikTok's browser (site.js). It
   sits in .hero-ctas right after the orange button: a row of its own under
   both buttons on wide screens, directly under the orange one on phones. */
.inapp-note {
  position: relative;
  order: 1;
  flex-basis: 100%;
  max-width: 34rem;
  margin: 0 auto;
  padding: 14px 48px 16px 18px;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-lg);
  text-align: left;
  font-size: .94rem;
  color: var(--text-secondary);
}
.inapp-note p { margin: 0 0 12px; }
.inapp-actions { display: flex; }
.inapp-url {
  width: 100%;
  min-height: 44px;
  padding: 0 14px;
  border: 1px solid var(--line-2);
  border-radius: 12px;
  background: var(--background);
  color: var(--text-primary);
  font: inherit;
}
.inapp-dismiss {
  position: absolute;
  top: 4px;
  right: 4px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 12px;
  background: none;
  color: var(--text-tertiary);
  cursor: pointer;
}
.inapp-dismiss svg { width: 18px; height: 18px; }
.inapp-dismiss:hover { color: var(--text-primary); }

/* ==========================================================================
   Stickers
   The app's own die-cut stickers (Fluent Emoji 3D, MIT — credits.html). The
   white border and the soft shadow are baked into the file, so nothing here
   draws a sticker; it only places one and leans it.

   Motion is the visitor's, never ambient: the hero's drift with the scroll
   (site.js sets --drift), and any sticker tilts toward the pointer and lifts
   off the page while it is under it (site.js sets --rx/--ry). With reduced
   motion both stop and every sticker simply sits at its lean.
   ========================================================================== */

.sticker {
  display: block;
  height: auto;
  --tilt: 0deg;
  /* The scroll drift is its own property with no transition: it changes on
     every scroll frame, and inside the transitioned transform each change
     restarted a 0.45s ease, so stickers trailed the finger and the browser
     never stopped animating. The transition is for the pointer lean only. */
  translate: 0 var(--drift, 0px);
  transform:
    perspective(700px)
    rotateX(var(--rx, 0deg))
    rotateY(var(--ry, 0deg))
    rotate(var(--tilt))
    scale(var(--lift, 1));
  transition: transform .45s cubic-bezier(.2, .8, .2, 1), filter .45s ease;
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}
@media (hover: hover) and (pointer: fine) {
  .sticker:hover {
    --lift: 1.08;
    filter: drop-shadow(0 16px 14px rgba(28, 21, 18, .28));
  }
}
@media (prefers-reduced-motion: reduce) {
  .sticker { transition: none; --drift: 0px !important; --rx: 0deg !important; --ry: 0deg !important; }
}

/* ---- hero: six stickers around the Home screen ---- */

.hero-stage {
  position: relative;
  max-width: 860px;
  margin: clamp(48px, 6vw, 72px) auto 0;
}
.hero-sticker {
  position: absolute;
  will-change: translate;
  width: clamp(70px, 12vw, 132px);
}
.hs-1 { left: 3%;   top: 10%; --tilt: -9deg; }
.hs-2 { left: 15%;  top: 47%; --tilt: 7deg;  width: clamp(60px, 10vw, 108px); }
.hs-3 { right: 3%;  top: 18%; --tilt: 10deg; }
.hs-4 { right: 15%; top: 56%; --tilt: -6deg; width: clamp(56px, 9vw, 100px); }
.hs-5 { left: 5%;   top: 78%; --tilt: 4deg; }
.hs-6 { right: 6%;  top: 84%; --tilt: -11deg; width: clamp(60px, 10vw, 110px); }

/* On a phone the screen is sized to fit above the fold with the button, which
   leaves a strip either side of it; four smaller stickers sit there. */
@media (max-width: 640px) {
  .hero-sticker { width: 58px; }
  .hs-2, .hs-4 { display: none; }
  .hs-1 { left: 2%;  top: 8%; }
  .hs-3 { right: 2%; top: 18%; }
  .hs-5 { left: 4%;  top: auto; bottom: 16%; }
  .hs-6 { right: 3%; top: auto; bottom: 4%; width: 58px; }
}

/* ---- showcase: one on the corner of each screenshot ---- */

.show-media { position: relative; }
.show-sticker {
  position: absolute;
  z-index: 1;
  top: -34px;
  width: clamp(76px, 9vw, 112px);
}
.sk-l { left: -34px;  --tilt: -8deg; }
.sk-r { right: -34px; --tilt: 9deg; }
@media (max-width: 860px) {
  .show-sticker { width: 72px; top: -24px; }
  .sk-l { left: -20px; }
  .sk-r { right: -20px; }
}

/* ---- kitchen cards: one per card, like the app's shelf headings ---- */

.k-sticker {
  width: 60px;
  margin: -6px 0 12px -6px;
  --tilt: -6deg;
}
.kitchen-card:nth-child(3n+2) .k-sticker { --tilt: 7deg; }
.kitchen-card:nth-child(3n)   .k-sticker { --tilt: -3deg; }

/* ---- plans: the paywall's own stickers (Core, Lite, Pro) ---- */

.plan-sticker {
  position: absolute;
  top: -24px;
  right: 14px;
  width: 78px;
  --tilt: 8deg;
}
.plan:nth-child(odd) .plan-sticker { --tilt: -7deg; }

/* ---- the closing band: a small pile above the headline ---- */

.cta-stickers {
  display: flex;
  justify-content: center;
  margin-bottom: 26px;
}
.cta-sticker { width: clamp(70px, 9vw, 96px); margin-inline: -8px; }
.cs-1 { --tilt: -12deg; }
.cs-2 { --tilt: 6deg;  margin-top: 14px; }
.cs-3 { --tilt: -4deg; }
.cs-4 { --tilt: 11deg; margin-top: 10px; }

/* A raw app capture, so it gets a screen's shape: the rounded corners and a
   hairline rim so the black status bar does not dissolve into the page. */
.screen {
  position: relative;
  border-radius: clamp(28px, 9%, 40px) / clamp(28px, 4.2%, 40px);
  overflow: hidden;
  background: #000;
  border: 2px solid var(--outline);
  box-shadow: 6px 8px 0 var(--outline), var(--shadow-panel);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .screen { border-color: var(--line-2); box-shadow: var(--shadow-panel); }
}
:root[data-theme="dark"] .screen { border-color: var(--line-2); box-shadow: var(--shadow-panel); }
.screen::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--line-2);
  pointer-events: none;
}
.screen img { width: 100%; }

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

section {
  position: relative;
  scroll-margin-top: 84px;
}
.section { padding: clamp(64px, 9vw, 116px) 0; }
.section--alt {
  background: var(--background-alt);
  border-block: 1px solid var(--line);
}

.section-head { max-width: 62ch; }
.section-head--center { margin-inline: auto; text-align: center; }

.eyebrow {
  margin: 0 0 14px;
  font-size: 12.5px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--primary-text);
}
.section-title {
  font-size: clamp(2rem, 4.6vw, 3rem);
  line-height: 1.1;
  margin: 0;
}
.section-lede {
  margin: 18px 0 0;
  color: var(--text-secondary);
  font-size: 1.05rem;
}

/* ------------------------------------------------------------ feature grid */

.feature-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 20px;
  margin-top: 52px;
}
/* Five cards: three across, then two centred underneath, rather than an orphan. */
@media (min-width: 900px) {
  .feature-grid--five { grid-template-columns: repeat(6, 1fr); }
  .feature-grid--five > * { grid-column: span 2; }
  .feature-grid--five > :nth-child(4) { grid-column: 2 / span 2; }
  .feature-grid--five > :nth-child(5) { grid-column: 4 / span 2; }
}
.feature-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 30px 28px 32px;
  box-shadow: var(--shadow-soft);
}
.feature-card h3 {
  font-family: var(--font-display);
  font-size: 1.2rem;
  font-weight: 700;
  margin: 20px 0 8px;
  letter-spacing: -.005em;
}
.feature-card p {
  margin: 0;
  color: var(--text-secondary);
  font-size: .97rem;
}
.f-icon {
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  border-radius: var(--radius);
  background: var(--primary);
  border: 1.5px solid var(--outline);
  color: var(--on-primary);
}
.f-icon svg { width: 22px; height: 22px; }

/* ---------------------------------------------------------------- showcase */

.showcase {
  display: grid;
  gap: clamp(72px, 10vw, 128px);
  margin-top: clamp(56px, 8vw, 92px);
}

.show-row {
  display: grid;
  grid-template-columns: 1fr minmax(260px, 360px);
  align-items: center;
  gap: clamp(34px, 6vw, 84px);
}
/* Mirrored rows need the track sizes swapped too, not just the order —
   otherwise the panel lands in the wide track and sits in a pool of space. */
.show-row:nth-child(even) { grid-template-columns: minmax(260px, 360px) 1fr; }
.show-row:nth-child(even) .show-media { order: -1; }

.show-copy h3 {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -.018em;
  font-size: clamp(1.7rem, 3.6vw, 2.4rem);
  line-height: 1.12;
  margin: 0 0 16px;
}
.show-copy > p:not(.eyebrow) { margin: 0; color: var(--text-secondary); font-size: 1.04rem; }

.show-list {
  margin: 26px 0 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 12px;
}
.show-list li {
  position: relative;
  padding-left: 26px;
  color: var(--text-secondary);
  font-size: .97rem;
}
.show-list li::before {
  content: "";
  position: absolute;
  left: 4px;
  top: .62em;
  width: 8px;
  height: 8px;
  border-radius: 2px;
  background: var(--primary-text);
}

@media (max-width: 860px) {
  .show-row,
  .show-row:nth-child(even) { grid-template-columns: 1fr; }
  .show-row .show-media,
  .show-row:nth-child(even) .show-media {
    order: -1;
    width: min(320px, 80vw);
    margin-inline: auto;
  }
}

/* ------------------------------------------------------------ heat badges */

/* The app's freshness scale (DESIGN.md §3): a tinted pill with an icon and a
   word, never colour alone. Used on the Kitchen row of the showcase. */
.heat-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 24px 0 0;
  padding: 0;
  list-style: none;
}
.heat {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 28px;
  padding: 3px 11px 3px 9px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
}
.heat svg { width: 15px; height: 15px; flex: none; }
.heat--fresh     { color: var(--fresh);     background: var(--fresh-tint); }
.heat--use-soon  { color: var(--use-soon);  background: var(--use-soon-tint); }
.heat--going-bad { color: var(--going-bad); background: var(--going-bad-tint); }
.heat--expired   { color: var(--expired);   background: var(--expired-tint); }

/* Plans open with the app's own paywall beside the intro, phone narrower
   than the showcase's, and the words first on a phone. */
.plans-intro { grid-template-columns: 1fr minmax(220px, 300px); }
@media (max-width: 860px) {
  .plans-intro { grid-template-columns: 1fr; }
  .plans-intro .show-media { order: 0; width: min(280px, 72vw); margin-inline: auto; }
}

/* --------------------------------------------------------------- kitchen */

.kitchen-grid {
  display: grid;
  /* 320px min keeps nine cards as an even 3 x 3 on desktop */
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 20px;
  margin-top: 52px;
}
.kitchen-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 28px 26px 30px;
  box-shadow: var(--shadow-soft);
}
.kitchen-card h3 {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-size: 1.12rem;
  font-weight: 700;
  margin: 0 0 9px;
}
.kitchen-card p { margin: 0; font-size: .95rem; color: var(--text-secondary); }
.k-tag {
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .07em;
  text-transform: uppercase;
  background: var(--primary-tint);
  border: 1px solid var(--primary-line);
  color: var(--primary-text);
}
/* for a card whose feature is real but not proven yet */
.k-tag--soft {
  background: var(--tint);
  border-color: var(--line-2);
  color: var(--text-secondary);
}

/* ----------------------------------------------------------------- plans */

.plans-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(248px, 1fr));
  gap: 20px;
  margin-top: 52px;
}
.plan {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 28px 26px 30px;
}
.plan { box-shadow: var(--shadow-soft); }
/* Pro is the paywall's highlighted plan, so it gets the sticker edge here too. */
.plan--pick { border: 1.5px solid var(--outline); box-shadow: 4px 4px 0 var(--outline); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .plan--pick { box-shadow: none; }
}
:root[data-theme="dark"] .plan--pick { box-shadow: none; }

.plan-kind {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--text-tertiary);
}
.plan--ai .plan-kind { color: var(--primary-text); }
/* Butter badge, as on the paywall. */
.plan--beta .plan-kind {
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
}

.plan-name {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.6rem;
  letter-spacing: -.015em;
  margin: 8px 0 10px;
}

/* price is the headline; the coin allowance sits under it as the detail */
.plan-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 8px;
  min-height: 40px;
  margin-bottom: 16px;
}
.plan-amount {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 2.15rem;
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--text-primary);
}
.plan-period { font-size: .88rem; color: var(--text-tertiary); }
.plan-allowance {
  display: flex;
  align-items: baseline;
  gap: 8px;
  /* keeps the rule under each allowance on the same line across all four
     cards, even when a longer label wraps */
  min-height: 46px;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.plan-num {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--primary-text);
}
.plan-num--none { color: var(--text-tertiary); font-size: 1.2rem; }
.plan-unit { font-size: .86rem; color: var(--text-tertiary); }
.plan p { margin: 0; font-size: .94rem; color: var(--text-secondary); }

.plans-note {
  margin: 30px 0 0;
  max-width: 78ch;
  font-size: .9rem;
  color: var(--text-tertiary);
}

/* ------------------------------------------------------------ good to know */

.notes-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
  margin-top: 48px;
}
.note {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: 26px 26px 28px;
  box-shadow: var(--shadow-soft);
}
.note h3 { font-family: var(--font-display); font-size: 1.08rem; font-weight: 700; margin: 0 0 8px; }
.note p  { margin: 0; font-size: .94rem; color: var(--text-secondary); }

/* ------------------------------------------------------------ proof strip */

/* Directly under the hero, whose own bottom padding is the gap. The cards
   are .kitchen-card; these rules only add the photo. */
.proof { padding-top: 0; }
.proof-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 20px;
  margin-top: 36px;
}
.kitchen-card.proof-card { padding: 0; overflow: hidden; }
.proof-photo { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.proof-body { padding: 18px 22px 22px; }
.proof-card h3 { margin-bottom: 4px; }

/* A phone gets a compact list, photo beside the words, so three recipes
   are one glance rather than three screens of scrolling. */
@media (max-width: 640px) {
  .proof-grid { gap: 12px; margin-top: 28px; }
  .kitchen-card.proof-card {
    display: grid;
    grid-template-columns: 96px 1fr;
    align-items: center;
    padding: 0;
  }
  .proof-photo { height: 100%; aspect-ratio: 1; }
  .proof-body { padding: 14px 18px; }
}

/* ----------------------------------------------------------- founder note */

.founder {
  display: flex;
  align-items: center;
  gap: clamp(20px, 4vw, 40px);
  max-width: 62ch;
}
.founder-photo {
  flex: none;
  width: clamp(96px, 16vw, 144px);
  aspect-ratio: 1;
  object-fit: cover;
  border-radius: 50%;
  box-shadow: var(--shadow-panel);
}
@media (max-width: 640px) {
  .founder { flex-direction: column; align-items: flex-start; }
}

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

/* The questions are drawn by site.js from config.js; without scripting
   there are none, so the heading would sit over nothing. */
html:not(.js) #faq { display: none; }

.faq { margin-top: 48px; display: grid; gap: 12px; }
.faq-item {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  box-shadow: var(--shadow-soft);
}
.faq-q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 20px 24px;
  background: none;
  border: 0;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1.02rem;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.faq-icon {
  flex: none;
  width: 18px;
  height: 18px;
  position: relative;
}
.faq-icon::before,
.faq-icon::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 2px;
  border-radius: 2px;
  background: var(--primary-text);
  transform: translateY(-50%);
}
.faq-icon::after { transform: translateY(-50%) rotate(90deg); }
.faq-q[aria-expanded="true"] .faq-icon::after { display: none; }

/* `hidden` rather than a collapsed height: a closed answer must be out of
   the tab order and the accessibility tree, not just clipped out of sight. */
.faq-a { display: none; }
.faq-q[aria-expanded="true"] + .faq-a { display: block; }

/* No scripting, no accordion — so leave every answer open rather than leaving
   the whole FAQ permanently shut with a button that does nothing. */
html:not(.js) .faq-a { display: block; }
html:not(.js) .faq-icon { display: none; }
html:not(.js) .faq-q { cursor: default; }
.faq-a p {
  margin: 0;
  padding: 0 24px 22px;
  color: var(--text-secondary);
  font-size: .97rem;
  max-width: 74ch;
}

/* ---------------------------------------------------------------- cta band */

.cta-band {
  text-align: center;
  padding: clamp(70px, 10vw, 124px) 0;
  background: var(--background-alt);
  border-top: 1px solid var(--line);
}
.cta-band .section-title { max-width: 18ch; margin-inline: auto; }
.cta-band .section-lede  { max-width: 52ch; margin-inline: auto; }
.cta-band .hero-ctas { margin-top: 32px; }

/* ------------------------------------------------------ sticky CTA (phones) */

/* The same translucent ink as the header, so the two bars read as one
   frame. Slides up from the bottom edge once site.js adds .is-shown; the
   delayed visibility on the way out keeps it out of the tab order and the
   accessibility tree whenever it is off screen. */
.sticky-cta { display: none; }
@media (max-width: 640px) {
  .sticky-cta {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    padding: 10px 20px calc(10px + env(safe-area-inset-bottom));
    background: var(--header-bg);   /* no blur, same reason as the header */
    border-top: 1px solid var(--line);
    visibility: hidden;
    transform: translateY(100%);
    transition: transform .25s ease, visibility 0s linear .25s;
  }
  .sticky-cta.is-shown {
    visibility: visible;
    transform: none;
    transition: transform .25s ease, visibility 0s;
  }
  .sticky-cta .btn { width: 100%; }

  /* Room under the footer for the bar, so it never covers the last links. */
  .has-sticky-cta body { padding-bottom: calc(76px + env(safe-area-inset-bottom)); }
}
@media (prefers-reduced-motion: reduce) {
  .sticky-cta, .sticky-cta.is-shown { transition: none; }
}

/* ----------------------------------------------------------------- footer */

.site-footer {
  padding: 56px 0 44px;
  border-top: 1px solid var(--line);
  background: var(--background);
}
.footer-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 20px 34px;
}
.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  margin-left: auto;
}
.footer-links a {
  display: inline-flex;
  align-items: center;
  /* 44px, Apple's minimum touch target. WCAG 2.2 AA only asks for 24, but
     most visitors arrive on a phone. */
  min-height: 44px;
  color: var(--text-secondary);
  text-decoration: none;
  font-size: 14.5px;
}
.footer-links a:hover { color: var(--text-primary); }
.footer-copyright {
  width: 100%;
  margin: 26px 0 0;
  color: var(--text-tertiary);
  font-size: 13.5px;
}

/* ==========================================================================
   Mobile navigation
   A three-line button in the header opens a full-width panel with the
   section links. The panel lives inside <header> so it inherits the sticky
   stacking context — no z-index fight with the rest of the page.
   ========================================================================== */

.nav-toggle {
  display: none;
  flex: none;
  margin-left: auto;
  width: 46px;
  height: 46px;
  align-items: center;
  justify-content: center;
  padding: 0;
  border-radius: 14px;
  border: 1px solid var(--line-2);
  background: var(--tint);
  color: var(--text-primary);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.nav-toggle[aria-expanded="true"] {
  background: var(--primary-tint);
  border-color: var(--primary-line);
}

.nav-toggle-bars {
  position: relative;
  display: block;
  width: 20px;
  height: 14px;
}
.nav-toggle-bars span {
  position: absolute;
  left: 0;
  right: 0;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
}
.nav-toggle-bars span:nth-child(1) { top: 0; }
.nav-toggle-bars span:nth-child(2) { top: 6px; }
.nav-toggle-bars span:nth-child(3) { top: 12px; }

.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(1) {
  transform: translateY(6px) rotate(45deg);
}
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] .nav-toggle-bars span:nth-child(3) {
  transform: translateY(-6px) rotate(-45deg);
}

/* ---- the panel ---- */

.mobile-nav {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  z-index: 2;
  display: none;
  /* vh first: dvh is Safari 15.4 / Chrome 108, and where it is not understood
     the declaration is dropped and the panel loses its cap entirely. */
  max-height: calc(100vh - 62px);
  max-height: calc(100dvh - 62px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 10px 24px calc(26px + env(safe-area-inset-bottom));
  /* fully opaque, so nothing on the page ghosts through */
  background: var(--background);
  border-bottom: 1px solid var(--line-2);
  box-shadow: var(--shadow-panel);
  /* hidden with visibility, so its links drop out of the tab order on their own */
  visibility: hidden;
}
.nav-open .mobile-nav { visibility: visible; }

.mobile-nav-list {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: 2px;
}
.mobile-nav-list a {
  display: block;
  padding: 13px 12px;
  border-radius: 14px;
  color: var(--text-primary);
  font-size: 1.06rem;
  font-weight: 600;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
}
.mobile-nav-list a:active { background: var(--tint-2); }

.mobile-nav-foot {
  margin-top: 18px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 14px;
}
.mobile-nav-foot .btn { width: 100%; }
.mobile-nav-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 18px;
  font-size: 14px;
}
.mobile-nav-meta a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--text-secondary);
  text-decoration: none;
}
.mobile-nav-meta .social-link { color: var(--primary-text); }

/* ---- scrim (outside the header, so its position:fixed covers the page) - */

.nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 45;
  background: var(--scrim);
  visibility: hidden;
}
.nav-open .nav-scrim { visibility: visible; }

/* ---- the swap ---- */

@media (max-width: 880px) {
  .site-nav .nav-link { display: none; }
  .site-nav { --nav-gap: 12px; margin-left: auto; }
  .nav-toggle { display: inline-flex; margin-left: 12px; }
  .mobile-nav { display: block; }
}
@media (max-width: 540px) {
  /* wordmark + CTA + toggle stops fitting; the panel carries the CTA. With
     the nav gone, nothing else pushes the toggle to the right edge. */
  .site-nav { display: none; }
  .nav-toggle { margin-left: auto; }
}

/* NO scroll lock here, deliberately.
   overflow:hidden breaks position:sticky whether it is put on <body> (which
   becomes a scroll container) or on <html> (which stops being one) — either
   way the header unsticks and jumps to the top of the document, taking the
   panel with it. Since the panel is a child of the sticky header it stays
   pinned under it regardless, and `overscroll-behavior: contain` stops its own
   scrolling from chaining to the page. */

/* Without scripting the toggle cannot open anything, so fall back to the plain
   links wrapping across the header rather than leaving no navigation at all.
   (The .js class comes from an inline <head> script, so this is a last resort.) */
html:not(.js) .nav-toggle { display: none; }
@media (max-width: 880px) {
  html:not(.js) .site-header .wrap { flex-wrap: wrap; padding-block: 10px; }
  html:not(.js) .site-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 18px;
    margin-left: 0;
    width: 100%;
  }
  html:not(.js) .site-nav .nav-link { display: inline-block; }
}

/* ==========================================================================
   Social
   ========================================================================== */

.social-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}
.social-link svg { width: 17px; height: 17px; flex: none; }

.icon-btn {
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: 12px;
  border: 1px solid var(--line-2);
  background: var(--tint);
  color: var(--text-secondary);
}
.icon-btn svg { width: 18px; height: 18px; }
.icon-btn:hover {
  color: var(--text-primary);
  border-color: var(--primary-line);
}

/* ==========================================================================
   Mobile polish
   ========================================================================== */

@media (max-width: 640px) {
  body { font-size: 16.5px; }
  .wrap { padding-inline: 20px; }

  /* Above the fold on a ~390 x 750 phone: status line, headline, subhead,
     the phone and the button, with the button in the lower half where a
     thumb is. Order does it: the phone moves up between the subhead and
     the button, and is sized from the viewport height so the button still
     lands on screen. The header already carries the wordmark, so the big
     one steps aside here; it stays on wider screens. */
  .hero { padding-top: 20px; }
  .hero .wrap { display: flex; flex-direction: column; }
  .hero .eyebrow { margin-bottom: 0; }
  .wordmark--hero { display: none; }
  .hero h1 { font-size: 2rem; max-width: 16ch; margin-top: 12px; }
  .hero .subhead { max-width: 40ch; margin-top: 12px; }
  /* Full width, so the stickers sit in the room beside the now smaller
     phone instead of on top of its screen. */
  .hero-stage { order: 1; width: 100%; margin-top: 20px; }
  .hero-ctas { order: 2; margin-top: 20px; }
  .store-badge { order: 3; align-self: center; }
  /* 860 x 1864 screenshot, so width = height x .4614. The height is
     whatever the viewport leaves after the text and the button (~470px),
     kept between 150px and 300px wide. svh after vh: without svh support
     the vh line stands. */
  .hero-shot {
    width: clamp(150px, calc((100vh - 470px) * .4614), min(300px, 74vw));
    width: clamp(150px, calc((100svh - 470px) * .4614), min(300px, 74vw));
  }

  .section { padding: 60px 0; }
  .section-head { max-width: none; }

  .feature-grid,
  .kitchen-grid,
  .notes-grid,
  .plans-grid { margin-top: 36px; gap: 14px; }
  .feature-card { padding: 24px 22px 26px; }
  .kitchen-card,
  .plan,
  .note { padding: 22px 20px 24px; }

  .showcase { gap: 64px; }
  .show-list { margin-top: 20px; }

  .faq { margin-top: 34px; }
  .faq-q { padding: 17px 18px; font-size: .99rem; }
  .faq-a p { padding: 0 18px 20px; }

  .footer-inner { gap: 18px 24px; }
  .footer-links { margin-left: 0; width: 100%; gap: 16px 22px; }
}

/* On an actual phone the two hero buttons side by side leave each one too
   narrow to read, so give them a line apiece and a thumb-sized target. */
@media (max-width: 520px) {
  .hero-ctas { flex-direction: column; align-items: stretch; }
  .hero-ctas > .btn { width: 100%; padding: 15px 24px; }
  .hero-ctas > .inapp-note { order: 0; }
}

@media (max-width: 400px) {
  .plan-allowance { min-height: 0; }
}

/* ==========================================================================
   3D phone stage
   Markup lives in index.html; assets/scene/phone-scene.js mounts a canvas into
   #phoneSceneMount and toggles .is-active on the captions. Everything here has
   to look right before that bundle arrives, and if it never does.
   ========================================================================== */

.phone-stage {
  position: relative;
  /* 400vh gives the rotation 300vh of scroll: the sticky child holds for the
     first 100vh, and the pin ends exactly where the scroll range does */
  height: 400vh;
  background: var(--background);
  border-block: 1px solid var(--line);
}

.phone-stage-sticky {
  position: sticky;
  top: 0;
  /* dvh, not svh. On a phone the address bar collapses as you scroll, and a
     pin sized in svh stays short, leaving an empty band across the bottom.
     The plain vh line is the fallback and matters more than it looks: an
     unsupported `height: 100dvh` is dropped, leaving `height: auto` on a box
     whose children are all absolute or percentage-sized — it measures zero
     and the whole 400vh section becomes an empty band. */
  height: 100vh;
  height: 100dvh;
  width: 100%;
  overflow: hidden;
  /* keeps the scene below the sticky header rather than under it */
  padding-top: var(--header-h);
}

.phone-scene,
.phone-scene canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* The still is what shows until the canvas has something on it, and the whole
   of the section for anyone the bundle never reaches. */
.phone-scene-still {
  position: absolute;
  inset: var(--header-h) 0 0 0;
  margin: auto;
  width: auto;
  height: min(62vh, 520px);
  max-width: 78vw;
  border-radius: clamp(18px, 3.4vh, 30px);
  box-shadow: var(--shadow-panel);
}
.phone-stage.is-ready .phone-scene-still {
  visibility: hidden;
}

.phone-captions {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  pointer-events: none;
  display: grid;
}
.phone-caption {
  grid-area: 1 / 1;
  align-self: center;
  justify-self: start;
  max-width: 21rem;
  margin-left: clamp(24px, 7vw, 96px);
  display: grid;
  gap: 8px;
  color: var(--text-secondary);
  font-size: 1rem;
  opacity: 0;
  transition: opacity .35s ease;
}
.phone-caption h3 {
  margin: 0;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(1.6rem, 3vw, 2.1rem);
  line-height: 1.1;
  color: var(--text-primary);
}
.phone-caption p { margin: 0; }
.phone-caption .eyebrow { margin: 0; }
.phone-caption.is-active { opacity: 1; }

/* Portrait: the scene frames the phone high and keeps the lower strip clear,
   so the captions go underneath rather than beside. */
@media (max-aspect-ratio: 11/10) {
  .phone-caption {
    justify-self: center;
    align-self: end;
    margin: 0 24px;
    padding-bottom: clamp(28px, 7vh, 72px);
    max-width: 28rem;
    text-align: center;
  }
  .phone-scene-still {
    inset: 0 0 auto 0;
    margin-top: clamp(40px, 9vh, 90px);
    height: min(52vh, 460px);
  }
}

@media (prefers-reduced-motion: reduce) {
  .phone-caption { transition: none; }
}

/* Without scripting the bundle never mounts, so collapse the tall scroll range
   to its content and list all three steps under the still image. */
/* Same for Data Saver, where scene.js never fetches the bundle. */
html:not(.js) .phone-stage, .phone-stage.is-static { height: auto; }
html:not(.js) .phone-stage-sticky, .is-static .phone-stage-sticky { position: static; height: auto; padding: 48px 0; }
html:not(.js) .phone-scene, .is-static .phone-scene { display: none; }
html:not(.js) .phone-scene-still, .is-static .phone-scene-still { position: static; margin: 0 auto 32px; }
html:not(.js) .phone-captions, .is-static .phone-captions { position: static; gap: 28px; justify-items: center; }
html:not(.js) .phone-caption, .is-static .phone-caption { grid-area: auto; opacity: 1; margin: 0 24px; padding: 0; text-align: center; }

/* ==========================================================================
   Focus
   Every interactive control needs a visible keyboard focus ring.
   ========================================================================== */

/* Safari only understood :focus-visible from 15.4, and an unknown pseudo-class
   invalidates the whole selector. Ring on plain :focus first, then take it
   back off for browsers that can tell a mouse click from a Tab. */
:focus {
  outline: 2px solid var(--primary-text);
  outline-offset: 3px;
  border-radius: 4px;
}
:focus:not(:focus-visible) { outline: none; }
:focus-visible {
  outline: 2px solid var(--primary-text);
  outline-offset: 3px;
  border-radius: 4px;
}
.btn:focus, .btn:focus-visible,
.nav-toggle:focus, .nav-toggle:focus-visible,
.icon-btn:focus, .icon-btn:focus-visible { outline-offset: 4px; }
.faq-q:focus, .faq-q:focus-visible { outline-offset: -3px; border-radius: var(--radius-lg); }
.mobile-nav-list a:focus, .mobile-nav-list a:focus-visible { outline-offset: -2px; }

/* Keyboard-only shortcut past the header. */
.skip-link {
  position: absolute;
  left: 16px;
  top: 12px;
  z-index: 200;
  padding: 11px 18px;
  border-radius: 999px;
  background: var(--primary-text);
  color: var(--on-primary);
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  transform: translateY(-200%);
}
.skip-link:focus,
.skip-link:focus-visible { transform: none; }

/* ==========================================================================
   Secondary pages — privacy.html, terms.html, credits.html
   The same header and footer as the home page, and one measure of prose
   between them.
   ========================================================================== */

/* The header on these pages has three short links and no sections to scroll
   to, so the menu button is not needed at any width. */
.site-header--page .site-nav .nav-link { display: inline-flex; }
.site-header--page .nav-toggle { display: none; }
@media (max-width: 880px) {
  .site-header--page .site-nav { --nav-gap: 16px; }
  .site-header--page .site-nav .btn { display: none; }
}
@media (max-width: 540px) {
  /* The home page hides the whole nav here and relies on the menu; this
     header has none, so the nav stays and loses the Instagram button. */
  .site-header--page .site-nav { display: flex; }
  .site-header--page .site-nav .icon-btn { display: none; }
  .site-header--page .site-nav a:not(.btn) { font-size: 14px; }
}

.page {
  padding: clamp(40px, 7vw, 80px) 0 clamp(64px, 9vw, 120px);
}
.page .wrap { max-width: 72ch; }

.page-head { margin: 0 0 clamp(28px, 4vw, 40px); }
.page-title {
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(34px, 5.2vw, 52px);
  line-height: 1.06;
  letter-spacing: -.02em;
  margin: 8px 0 12px;
  color: var(--text-primary);
}
.page-updated {
  margin: 0;
  color: var(--text-tertiary);
  font-size: 14px;
}
.page-lede {
  margin: 20px 0 0;
  font-size: 1.08rem;
  line-height: 1.65;
  color: var(--text-secondary);
}

.prose { font-size: 1.02rem; line-height: 1.7; color: var(--text-secondary); }
.prose h2 {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: clamp(22px, 2.6vw, 26px);
  line-height: 1.2;
  letter-spacing: -.01em;
  color: var(--text-primary);
  margin: 44px 0 12px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}
.prose p { margin: 0 0 14px; }
.prose ul { margin: 0 0 16px; padding-left: 22px; }
.prose li { margin: 0 0 10px; }
.prose li::marker { color: var(--primary-text); }
.prose strong { color: var(--text-primary); font-weight: 600; }
.prose a { color: var(--primary-text); text-decoration-thickness: 1px; text-underline-offset: 2px; }
.prose a:hover { color: var(--text-primary); }
.prose dl { margin: 0 0 16px; }
.prose dt { color: var(--text-primary); font-weight: 600; margin: 18px 0 4px; }
.prose dd { margin: 0 0 4px; color: var(--text-tertiary); }

/* Where the page sits in the set: privacy · terms · credits */
.page-siblings {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin: 48px 0 0;
  padding-top: 22px;
  border-top: 1px solid var(--line);
  font-size: 14.5px;
}
.page-siblings a { color: var(--text-secondary); text-decoration: none; min-height: 26px; display: inline-flex; align-items: center; }
.page-siblings a:hover { color: var(--text-primary); }
.page-siblings a[aria-current="page"] { color: var(--primary-text); }

/* ==========================================================================
   Motion
   One-time entrances, set up by motion() in site.js. Nothing here hides
   anything unless html.motion is set, and only site.js sets it, so a page
   whose script never ran is simply all there. Each thing moves once as it
   first comes into view; nothing loops (DESIGN.md §7). The easing has no
   overshoot on purpose: things settle, they do not bounce.
   ========================================================================== */

:root {
  --ease-settle: cubic-bezier(.2, .7, .2, 1);
}

/* Cards and section heads: rise and fade, a beat apart within a grid. */
.motion .reveal {
  transition:
    opacity .7s var(--ease-settle),
    transform .7s var(--ease-settle);
  transition-delay: calc(var(--i, 0) * 70ms);
}
.motion .reveal:not(.is-in) {
  opacity: 0;
  transform: translateY(18px);
}

/* The hero's stickers land on the page one after another. `scale` rather
   than `transform`, which already carries the drift and the pointer lean. */
@keyframes stick-on {
  from { opacity: 0; scale: 1.3; }
  45%  { opacity: 1; }
  to   { opacity: 1; scale: 1; }
}
.motion .hero-sticker {
  animation: stick-on .6s var(--ease-settle) both;
  animation-delay: calc(200ms + var(--i, 0) * 90ms);
}

/* Heat badges pop in 40ms apart (the app's "items detected" stagger), then
   Going bad flickers once, like a badge turning Hot on app open. */
.motion .heat {
  transition: opacity .26s ease, scale .26s var(--ease-settle);
  transition-delay: calc(var(--i, 0) * 40ms);
}
.motion .heat-row:not(.is-in) .heat { opacity: 0; scale: .88; }
@keyframes flicker {
  0%, 100% { opacity: 1; }
  20% { opacity: .35; }
  40% { opacity: 1; }
  60% { opacity: .55; }
}
.motion .heat-row.is-in .heat--going-bad { animation: flicker .6s ease .55s 1; }

/* One band of light across each screenshot, once, the same slow sweep the
   app runs over things it is loading. ::after is the screen's rim. */
@keyframes sweep {
  to { transform: translateX(130%); }
}
.motion .screen::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 244, 234, .16) 50%, transparent 62%);
  transform: translateX(-130%);
}
.motion .screen.is-in::before {
  animation: sweep 1.8s cubic-bezier(.4, 0, .2, 1) .35s 1 both;
}

/* Reduced motion (DESIGN.md §7): the reveals become a 150ms fade with no
   movement, and the landing, the pop, the flicker and the sweep are gone. */
@media (prefers-reduced-motion: reduce) {
  .motion .reveal { transition: opacity .15s linear; transition-delay: 0s; }
  .motion .reveal:not(.is-in) { transform: none; }
  .motion .hero-sticker,
  .motion .heat-row.is-in .heat--going-bad { animation: none; }
  .motion .heat { transition: opacity .15s linear; transition-delay: 0s; }
  .motion .heat-row:not(.is-in) .heat { scale: 1; }
  .motion .screen::before { content: none; }
}
