/* ========================================
   KiWi Recipes: Landing Page Styles
   ======================================== */

/* --- Reset & Variables --- */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --primary: #6B9B37;
  --primary-dark: #527A28;
  --primary-darker: #3F5F1E;
  --primary-light: #C8E6A0;
  --primary-lighter: #E8F5D6;
  --accent: #C88F3C;
  --accent-dark: #A87428;
  --soft-green: #F0F7E8;
  --text-primary: #212121;
  --text-secondary: #555;
  --text-muted: #707070;
  --white: #fff;
  --bg-light: #FAFCF7;
  --border: #E0E0E0;
  --font: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --radius: 12px;
  --radius-lg: 20px;
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
  --shadow-md: 0 4px 16px rgba(0, 0, 0, 0.1);
  --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.12);
  --shadow-phone: 0 20px 60px rgba(0, 0, 0, 0.15);
  --max-width: 1120px;
  --transition: 0.3s ease;
}

html {
  scroll-behavior: smooth;
  -webkit-text-size-adjust: 100%;
}

body {
  font-family: var(--font);
  color: var(--text-primary);
  background: var(--white);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

a {
  color: var(--primary);
  text-decoration: none;
  transition: color var(--transition);
}

a:hover {
  color: var(--primary-dark);
}

/* --- Focus indicators (WCAG 2.1 AA) --- */
*:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

.cta-box *:focus-visible {
  outline-color: var(--white);
}

/* --- Skip navigation link --- */
.skip-link {
  position: absolute;
  top: -100%;
  left: 16px;
  z-index: 1000;
  padding: 12px 24px;
  background: var(--primary);
  color: var(--white);
  border-radius: var(--radius);
  font-weight: 600;
  font-size: 0.95rem;
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 12px;
  color: var(--white);
}

/* --- Utility --- */
.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 24px;
}

.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;
}

/* --- Nav --- */
.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid transparent;
  transition: border-color var(--transition), box-shadow var(--transition);
}

.nav.scrolled {
  border-bottom-color: var(--border);
  box-shadow: var(--shadow-sm);
}

.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 64px;
}

.nav-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

.nav-brand img {
  width: 36px;
  height: 36px;
}

.nav-brand span {
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -0.02em;
  white-space: nowrap;
}

.nav-link {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--text-secondary);
  white-space: nowrap;
  transition: color var(--transition);
}

.nav-link:hover {
  color: var(--text-primary);
}

.nav-cta {
  display: inline-flex;
  align-items: center;
  padding: 8px 20px;
  background: var(--primary-dark);
  color: var(--white);
  border-radius: 100px;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
  transition: background var(--transition), transform var(--transition);
}

.nav-cta:hover {
  background: var(--primary-darker);
  color: var(--white);
  transform: translateY(-1px);
}

/* --- Nav Right --- */
.nav-right {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* --- Language Switcher --- */
.lang-switcher {
  position: relative;
}

.lang-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  min-height: 48px;
  min-width: 48px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 8px;
  cursor: pointer;
  font-family: var(--font);
  color: var(--text-primary);
  transition: border-color var(--transition), background var(--transition);
}

.lang-toggle:hover {
  border-color: var(--primary-light);
  background: var(--soft-green);
}

.lang-toggle img {
  width: 24px;
  height: 14px;
  border-radius: 2px;
  object-fit: cover;
  display: inline-block;
}

.lang-chevron {
  color: var(--text-muted);
  transition: transform var(--transition);
  flex-shrink: 0;
}

.lang-switcher.open .lang-chevron {
  transform: rotate(180deg);
}

.lang-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 150px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity var(--transition), visibility var(--transition), transform var(--transition);
  z-index: 200;
}

.lang-switcher.open .lang-dropdown {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.lang-option {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  font-size: 0.875rem;
  color: var(--text-secondary);
  transition: background var(--transition), color var(--transition);
}

.lang-option:first-child {
  border-radius: var(--radius) var(--radius) 0 0;
}

.lang-option:last-child {
  border-radius: 0 0 var(--radius) var(--radius);
}

.lang-option:hover {
  background: var(--soft-green);
  color: var(--text-primary);
}

.lang-option.active {
  background: var(--primary-lighter);
  color: var(--primary-dark);
  font-weight: 600;
}

.lang-option img {
  width: 24px;
  height: 14px;
  border-radius: 2px;
  object-fit: cover;
  display: inline-block;
  flex-shrink: 0;
}

/* --- Full language picker (legal pages) --- */
.lang-switcher--full .lang-toggle {
  gap: 8px;
}

.lang-flag {
  font-size: 1.15rem;
  line-height: 1;
  flex-shrink: 0;
}

.lang-current {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

.lang-switcher--full .lang-dropdown {
  max-height: 360px;
  overflow-y: auto;
  min-width: 200px;
}

.lang-switcher--full .lang-option {
  padding: 8px 14px;
}

/* --- Hero --- */
.hero {
  padding: 120px 0 80px;
  background: linear-gradient(180deg, var(--soft-green) 0%, var(--white) 100%);
  position: relative;
  overflow: hidden;
}

.hero-inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: 48px;
  align-items: center;
}

.hero-content {
  text-align: center;
}

.hero-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 16px;
  background: var(--white);
  border: 1px solid var(--primary-light);
  border-radius: 100px;
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--primary-dark);
  margin-bottom: 24px;
}

.hero-badge::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--primary);
  animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.6; transform: scale(0.85); }
}

.hero h1 {
  font-size: 2.75rem;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: -0.03em;
  color: var(--text-primary);
  margin-bottom: 16px;
}

.hero h1 .highlight {
  color: var(--primary);
}

/* The differentiator kicker, an <h2> sub-headline. Black setup, green
   payoff, same two-color grammar as the headline (black "Recipes from
   anywhere." + green "Yours everywhere."). Sized between the h1 (2.75rem)
   and the subtitle so it reads as a clear second headline. */
.hero-kicker {
  font-size: 1.75rem;
  font-weight: 800;
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  /* 20px (0.71em at this size), not the 8px it carried before the swash.
     The stroke's bottom edge lands 0.4px BELOW this line's box -- it fills
     the descender space by design -- so the old margin was the entire
     visible gap between wet ink and the caption, and the caption read as
     attached to it. Measured on the rendered page, not estimated. The app's
     welcome hero holds the same 0.71em ratio at its own size. */
  margin: 0 0 20px;
}

/* The "free" beat: green, matching "Yours everywhere." so the two
   differentiators read as a matched pair of payoffs. */
.hero-kicker-free {
  color: var(--primary);
}

/* Hand-drawn swash under the "free" beat.
   ------------------------------------------------------------------------
   PROVENANCE: the same artwork the App Store deck draws (SWASH_SVG in
   store-assets/deck/build-deck.cjs) and the same one the app paints under
   this line on its welcome screen (free_swash_underline.dart). The markup
   is emitted by scripts/generate-landing-pages.mjs, which reads the path
   straight out of the deck builder -- there is ONE copy of this drawing in
   the repo and it is the deck's.

   HOUSE RULE (deck, 2026-07-20): the stroke underlines the "for free" beat
   and ONLY that beat. It is not a general emphasis mechanism.

   🔴 An INLINE <svg>, not a CSS mask. The mask version shipped visibly
   PIXELATED: a mask forces the engine to rasterize the artwork and then
   squash the bitmap, and this viewBox is 300x56 drawn into roughly
   180x9.5px -- a ~5.9x vertical compression of a shape made entirely of
   long, near-horizontal tapered edges, which is the worst case for
   stair-stepping. Inline SVG stays vector, so the geometry is rasterized at
   device resolution and holds up at any DPR and any zoom. The deck emits it
   inline for the same reason.

   COLOUR: --text-primary, the headline's BASE ink -- deliberately NOT the
   green of the run it underlines. Read off the deck: on frame 2 the run
   "For Free." is LIME and SWASH_SVG is fill="#fff", the same white as lines
   1 and 2 of that headline. The stroke is the pen the sentence is written
   with; the accent belongs to the words. It is set as `color` on the <svg>
   itself, NOT on the wrapping span -- the span carries the run's green
   text, and fill="currentColor" resolves against the svg's own colour.

   GEOMETRY mirrors the deck's swashBox(), in em so it tracks font-size
   through the mobile breakpoint:
     width  = the run plus 20% overshoot on each side
     height = 0.70em
     top    = the baseline, plus 0.15em, minus the 10/56 of the box that is
              empty above the ink in the 0 0 300 56 viewBox.

   🔴 The 0.70em and the 20% are BOTH small-size compensation, and both were
   chosen on a side-by-side render of seven variants at real size. The deck
   builder's 0.34em / 8% are POSTER ratios: against a 116px headline they
   give a 3-5px lens with fine points and a 43px sweep past the word, but
   the identical ratios against this 28px kicker give a 0.7px hairline and a
   10px over-run -- a stroke that stair-steps and stops short. Scaling the
   BOX preserves the taper (an added stroke does not: it thickens the thin
   end, which is the one part that must stay thin). The cost is a
   proportionally steeper tilt, since the rise scales with the box too.
   The 0.15em is the DECK'S OWN value for this beat -- frame 2 carries
   swash:{word:'For Free', dyEm:.15}, overriding the builder's generic .03
   default. Shipping the default put the stroke in the descender space of
   the words instead of under them.
   With Nunito (ascent 1.011em, content 1.364em) inside .hero-kicker's
   line-height 1.25, the baseline sits 0.954em below the box top, so
   0.954 + 0.15 - (10/56 x 0.70) = 0.979em.

   The span is inline-block so it has a box for the stroke to measure
   against; that also keeps the two words of the beat on one line, which is
   what the app does by drawing a single stroke under the last line. */
.hero-kicker-free .kicker-swash {
  position: relative;
  display: inline-block;
}

.hero-kicker-free .kicker-swash-ink {
  position: absolute;
  left: -20%;
  width: 140%;
  top: 0.979em;
  height: 0.70em;
  display: block;
  color: var(--text-primary);
  pointer-events: none;
}

/* Substantiation line under the nutrition kicker: quiet, factual, so the
   trust claim is backed rather than asserted. */
.hero-nutri-subtext {
  font-size: 1rem;
  color: var(--text-muted);
  max-width: 520px;
  margin: 0 auto 32px;
  line-height: 1.6;
}

.hero-nutri-subtext sup {
  font-size: 0.6em;
  vertical-align: super;
  line-height: 0;
}

.hero-subtitle {
  font-size: 1.15rem;
  color: var(--text-secondary);
  max-width: 480px;
  margin: 0 auto 32px;
  line-height: 1.7;
}

.hero-subtitle sup {
  font-size: 0.6em;
  vertical-align: super;
  line-height: 0;
}

.hero-phone {
  display: flex;
  justify-content: center;
  align-items: center;
}

/* --- Phone Mockup --- */
/* 🔴 NO aspect-ratio HERE, and that is the fix rather than a tidy-up.
   It used to declare `aspect-ratio: 9 / 18.6`, which works out to an inner screen of
   0.4667 while the deck raws are 1320x2868 = 0.4603. Those cannot both be right, so
   `object-fit: cover` had to crop something: the frame was dictating a shape the
   screenshot does not have, and the screenshot lost the edge. The frame now takes its
   height FROM the image, so no shot can ever be cropped by it, whatever its native
   size. The watch and tablet frames already work this way. */
.phone-frame {
  width: 240px;
  max-width: 100%;
  background: #1a1a1a;
  border-radius: 32px;
  /* 10px, not 8: at 8 the screen sat hard against the bezel and read as a screenshot
     with a border rather than a phone. */
  padding: 10px;
  box-shadow: var(--shadow-phone);
  position: relative;
}

.phone-frame::before {
  content: '';
  position: absolute;
  top: 8px;
  left: 50%;
  transform: translateX(-50%);
  width: 72px;
  height: 20px;
  background: #1a1a1a;
  border-radius: 0 0 10px 10px;
  z-index: 2;
}

.phone-screen {
  width: 100%;
  /* height comes from the image, not from a guess about phone proportions */
  border-radius: 24px;
  background: #fff;
  overflow: hidden;
  position: relative;
  display: block;
}

.phone-screen picture {
  display: block;
  width: 100%;
}

.phone-screen img {
  display: block;
  width: 100%;
  height: auto;
}

/* Placeholder when no screenshot exists */

/* --- Email Signup Form --- */
.signup-form {
  display: flex;
  gap: 10px;
  max-width: 420px;
  margin: 0 auto;
}

.signup-form input {
  flex: 1;
  padding: 14px 18px;
  border: 2px solid var(--border);
  border-radius: var(--radius);
  font-size: 0.95rem;
  font-family: var(--font);
  color: var(--text-primary);
  background: var(--white);
  transition: border-color var(--transition);
}

.signup-form input:focus {
  border-color: var(--primary);
}

.signup-form input::placeholder {
  color: var(--text-muted);
}

.signup-form button {
  padding: 14px 24px;
  background: var(--primary-dark);
  color: var(--white);
  border: none;
  border-radius: var(--radius);
  font-size: 0.95rem;
  font-weight: 600;
  font-family: var(--font);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--transition), transform var(--transition);
}

.signup-form button:hover {
  background: var(--primary-darker);
  transform: translateY(-1px);
}

.signup-form button:disabled {
  opacity: 0.7;
  cursor: not-allowed;
  transform: none;
}

.form-message {
  font-size: 0.85rem;
  margin-top: 10px;
  text-align: center;
  min-height: 20px;
}

.form-message.success {
  color: var(--primary-dark);
}

.form-message.error {
  color: #D32F2F;
}

.form-privacy {
  font-size: 0.75rem;
  color: var(--text-muted);
  margin-top: 12px;
  text-align: center;
}

.hero-footnote {
  font-size: 0.7rem;
  color: var(--text-muted);
  margin-top: 16px;
  text-align: center;
  line-height: 1.4;
}

/* --- Features --- */
.features {
  padding: 80px 0;
}

.section-header {
  text-align: center;
  margin-bottom: 56px;
}

.section-label {
  display: inline-block;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--primary);
  margin-bottom: 12px;
}

.section-header h2 {
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  margin-bottom: 12px;
}

.section-header p {
  font-size: 1.05rem;
  color: var(--text-secondary);
  max-width: 520px;
  margin: 0 auto;
}

.features-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}

.feature-card {
  padding: 32px 28px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  transition: transform var(--transition), box-shadow var(--transition);
}

.feature-card:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-md);
}

.feature-icon {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--soft-green);
}

/* The glyph and the Pro mark share one row, the mark at the INLINE end so it reads as a
   trailing label on the tile - the same position MenuRowProMark takes on a menu row in
   the app. `space-between` rather than a margin so RTL flips it without a second rule. */
.feature-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 16px;
}

.feature-icon svg {
  width: 24px;
  height: 24px;
  color: var(--primary);
}

.feature-card h3 {
  font-size: 1.1rem;
  font-weight: 700;
  margin-bottom: 8px;
  letter-spacing: -0.01em;
}

.feature-card p {
  font-size: 0.925rem;
  color: var(--text-secondary);
  line-height: 1.6;
}

/* --- Screenshots --- */
.screenshots {
  padding: 80px 0;
  background: var(--bg-light);
}

.screenshots-row {
  display: flex;
  gap: 24px;
  overflow-x: auto;
  padding: 0 24px 16px;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scroll-snap-type: x mandatory;
}

.screenshots-row::-webkit-scrollbar {
  display: none;
}

.screenshot-item {
  flex-shrink: 0;
  scroll-snap-align: center;
  text-align: center;
}

.screenshot-item .phone-frame {
  width: 220px;
  margin-bottom: 12px;
}

.screenshot-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
}

/* --- CTA Section --- */
.cta-section {
  padding: 80px 0;
}

.cta-box {
  background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
  border-radius: var(--radius-lg);
  padding: 56px 32px;
  text-align: center;
  position: relative;
  overflow: hidden;
}

.cta-box::before {
  content: '';
  position: absolute;
  top: -40%;
  right: -20%;
  width: 400px;
  height: 400px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.06);
}

.cta-box::after {
  content: '';
  position: absolute;
  bottom: -30%;
  left: -10%;
  width: 300px;
  height: 300px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.04);
}

.cta-box > * {
  position: relative;
  z-index: 1;
}

.cta-box h2 {
  font-size: 2rem;
  font-weight: 800;
  color: var(--white);
  margin-bottom: 12px;
  letter-spacing: -0.02em;
}

.cta-box p {
  font-size: 1.05rem;
  color: rgba(255, 255, 255, 0.85);
  max-width: 420px;
  margin: 0 auto 32px;
}

.cta-box .signup-form {
  max-width: 400px;
  margin: 0 auto;
}

.cta-box .signup-form input {
  border-color: rgba(255, 255, 255, 0.3);
  background: rgba(255, 255, 255, 0.12);
  color: var(--white);
}

.cta-box .signup-form input::placeholder {
  color: rgba(255, 255, 255, 0.85);
}

.cta-box .signup-form input:focus {
  border-color: rgba(255, 255, 255, 0.6);
  background: rgba(255, 255, 255, 0.18);
}

.cta-box .signup-form button {
  background: var(--white);
  color: var(--primary-dark);
}

.cta-box .signup-form button:hover {
  background: var(--primary-lighter);
}

.cta-box .form-message.success {
  color: var(--primary-lighter);
}

.cta-box .form-message.error {
  color: #FFCDD2;
}

.cta-box .form-privacy {
  color: rgba(255, 255, 255, 0.75);
}

.cta-box .form-privacy a {
  color: rgba(255, 255, 255, 0.9);
  text-decoration: underline;
}

/* --- Footer --- */
.footer {
  padding: 40px 0;
  border-top: 1px solid var(--border);
}

.footer-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  text-align: center;
}

.footer-brand {
  display: flex;
  align-items: center;
  gap: 8px;
}

.footer-brand img {
  width: 28px;
  height: 28px;
}

.footer-brand span {
  font-size: 1rem;
  font-weight: 700;
  color: var(--text-primary);
}

.footer-links {
  display: flex;
  gap: 24px;
}

.footer-links a {
  font-size: 0.85rem;
  color: var(--text-muted);
}

.footer-links a:hover {
  color: var(--primary);
}

.footer-copyright {
  font-size: 0.8rem;
  color: var(--text-muted);
}

.footer-trademarks {
  font-size: 0.7rem;
  color: var(--text-muted);
  margin-top: 4px;
}

/* --- In-app mode (loaded from mobile app) --- */
.in-app .nav,
.in-app .footer-brand,
.in-app .footer-copyright { display: none; }

.in-app .footer-links a:not([href*="legal-notice"]) { display: none; }

/* --- Fade-in Animation ---
 *
 * 🔴 THE HIDING IS SCOPED TO `html.js` ON PURPOSE, AND MUST STAY THAT WAY.
 * `.fade-in { opacity: 0 }` used to be unconditional, and the only thing that ever
 * set it back was an IntersectionObserver at the bottom of js/main.js. main.js
 * statically imported firebase-config.js, which pulls three bundles from
 * www.gstatic.com and top-level-awaits /__/firebase/init.json, so ANY of those
 * failing meant main.js never executed, the observer never attached, and all ten
 * `.fade-in` sections stayed invisible. Features, screenshots, CTA, footer: gone,
 * with the hero left stranded above a blank page and nothing on screen to explain it.
 *
 * Three independent guards now, because this one fails silently and expensively:
 *   1. main.js imports Firebase lazily, so the CDN is off the critical path.
 *   2. The hiding requires `html.js`, set by an inline head script. No JS, or a
 *      script that 404s, means the content was never hidden in the first place.
 *   3. `html.reveal` is a timed failsafe armed by that same inline script and
 *      CANCELLED by main.js once the observer is attached, so it fires only when
 *      main.js genuinely did not get that far. It is not a blanket timer: a slow
 *      reader still gets the scroll animation.
 */
html.js .fade-in {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}

html.js .fade-in.visible,
html.reveal .fade-in {
  opacity: 1;
  transform: translateY(0);
}

/* --- Responsive --- */

@media (max-width: 480px) {
  .nav-cta {
    padding: 6px 12px;
    font-size: 0.75rem;
  }

  .nav-right {
    gap: 8px;
  }

  .hero h1 {
    font-size: 2rem;
  }

  .hero-kicker {
    font-size: 1.45rem;
  }

  .hero-subtitle {
    font-size: 1rem;
  }

  .signup-form {
    flex-direction: column;
  }

  .signup-form button {
    width: 100%;
  }

  .cta-box .signup-form {
    flex-direction: column;
  }

  .cta-box {
    padding: 40px 20px;
  }

  .cta-box h2 {
    font-size: 1.5rem;
  }
}

@media (min-width: 640px) {
  .features-grid {
    grid-template-columns: repeat(2, 1fr);
  }

  .screenshots-row {
    /* 🔴 TWO DECLARATIONS, AND THE FIRST IS THE LOAD-BEARING ONE.
       A bare `justify-content: center` on a flex row with `overflow-x: auto` pushes
       the overflow out BOTH sides, and scrollLeft cannot go negative, so the leading
       items become permanently unreachable. It was invisible while this row held four
       placeholder boxes that fitted; six real screenshots do not fit, and the first
       one could not be scrolled to at all.
       `safe center` is exactly the keyword for this: it centres while the content
       fits and falls back to flex-start the moment it would overflow. The plain
       flex-start above it is the floor for anything that does not understand `safe`,
       which is left-aligned and scrollable rather than pretty and broken. */
    justify-content: flex-start;
    justify-content: safe center;
  }
}

@media (min-width: 768px) {
  .hero {
    padding: 140px 0 100px;
  }

  .hero-inner {
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    text-align: left;
  }

  .hero-content {
    text-align: left;
  }

  .hero h1 {
    font-size: 3.25rem;
  }

  .hero-subtitle {
    margin-left: 0;
  }

  .signup-form {
    margin: 0;
  }

  .form-message,
  .form-privacy {
    text-align: left;
  }

  .hero-phone {
    justify-content: center;
  }

  .section-header h2 {
    font-size: 2.25rem;
  }

  .footer-inner {
    flex-direction: row;
    justify-content: space-between;
  }
}

@media (min-width: 1024px) {
  .hero h1 {
    font-size: 3.5rem;
  }

  .features-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  .cta-box {
    padding: 72px 48px;
  }

  .cta-box h2 {
    font-size: 2.5rem;
  }

  .screenshot-item .phone-frame {
    width: 240px;
  }
}

/* =========================================================================
   Launch overhaul, 2026-08-20. Sections added to carry the store messaging the
   site never had: the nutrition proof, the device reach, and an honest free vs
   Pro statement.

   These extend the existing vocabulary on purpose - .section-header,
   .phone-frame, .feature-card, the --primary / --soft-green / --radius tokens -
   rather than introducing a parallel one. The single new colour role is
   --accent, which was already defined in :root and used exactly ZERO times in
   1,123 lines. It marks Pro, so the paid tier reads as different without any
   literal being invented.

   Written with LOGICAL properties (inline-start/end, margin-inline) because the
   site ships ar, he and ur, and the stylesheet's physical left/right rules are a
   known RTL defect class in this repo.
   ========================================================================= */

/* --- App screenshots inside a phone frame --- */

/* 🔴 NO CSS NOTCH ON A REAL CAPTURE. `.phone-frame::before` paints a black pill at the
   top of the frame, which was right when this component held a flat placeholder. The
   deck raws are real device captures: they already contain the status bar AND the
   Dynamic Island, painted into the image. Drawing the CSS notch over them gives the
   phone two notches, which is the "double bezel" the frame is supposed to prevent.
   The frame itself stays; only its invented notch goes. */
.phone-frame-shot::before {
  content: none;
}

/* 🔴 `height: auto`, NOT `height: 100%` with cover. The whole screenshot is shown, top
   to bottom, because a marketing page that crops its own product screenshot loses
   exactly the edge content the screenshot was chosen for: the third column of the
   recipe grid, the last row of the nutrient list. */
.phone-screen picture,
.phone-screen img {
  display: block;
  width: 100%;
  height: auto;
}

/* --- The Pro mark, ported from the app rather than invented --------------
 *
 * 🔴 THIS WAS AN AMBER BORDER AND A TINTED CARD, AND THAT WAS THE MISTAKE THE APP HAD
 * ALREADY MADE AND FIXED. lib/core/widgets/premium_badge.dart says so in its own header:
 * the Pro mark used to be orange, and orange is `context.colors.warning`, the same token
 * the nutrition surface uses for "High sodium", "High saturated fat" and every ! badge.
 * A reader who has learned that orange means something is wrong reads a paid feature the
 * same way, so the product was flagging its own Pro features as problems. I reinvented
 * that from scratch on the website.
 *
 * The shipped mark is an OUTLINED WORDMARK with NO GLYPH and NEUTRAL INK, and each of
 * those three is a decision with a reason:
 *   - no orange, because warning owns it;
 *   - no glyph, because a star is already RATING and FAVOURITE, a sparkle is already AI,
 *     a padlock says "you cannot" on a control built to be tapped, and a crown means
 *     nothing in this product;
 *   - neutral ink, because orange, red, green, violet and blue are all spoken for and a
 *     sixth hue invented for Pro would collide with whatever ships next. The BORDER
 *     carries the identity.
 *
 * Ported values: 1px outline, radius = spacing.xs, caption size, w600, 0.4 letter
 * spacing, secondary text colour, padding sm horizontal and xs vertical because at xs on
 * both axes the word crowds its own border.
 */
.pro-mark {
  display: inline-block;
  flex: 0 0 auto;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.4px;
  line-height: 1;
  color: var(--text-secondary);
  text-transform: none;
}

/* --- Proof --- */
.proof {
  padding: 96px 0;
  background: var(--soft-green);
}

.proof-inner {
  display: grid;
  grid-template-columns: 1fr;
  gap: 40px;
  align-items: center;
  justify-items: center;
}

.proof-shot .phone-frame {
  width: 280px;
}

.proof-points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 28px;
  max-width: 34rem;
}

.proof-points li {
  padding-inline-start: 20px;
  border-inline-start: 3px solid var(--primary);
}

.proof-points h3 {
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 6px;
  letter-spacing: -0.01em;
}

.proof-points p {
  font-size: 0.95rem;
  color: var(--text-secondary);
  line-height: 1.65;
}

/* --- Devices --- */
.devices {
  padding: 96px 0;
}

.devices-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: center;
  gap: 32px;
}

.device-item {
  margin: 0;
  text-align: center;
}

/* Each frame is the real bezel for its machine, so the three read as three DEVICES
   rather than three pictures at different sizes. Widths are deliberately unequal:
   a Mac beside an iPhone at the same width is not a device family, it is a grid. */
.device-frame {
  background: #1a1a1a;
  box-shadow: var(--shadow-phone);
  margin-inline: auto;
  overflow: hidden;
}

.device-frame img,
.device-frame picture {
  display: block;
  width: 100%;
  height: auto;
}

.device-frame-laptop {
  width: 100%;
  max-width: 520px;
  padding: 12px 12px 0;
  border-radius: 12px 12px 4px 4px;
}

/* The lip under a laptop screen. Drawn rather than photographed so it scales cleanly
   and needs no second asset. */
.device-frame-laptop::after {
  content: '';
  display: block;
  height: 10px;
  margin: 0 -12px;
  background: #2a2a2a;
  border-radius: 0 0 10px 10px;
}

.device-frame-tablet {
  width: 200px;
  padding: 10px;
  border-radius: 18px;
}

.device-frame-phone {
  width: 132px;
  padding: 7px;
  border-radius: 20px;
}

/* The screen inside a bezel: without its own radius the screenshot's corners sit
   square against the rounded dark frame. Inner radius = outer radius - padding, the
   same rule .phone-screen (24 inside 32) already follows. */
.device-frame-tablet picture,
.device-frame-phone picture {
  overflow: hidden;
}
.device-frame-tablet picture { border-radius: 8px; }
.device-frame-phone picture { border-radius: 13px; }

.device-label {
  margin-top: 14px;
  font-size: 0.875rem;
  font-weight: 700;
  color: var(--text-secondary);
}

@media (min-width: 700px) {
  .device-frame-tablet { width: 240px; }
  .device-frame-phone { width: 160px; }
}

/* --- Family --- */
.family {
  padding: 96px 0;
}

.family-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}

.family-card {
  padding: 32px 28px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

/* The privacy card is the rarest claim on the page and the only one a competitor
   cannot copy by shipping a feature, so it is marked out rather than left as the
   fourth of four. --accent-dark, not --accent: measured 4.04 against white where
   --accent is 2.82, under the 3.0 non-text minimum. */
.family-card-private {
  border-color: var(--accent-dark);
  background: var(--bg-light);
}

.family-card h3 {
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 10px;
  letter-spacing: -0.01em;
}

.family-card-private h3 {
  color: var(--accent-dark);
}

.family-card p {
  font-size: 0.95rem;
  color: var(--text-secondary);
  line-height: 1.7;
}

.family-note {
  margin-top: 28px;
  text-align: center;
  font-size: 0.875rem;
  color: var(--text-muted);
}

@media (min-width: 700px) {
  .family-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* --- Pricing --- */
.pricing {
  padding: 96px 0;
  background: var(--bg-light);
}

.pricing-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
  max-width: 52rem;
  margin-inline: auto;
}

.pricing-card {
  padding: 32px 28px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

/* The only use of --accent on the site. Pro is a different KIND of thing, not a
   louder version of free, so it gets the warm token rather than a heavier green. */
/* 🔴 --accent-dark, NOT --accent. Measured against white: --accent (#C88F3C) is 2.82,
   which is under the 3.0 WCAG non-text minimum, so the border that distinguishes Pro
   from free would not have been reliably visible. --accent-dark (#A87428) is 4.04. */
.pricing-card-pro {
  border-color: var(--accent-dark);
  box-shadow: var(--shadow-sm);
}

/* 🔴 1.2rem, not 1.15rem, and the reason is contrast rather than taste. The Pro
   heading below is --accent-dark on white = 4.04, which clears WCAG AA for LARGE text
   (3.0) but not for normal text (4.5). Large means >=18.66px when bold. 1.15rem is
   18.4px and misses it by a quarter of a pixel; 1.2rem is 19.2px and clears it. */
.pricing-card h3 {
  font-size: 1.2rem;
  font-weight: 800;
  margin-bottom: 10px;
  letter-spacing: -0.01em;
}

.pricing-card-pro h3 {
  color: var(--accent-dark);
}

/* Pro+AI is the third card and it is a SUPERSET of Pro, not a louder Pro, so it
   takes the deepest brand green rather than a second warm token. Measured against
   white: --primary-darker (#3F5F1E) is 7.33, which clears WCAG AA for NORMAL text
   (4.5) as well as the 3.0 non-text minimum the border needs, so this heading does
   not depend on the 1.2rem large-text carve-out the Pro heading above relies on. */
.pricing-card-pro-ai {
  border-color: var(--primary-darker);
  box-shadow: var(--shadow-sm);
}

.pricing-card-pro-ai h3 {
  color: var(--primary-darker);
}

.pricing-card p {
  font-size: 0.95rem;
  color: var(--text-secondary);
  line-height: 1.7;
}

.pricing-note {
  margin-top: 24px;
  text-align: center;
  font-size: 0.875rem;
  color: var(--text-muted);
}

/* --- On your wrist --- */
.watch {
  padding: 96px 0;
  background: var(--soft-green);
}

.watch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 48px;
  justify-content: center;
  align-items: flex-start;
}

.watch-item {
  margin: 0;
  text-align: center;
}

.watch-frame {
  background: #1a1a1a;
  padding: 10px;
  box-shadow: var(--shadow-phone);
  width: 200px;
  margin-inline: auto;
}

/* The two faces are different native aspects (416x496 watchOS, 426x426 Wear OS), so
   equal WIDTHS make the round one look smaller. Match their visual mass instead. */
.watch-frame-round {
  width: 216px;
}

/* watchOS: a rounded rectangle, which is what the hardware is. */
.watch-frame-square {
  border-radius: 42px;
}

.watch-frame-square img,
.watch-frame-square picture {
  border-radius: 32px;
  overflow: hidden;
}

/* 🔴 Wear OS is ROUND HERE and SQUARE in the store, deliberately.
   The Play listing requires a square screenshot asset, so the per-locale
   wear capture directory holds a square asset. Nothing requires that on our own site, and the display genuinely
   is round, so the site masks it. Showing a round watch as a square is a store
   constraint being mistaken for a product fact. */
.watch-frame-round,
.watch-frame-round img,
.watch-frame-round picture {
  border-radius: 50%;
}

.watch-frame-round {
  aspect-ratio: 1;
  overflow: hidden;
}

.watch-frame img,
.watch-frame picture {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.watch-label {
  margin-top: 16px;
  font-size: 0.925rem;
  font-weight: 700;
  color: var(--text-secondary);
}

/* --- Story teaser --- */
.storyteaser {
  padding: 0 0 96px;
}

.storyteaser-box {
  max-width: 44rem;
  margin-inline: auto;
  text-align: center;
  padding: 48px 32px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
}

.storyteaser-box h2 {
  font-size: 1.6rem;
  font-weight: 800;
  margin: 12px 0 16px;
  letter-spacing: -0.02em;
}

.storyteaser-box p {
  color: var(--text-secondary);
  line-height: 1.75;
  margin-bottom: 24px;
}

.storyteaser-link {
  display: inline-block;
  font-weight: 700;
  color: var(--primary-dark);
  text-decoration: none;
  border-bottom: 2px solid var(--primary-light);
  padding-bottom: 2px;
  transition: border-color var(--transition), color var(--transition);
}

.storyteaser-link:hover {
  color: var(--primary-darker);
  border-bottom-color: var(--primary);
}

@media (min-width: 700px) {
  .devices-row {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  /* Two columns cannot hold three cards without orphaning one, so in the middle
     band Pro+AI spans the full row underneath Free and Pro rather than sitting
     half-width beside a gap. Three cards go side by side only once there is room
     for them: 52rem split three ways is ~17rem a card, which puts the 0.95rem
     body at about four words a line. */
  .pricing-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .pricing-card-pro-ai {
    grid-column: 1 / -1;
  }
}

@media (min-width: 980px) {
  .pricing-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    max-width: 66rem;
  }

  .pricing-card-pro-ai {
    grid-column: auto;
  }
}

@media (min-width: 900px) {
  .proof-inner {
    grid-template-columns: auto 1fr;
    gap: 64px;
    justify-items: start;
  }
}

/* --- Reduced motion --- */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .hero-badge::before {
    animation: none;
  }

  /* 🔴 Must out-specify `html.js .fade-in` (0,2,1) above, which hides these. A bare
     `.fade-in` selector here is only (0,1,0) and LOSES, so a reduced-motion visitor
     would be handed the animation this block exists to switch off. */
  html.js .fade-in {
    opacity: 1;
    transform: none;
    transition: none;
  }

  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
}
