/* Ceke — marketing landing page.
   Built 1:1 from Figma "Landing Page" (file 90kAj3C6vWgClV7Fwnfnj3):
     Desktop - Light 2602:9532 · Desktop - Dark 2625:8960
     Mobile  - Light 2625:9511 · Mobile  - Dark 2625:10530
   Tokens below are the Figma variable collection, both modes, verified against
   pixel samples of the rendered frames. Same conventions as caregiver-web:
   vanilla CSS custom properties, local @font-face, no build step.
   Theme: `data-theme` on <html> (set pre-paint by the inline script in index.html). */

/* ── Fonts — the same faces the iOS app bundles, converted to WOFF2 ────
   WOFF2 only, no TTF fallback: the page already needs backdrop-filter, CSS
   masks and aspect-ratio, so a browser without WOFF2 could not render it
   anyway and 1.6 MB of TTF would never be fetched. */
@font-face {
  font-family: "Baloo"; font-style: normal; font-weight: 400 700; font-display: swap;
  src: url("assets/fonts/Baloo-Regular.woff2") format("woff2");
}
@font-face {
  font-family: "Inter 18pt"; font-style: normal; font-weight: 400; font-display: swap;
  src: url("assets/fonts/Inter_18pt-Regular.woff2") format("woff2");
}
/* SemiBold covers 600 AND 700. There is no Inter Bold on the site any more: it
   was a 119 KB file serving a single element (.h6), which now asks for 600 like
   everything else. The range is a guard rather than a fudge — if a stray 700
   ever creeps back in it renders as SemiBold instead of the browser faking a
   bold, which looks far worse and is easy to miss. */
@font-face {
  font-family: "Inter 18pt"; font-style: normal; font-weight: 600 700; font-display: swap;
  src: url("assets/fonts/Inter_18pt-SemiBold.woff2") format("woff2");
}

/* ── Tokens · light mode ─────────────────────────────────────────────── */
:root {
  color-scheme: light;

  --surface-page: #fafafa;
  --surface-primary: #ffffff;
  --surface-tertiary: #f4f4f4;
  --surface-action: #12c763;
  --surface-action-pressed: #0e9f4f;
  --surface-action-dark: #0b773b;
  --surface-action-pressed-light: #e8faf0;
  --surface-action-pressed-light-2: #f3fcf7;
  --surface-information-light: #e7f8ff;
  --surface-information-light-2: #f9fdff;
  --surface-information-pressed: #6bb0ca;
  --surface-warning-light: #fff6eb;
  --surface-warning-light-2: #fffcfa;
  --surface-error-light: #ffeaea;
  --surface-error-light-2: #fffafa;
  --surface-button-dark: #262626;
  --surface-button-dark-hover: #404040;   /* no hover in the design; lifted off button-dark */

  --text-headings: #262626;
  --text-headings-tertiary: #ababab;
  --text-body: #575757;
  --text-action: #12c763;
  --text-information: #6bb0ca;
  /* Accessible variants, used ONLY for the small chip labels. The brand
     text-action / text-information fail AA at 14px on their own tinted chip
     backgrounds (2.1–2.4:1). These are the same hues darkened just far enough
     to clear 4.5:1, so everything else keeps the brand colour exactly. */
  --text-action-strong: #0c8140;
  --text-information-strong: #4b7b8d;
  --text-warning: #dc8c2a;
  --text-error: #ea1f22;
  --text-on-action: #ffffff;
  --text-light: #ffffff;
  --text-button-dark: #ffffff;

  --icon-default: #575757;
  --icon-action: #12c763;
  --icon-information: #6bb0ca;
  --icon-warning: #dc8c2a;
  --icon-error: #ea1f22;
  --icon-on-action: #ffffff;

  --border-primary: #d5d5d5;
  --border-action: #71dda1;
  --border-information: #9ee3fd;
  --border-focus: #6bb0ca;

  --fills-primary: rgba(120, 120, 120, 0.2);

  /* Theme-dependent artwork */
  --art-ceke-left: url("assets/svg/deco-ceke-left-light.svg");
  --art-leaf: url("assets/svg/deco-leaf-light.svg");
  --art-ceke-right: url("assets/svg/deco-ceke-right-light.svg");
  --art-toggle: url("assets/svg/icon-sun.svg");
  --art-hero-phones: url("assets/img/hero-phones.webp");
  --art-hand-phone: url("assets/img/hand-phone.webp");

  /* Spacing / radius (CekeSpacing · CekeRadius) */
  --sp-2xs: 4px; --sp-xs: 8px; --sp-sm: 12px; --sp-md: 16px;
  --sp-lg: 24px; --sp-xl: 32px; --sp-2xl: 48px; --sp-3xl: 64px;
  --r-lg: 8px; --r-tile: 12px; --r-xlg: 16px; --r-xxlg: 32px; --r-round: 64px;

  --font-head: "Baloo", system-ui, sans-serif;
  --font-body: "Inter 18pt", -apple-system, system-ui, "Segoe UI", sans-serif;

  /* "LP - card shadow" effect from the Figma library. The ramp's last two stops
     are alpha 0.01 and 0 — they paint nothing but stretched the shadow's reach
     to 292px, far enough to escape a pinned panel and land on whatever follows.
     Dropped: identical to look at, and the reach falls to ~107px. */
  --shadow-card:
    0 9px 20px rgba(84, 92, 95, 0.10),
    0 37px 37px rgba(84, 92, 95, 0.09),
    0 82px 49px rgba(84, 92, 95, 0.05);
  /* Liquid-glass pill: a translucent tint the backdrop blur shows through, a
     bright hairline edge, and an inner sheen along the top. Explicit rgba
     rather than the surface token, so the alpha is part of the value. */
  --glass-bg: rgba(231, 248, 255, 0.62);
  --glass-border: rgba(255, 255, 255, 0.55);
  --glass-sheen: rgba(255, 255, 255, 0.50);

  /* Hero mockup shadow, from the frame: layers thrown to the RIGHT, no vertical
     offset. Written as drop-shadow filters, NOT box-shadow — Figma casts from
     the layer's alpha (the phones' silhouette), whereas box-shadow casts from
     the element's rectangle, which draws a visible pale box around the mockup.
     The frame's two widest layers are at 0.01 and 0 alpha; they'd cost a filter
     pass each for nothing, so they're dropped. */
  --shadow-mockup:
    drop-shadow(17px 0 37px rgba(44, 47, 48, 0.10))
    drop-shadow(67px 0 67px rgba(44, 47, 48, 0.09))
    drop-shadow(150px 0 90px rgba(44, 47, 48, 0.05));

  /* Section rhythm: 160/1440 gutters (content 1120), 80/1440 for the bars */
  --gutter: clamp(24px, 11.111vw, 160px);
  --bar-gutter: clamp(24px, 5.556vw, 80px);
  --section-y: 120px;
  --col-gap: 80px;   /* between a text column and its card */
}

/* ── Tokens · dark mode ──────────────────────────────────────────────── */
:root[data-theme="dark"] {
  color-scheme: dark;

  --surface-page: #313131;
  --surface-primary: #262626;
  --surface-tertiary: #575757;
  --surface-action: #41d282;
  --surface-action-pressed: #71dda1;
  --surface-action-dark: #0b773b;
  --surface-action-pressed-light: #075028;
  --surface-action-pressed-light-2: #042814;
  --surface-information-light: #365865;
  --surface-information-light-2: #1b2c33;
  --surface-information-pressed: #6bb0ca;
  --surface-warning-light: #764304;
  --surface-warning-light-2: #542e00;
  --surface-error-light: #a60204;
  --surface-error-light-2: #620002;
  --surface-button-dark: #ffffff;
  --surface-button-dark-hover: #e4e4e4;   /* the dark-mode button is white, so hover dims */

  --text-headings: #f4f4f4;
  --text-headings-tertiary: #818181;
  --text-body: #d5d5d5;
  --text-information: #86dcfd;
  /* On dark grounds the information colour already clears AA, so it stands;
     the green needs a nudge lighter to clear it on the premium chip. */
  --text-information-strong: #86dcfd;
  --text-action-strong: #25cb6f;
  --text-warning: #ffcf94;
  --text-error: #ffbcbd;
  --text-button-dark: #262626;

  --icon-default: #d5d5d5;
  --icon-information: #86dcfd;
  --icon-warning: #ffcf94;
  --icon-error: #ffbcbd;

  --border-primary: #6d6d6d;
  --border-action: #12c763;
  --border-focus: #9ee3fd;

  --art-ceke-left: url("assets/svg/deco-ceke-left-dark.svg");
  --art-leaf: url("assets/svg/deco-leaf-dark.svg");
  --art-ceke-right: url("assets/svg/deco-ceke-right-dark.svg");
  --art-toggle: url("assets/svg/toggle-glyph-dark.svg");
  /* Dark mockups. Single layer deliberately — these are transparent around the
     phones, so keeping the light file underneath as a fallback would show it
     bleeding through the gaps. */
  --art-hero-phones: url("assets/img/hero-phones-dark.webp");
  --art-hand-phone: url("assets/img/hand-phone-dark.webp");

  --shadow-card:
    0 9px 20px rgba(0, 0, 0, 0.32),
    0 37px 37px rgba(0, 0, 0, 0.24),
    0 82px 49px rgba(0, 0, 0, 0.12);
  /* Glass over dark grounds: less tint, a dimmer edge, barely any sheen */
  --glass-bg: rgba(54, 88, 101, 0.55);
  --glass-border: rgba(255, 255, 255, 0.12);
  --glass-sheen: rgba(255, 255, 255, 0.08);

  /* Same geometry, but the frame's near-black shadow colour disappears against
     a dark ground — deepened so the mockup still lifts off the page. */
  --shadow-mockup:
    drop-shadow(17px 0 37px rgba(0, 0, 0, 0.38))
    drop-shadow(67px 0 67px rgba(0, 0, 0, 0.30))
    drop-shadow(150px 0 90px rgba(0, 0, 0, 0.18));
}

/* ── Base ────────────────────────────────────────────────────────────── */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html {
  scroll-behavior: smooth;
  /* Keeps anything the browser scrolls to — a tabbed-to control, find-in-page,
     a hash on load — clear of the sticky header instead of hidden underneath. */
  scroll-padding-top: 104px;
  /* Paint the canvas ourselves, so the rubber-band overscroll shows the page's
     own colour instead of whatever sits behind the document. The hero's blue,
     not the footer's grey: one colour has to serve both ends, and the top of a
     page is pulled far more often than the bottom.
     Deliberately NOT overscroll-behavior-y: none. That did hide the bounce, but
     it also disables pull-to-refresh on Android Chrome — too high a price for a
     cosmetic edge, especially as the black seen in the preview was the host
     frame's own backdrop rather than this page. */
  background: var(--surface-information-light);
}
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
html, body { margin: 0; padding: 0; }
body {
  background: var(--surface-information-light);
  color: var(--text-body);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 20px;
  -webkit-font-smoothing: antialiased;
  /* clip, NOT hidden: `hidden` turns body into a scroll container, which stops
     position:sticky working for the stacked panels. `clip` contains the hero
     artwork's overhang without that side effect. */
  overflow-x: clip;
}
img { -webkit-user-drag: none; user-select: none; max-width: 100%; }
button { font-family: inherit; cursor: pointer; border: 0; background: none; }
a { color: inherit; text-decoration: none; }
h1, h2, h3, p, ul { margin: 0; }
ul { padding: 0; list-style: none; }

/* Keyboard focus ring — two-tone on purpose.
   This used to be a single 2px stroke in --border-focus, and it failed WCAG
   1.4.11 (3:1 for non-text indicators) on 10 of the 13 tab stops: #6bb0ca lands
   at 2.22 on the hero blue, 2.32 on the page grey and 2.42 on white. Dark mode
   inverted the same problem — the light ring reads well on the dark surfaces but
   drops to 1.41 on the CTA, which is a white button there.
   No single colour clears 3:1 against both a near-white surface and a near-black
   button, so this draws two adjacent strokes instead, the way Chrome and Firefox
   now do natively. The tokens invert together between themes, so whichever
   surface a control sits on, one of the two strokes contrasts with it and the
   other contrasts with its neighbour.
   box-shadow hugs the element (0–2px), outline sits just outside it (2–4px);
   both follow the element's own shape. */
:where(a, button):focus-visible {
  outline: 2px solid var(--text-headings);
  outline-offset: 2px;
  box-shadow: 0 0 0 2px var(--surface-primary);
  border-radius: var(--r-lg);
}

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 20;
  padding: 12px 20px; background: var(--surface-primary); color: var(--text-headings);
  border-radius: 0 0 var(--r-xlg) 0;
}
.skip-link:focus { left: 0; }

/* ── Type scale (Figma text styles) ──────────────────────────────────── */
.h1 { font-family: var(--font-head); font-weight: 700; font-size: 60px; line-height: 72px; color: var(--text-headings); }
.h2 { font-family: var(--font-head); font-weight: 700; font-size: 48px; line-height: 56px; color: var(--text-headings); }
.h5 { font-family: var(--font-head); font-weight: 600; font-size: 24px; line-height: 28px; color: var(--text-headings); }
.h6 { font-family: var(--font-body); font-weight: 600; font-size: 20px; line-height: 24px; color: var(--text-headings); }
.lead { font-size: 20px; line-height: 24px; color: var(--text-body); }
.body-sm { font-size: 14px; line-height: 16px; }
.semibold { font-weight: 600; }

.accent-action { color: var(--text-action); }
.accent-info { color: var(--text-information); }

/* ── Icons · exact SF Symbol / Material glyphs exported from Figma,
      tinted with currentColor through a CSS mask. ────────────────────── */
.i {
  display: inline-block; flex: none;
  background-color: currentColor;
  -webkit-mask: var(--glyph) center / contain no-repeat;
  mask: var(--glyph) center / contain no-repeat;
}
.i--drop    { --glyph: url("assets/icons/drop.svg"); }
.i--sun     { --glyph: url("assets/icons/sun.svg"); }
.i--leaf    { --glyph: url("assets/icons/leaf.svg"); }
.i--bell    { --glyph: url("assets/icons/bell.svg"); }
.i--sparkle { --glyph: url("assets/icons/sparkle.svg"); }
.i--chat    { --glyph: url("assets/icons/chat.svg"); }
.i--magic   { --glyph: url("assets/icons/magic.svg"); }
.i--people  { --glyph: url("assets/icons/people.svg"); }
.i--camera  { --glyph: url("assets/icons/camera.svg"); }
.i--apple   { --glyph: url("assets/icons/apple.svg"); }
.i--crown   { --glyph: url("assets/icons/crown.svg"); }
.i--warn    { --glyph: url("assets/icons/warn.svg"); }
.i--check   { --glyph: url("assets/icons/check.svg"); }
.i--overdue { --glyph: url("assets/icons/overdue.svg"); }
.i--back    { --glyph: url("assets/icons/back.svg"); }   /* 404 "Back to home" */
.i--done    { --glyph: url("assets/icons/done.svg"); }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-xs);
  font-weight: 600; white-space: nowrap; border-radius: var(--r-xxlg);
  transition: transform .16s ease, opacity .16s ease;
}

/* ── Interactive states ──────────────────────────────────────────────────
   The Figma button component only declares a Default state, so these are
   derived from the existing palette rather than invented.

   Deliberately scoped to a.btn / button.btn: most .btn elements on this page
   are <span>s inside the product cards — the "Due today" chips, the round
   task buttons, "Share care plan". Those are pictures of the app, not
   controls, and must not light up under the cursor. */
a.btn, button.btn,
.theme-toggle, .menu-toggle,
.nav a, .mobile-nav a {
  transition: background-color .18s ease, border-color .18s ease,
              color .18s ease, transform .16s ease;
}
a.btn:active, button.btn:active { transform: scale(.98); }
.theme-toggle:active, .menu-toggle:active { transform: scale(.94); }

/* Scoped to pointer devices, so a tap on a phone doesn't leave the control
   stuck in its hover colour. Scoping the rules IN is what's wanted here —
   writing them unscoped and then undoing them in `@media (hover: none)`
   produces two rules of identical specificity, and the reset wins the cascade
   on every device. */
@media (hover: hover) {
  a.btn--dark:hover, button.btn--dark:hover { background: var(--surface-button-dark-hover); }

  a.btn--ghost:hover, button.btn--ghost:hover {
    background: var(--surface-tertiary);
    color: var(--text-headings);
  }

  /* Straight from the toggle component's Hover variant (2624:2727): the tinted
     action surface, an action-coloured edge, and the glyph itself turning
     green. Same in both themes — the tokens do the switching. The hamburger is
     the same circular control, so it follows. */
  .theme-toggle:hover, .menu-toggle:hover {
    background: var(--surface-action-pressed-light);
    border-color: var(--border-action);
    color: var(--icon-action);
  }
  .theme-toggle:hover .theme-toggle__glyph { background-color: var(--icon-action); }

  /* Nav link Hover variant (2625:2739) — the label turns action green */
  .nav a:hover, .mobile-nav a.nav-link:hover { color: var(--text-action); }
}

@media (prefers-reduced-motion: reduce) {
  a.btn, button.btn, .theme-toggle, .menu-toggle {
    transition: background-color .18s ease, border-color .18s ease, color .18s ease;
  }
  a.btn:active, button.btn:active,
  .theme-toggle:active, .menu-toggle:active { transform: none; }
}
/* 14 + 22 + 14 = the 50px button height in the frames (SF Pro 17/22 line box) */
.btn--lg { padding: 14px 20px; font-size: 16px; line-height: 22px; }
.btn--dark { background: var(--surface-button-dark); color: var(--text-button-dark); }
.btn--dark .i { width: 14px; height: 14px; }
/* Apple mark is taller than wide (814:1000) — keep its ratio, don't square it */
.btn--dark .i--apple { width: 13px; height: 16px; }
.btn--ghost { background: var(--surface-primary); color: var(--text-body); }

/* Round 34px task button */
.btn-round {
  width: 34px; height: 34px; border-radius: var(--r-round);
  display: inline-grid; place-items: center; flex: none;
}
.btn-round--outline { background: var(--surface-page); color: var(--icon-default); }
.btn-round--action { background: var(--surface-action); color: var(--icon-on-action); }
.btn-round .i { width: 15px; height: 15px; }

/* ── Chips (section eyebrows) & soft badges ──────────────────────────── */
.chip {
  display: inline-flex; align-items: center; gap: var(--sp-2xs);
  padding: 7px 14px; border-radius: 40px; border: 1px solid;
  font-size: 14px; line-height: 16px; font-weight: 600; white-space: nowrap;
  align-self: flex-start;
}
.chip .i { width: 15px; height: 15px; }
.chip--info {
  background: var(--surface-information-light-2);
  border-color: var(--border-information); color: var(--text-information-strong);
}
.chip--action {
  background: var(--surface-action-pressed-light-2);
  border-color: var(--border-action); color: var(--text-action-strong);
}
.chip--premium {
  background: var(--surface-action-pressed-light);
  border-color: var(--border-action); color: var(--text-action-strong);
}
.chip--premium .i { width: 12px; height: 12px; }

.badge {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 8px; border-radius: var(--r-xlg);
  font-size: 12px; line-height: 16px; font-weight: 600; white-space: nowrap;
}
.badge .i { width: 15px; height: 15px; }
.badge--warning { background: var(--surface-warning-light-2); color: var(--text-warning); }
.badge--error { background: var(--surface-error-light-2); color: var(--text-error); }

/* ── Icon tiles ──────────────────────────────────────────────────────── */
.tile {
  width: 48px; height: 48px; border-radius: var(--r-tile);
  display: grid; place-items: center; flex: none;
}
.tile--sq { border-radius: var(--r-lg); }              /* 8px corner (bell card) */
.tile--info { background: var(--surface-information-light); color: var(--icon-information); }
.tile--warning { background: var(--surface-warning-light-2); color: var(--icon-warning); }
.tile--warning-2 { background: var(--surface-warning-light); color: var(--icon-warning); }
.tile--error { background: var(--surface-error-light-2); color: var(--icon-error); }
.tile--error-2 { background: var(--surface-error-light); color: var(--text-error); }
.tile--action { background: var(--surface-action-pressed-light); color: var(--icon-action); }

/* Toolbar tile (36px) */
.tile-sm {
  width: 36px; height: 36px; border-radius: 6px; flex: none;
  display: grid; place-items: center;
  background: var(--surface-information-light-2); color: var(--text-information);
}

/* Per-glyph sizes — the drawn size each symbol has in the Figma frames */
.tile .i--drop { width: 19.22px; height: 19.22px; }
.tile .i--sun { width: 20.86px; height: 20.86px; }
.tile .i--leaf { width: 20.09px; height: 20.09px; }
.tile .i--bell { width: 22.21px; height: 22.21px; }
.tile .i--sparkle { width: 16.13px; height: 16.13px; }
.tile .i--chat { width: 17.47px; height: 17.47px; }
.tile .i--magic { width: 17.51px; height: 17.51px; }
.tile-sm .i--people { width: 22.4px; height: 22.4px; }
.tile-sm .i--camera { width: 19.67px; height: 19.67px; }

/* ── Scroll reveal ───────────────────────────────────────────────────────
   Blocks lift in the first time they come into view, then stay put.

   The hidden state hangs off `.has-reveal`, which the inline head script only
   sets when IntersectionObserver exists. That way the page is never left with
   invisible content if the script doesn't run — without JS, none of this
   applies and everything renders normally. app.js also reveals everything as a
   safety net if the observer somehow never fires.

   KEEP THIS SELECTOR IN SYNC with REVEAL_SELECTOR in app.js. The hero is
   excluded on purpose: it has its own entrance. */
.two-col__text, .two-col__aside, .info-card, .premium__text, .cta {
  transition: opacity .7s ease, transform .7s cubic-bezier(.22, .9, .3, 1);
}
.has-reveal :is(.two-col__text, .two-col__aside, .info-card, .premium__text, .cta) {
  opacity: 0;
  transform: translateY(32px);
}
.has-reveal :is(.two-col__text, .two-col__aside, .info-card, .premium__text, .cta).is-revealed {
  opacity: 1;
  transform: none;
}
/* A card follows its column, and the three benefit cards come in across */
.two-col__aside { transition-delay: .14s; }
.info-cards .info-card:nth-child(2) { transition-delay: .12s; }
.info-cards .info-card:nth-child(3) { transition-delay: .24s; }

@media (prefers-reduced-motion: reduce) {
  .has-reveal :is(.two-col__text, .two-col__aside, .info-card, .premium__text, .cta) {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ── Layout ──────────────────────────────────────────────────────────── */
.wrap { max-width: 1440px; margin: 0 auto; padding-inline: var(--gutter); }
.section { padding-block: var(--section-y); }
.section--info { background: var(--surface-information-light); }
.section--page { background: var(--surface-page); }
.section--premium { background: var(--surface-action-dark); position: relative; overflow: hidden; }

/* ── Stacked panels ──────────────────────────────────────────────────────
   Daily care, AI identify, Caregiver and Premium pin and pile up: Daily care
   is the base, and each following panel slides up over the one before. Every
   panel here is TALLER than a typical viewport, so pinning at top:0 would
   strand the bottom of the panel permanently offscreen — they pin
   bottom-aligned instead, via --stick-top (a negative offset measured in
   app.js). Without JS every panel falls back to top:0 and the page still
   reads correctly, just without the stack. */
/* Nothing may paint outside the stack. The panels' cards carry a shadow that
   falls ~100px, and because every panel bottom-aligns to the stack's bottom edge
   when pinned, those shadows cross out of the stack entirely and land on the
   footer as one grey band.
   clip, NOT hidden: hidden would turn this into a scroll container and kill the
   panels' sticky behaviour (same trap as body's overflow-x). overflow-x stays
   visible — the Premium mockup is deliberately wider than the viewport. */
.stack { overflow-x: visible; overflow-y: clip; }
.stack > .section {
  position: sticky;
  top: var(--stick-top, 0px);
  /* Rounded leading edge so each panel reads as a card sliding over the one
     behind. Daily care gets it too — it slides over the hero the same way. */
  border-radius: var(--r-xxlg) var(--r-xxlg) 0 0;
}
/* The footer has to out-rank the stack. The panels are positioned with z-indexes
   1–5, so their content paints above any static element that follows them in the
   flow — the card shadows sitting at the bottom of each pinned panel were
   spilling their soft edge across the top of the footer as one grey band. */
.site-footer { position: relative; z-index: 6; }
.stack > .section:nth-of-type(1) { z-index: 1; }
.stack > .section:nth-of-type(2) { z-index: 2; }
.stack > .section:nth-of-type(3) { z-index: 3; }
.stack > .section:nth-of-type(4) { z-index: 4; }
.stack > .section:nth-of-type(5) { z-index: 5; }

/* The closing CTA is the last panel. Premium stays pinned against it while it
   slides over, which is what a blank spacer used to do — except the spacer
   read as a band of empty green once Premium scrolled away. */

.two-col { display: flex; align-items: center; gap: var(--col-gap); }
.two-col__text { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-lg); justify-content: center; }
.two-col__aside { flex: 0 0 402px; max-width: 402px; }
.stack-8 { display: flex; flex-direction: column; gap: var(--sp-xs); }
.stack-12 { display: flex; flex-direction: column; gap: var(--sp-sm); }
.stack-24 { display: flex; flex-direction: column; gap: var(--sp-lg); }
.stack-32 { display: flex; flex-direction: column; gap: var(--sp-xl); }

/* ── Header / footer bars ────────────────────────────────────────────── */
.bar {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--sp-lg) var(--bar-gutter);
  max-width: 1440px; margin: 0 auto;
}
/* Sticky, above the stacked panels (they run z-index 1–4) so they slide under
   it. Opaque, and since the hero shares this exact background it looks like
   part of the page until you scroll off the hero. */
.site-header {
  background: var(--surface-information-light);
  position: sticky;
  top: 0;
  z-index: 10;
  transition: box-shadow .25s ease;
}
/* Once past the hero, the bar detaches into the floating pill from the design
   (frame 2658:7045 — 1280 of 1440 wide, stadium radius, 24/48 padding). The
   header itself goes transparent so only the pill shows and content scrolls
   past either side of it. `top` is animated rather than any layout property,
   so nothing on the page shifts when it changes. */
.site-header .bar {
  padding-block: var(--sp-md);          /* 16 top and bottom, tighter than the footer's 24 */
  /* Line the flat header's content up with the pill's content: the pill sits
     one bar-gutter in and pads itself by 48, so the flat bar needs both. Nav
     then starts at 128 on the 1440 frame and nothing shifts when the pill
     appears. */
  padding-inline: calc(var(--bar-gutter) + var(--sp-2xl));
  transition: max-width .3s ease, padding .3s ease,
              border-radius .3s ease, background-color .3s ease;
}
.site-header[data-past-hero="true"] {
  top: 12px;
  background: transparent;
}
.site-header[data-past-hero="true"] .bar {
  /* Same width as the bar at rest: its edges land exactly where the flat
     header's content sits, so the pill doesn't jut out past the menu. Works
     out to the frame's 1280 of 1440 at full width. */
  width: calc(100% - var(--bar-gutter) * 2);
  max-width: 1280px;
  padding-inline: var(--sp-2xl);        /* 48 */
  border-radius: var(--r-round);        /* stadium */
  /* Liquid glass rather than a flat fill — the page shows through, blurred,
     which also keeps the pill legible over panels of any colour. */
  background: var(--glass-bg);
  -webkit-backdrop-filter: blur(18px) saturate(180%);
  backdrop-filter: blur(18px) saturate(180%);
  border: 1px solid var(--glass-border);
  box-shadow: inset 0 1px 0 var(--glass-sheen);
}

/* Firefox with backdrop-filter disabled, and anything older: fall back to the
   opaque surface so the pill never renders as unreadable transparency. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header[data-past-hero="true"] .bar { background: var(--surface-information-light); }
}

/* No point offering the App Store button while the hero is still showing its
   own pair. It fades in once those are gone. visibility (not just opacity)
   keeps it out of the tab order while hidden. Scoped to the header bar so the
   mobile menu's copy of the button is unaffected. */
.site-header__actions .btn--dark {
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity .25s ease, transform .25s ease, visibility .25s;
}
.site-header[data-past-hero="true"] .site-header__actions .btn--dark {
  opacity: 1;
  visibility: visible;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .site-header__actions .btn--dark { transition: opacity .25s ease, visibility .25s; transform: none; }
}
.nav { display: flex; align-items: center; gap: var(--sp-3xl); }
.nav a { font-weight: 600; font-size: 16px; line-height: 20px; color: var(--text-body); }
.site-header__actions { display: flex; align-items: center; gap: var(--sp-md); }

.theme-toggle {
  width: 48px; height: 48px; border-radius: var(--r-round);
  border: 1px solid var(--border-primary); background: var(--surface-primary);
  display: grid; place-items: center; flex: none;
}
.theme-toggle__glyph {
  width: 24px; height: 24px; display: block;
  background-color: var(--icon-default);
  transition: background-color .18s ease;
  -webkit-mask: var(--art-toggle) center / contain no-repeat;
  mask: var(--art-toggle) center / contain no-repeat;
}

/* The footer used to carry a 1px border-primary rule along its top edge. Dropped
   at Marija's request — against #fafafa on both sides it read as a stray hairline
   rather than a divider — and since removed from all four footer frames too, so
   this is no longer a deviation from the design. */
.site-footer { background: var(--surface-page); }
.site-footer .nav a { font-weight: 600; }
.site-footer__copy { font-size: 16px; line-height: 20px; color: var(--text-body); }

/* ── 404 (frames 2703:11284 light / 2703:11483 dark) ─────────────────────
   Shares the header, buttons and every token with the home page; only the
   centred stack and the bottom artwork are its own. The whole page sits on the
   hero's information surface rather than the page grey. */
/* The frame is one screenful: header, then the error block filling what's left,
   with the artwork sitting on the bottom edge. Making the body a flex column and
   letting .err take the remainder is what keeps that true at any window height —
   `min-height: 100vh` on .err alone would add a viewport on top of the header
   and push the artwork below the fold. Scoped to this page so the home page's
   sticky panel stack is untouched. */
.page-404 { display: flex; flex-direction: column; min-height: 100dvh; }
.page-404 .err { flex: 1 1 auto; }
.err {
  background: var(--surface-information-light);
  display: flex; flex-direction: column; align-items: center;
  /* The frame centres the text block in the 1024 canvas and hangs the artwork
     off the bottom edge, so the copy takes the free space and the art keeps its
     own height underneath. */
  justify-content: center;
  position: relative;
  /* clip sideways only — the hill runs wider than the viewport, but a short
     window should be able to scroll rather than have the buttons cut off. */
  overflow-x: clip; overflow-y: visible;
  /* The bottom padding RESERVES the artwork band, so the centred copy can never
     be centred into it. The artwork is absolutely positioned and spans the full
     width up to 1440, so its height is that width over its 4.221 aspect — the
     same expression, kept in sync by hand.
     Top padding is 109 rather than 48 to bias the copy down onto the frame's
     y=263; capped in vh so a short window gives the space back instead of
     shoving the buttons into the characters. Once even that is not enough the
     block simply grows and the page scrolls, which is why overflow-y is visible. */
  padding: min(109px, 12vh) var(--gutter) calc(min(1440px, 100vw) / 4.221);
}
.err__inner {
  display: flex; flex-direction: column; align-items: center;
  gap: var(--sp-lg);
  width: 466px; max-width: 100%;   /* 466 is the frame's Hero Section width */
  text-align: center;
}

/* "4 <mascot> 4" — 160/120 Baloo in the information colour, 18px gaps */
.err__code {
  display: flex; align-items: center; gap: 18px;
  font-family: var(--font-head); font-weight: 700;
  font-size: 160px; line-height: 120px;
  color: var(--text-information);
}
.err__mascot {
  display: block; width: 120px; height: 120px; flex: none;
  background: url("assets/svg/404-mascot.svg") center / contain no-repeat;
}
.err__text { display: flex; flex-direction: column; gap: var(--sp-xs); width: 100%; }
.err__title {
  font-family: var(--font-head); font-weight: 700;
  font-size: 60px; line-height: 72px; color: var(--text-headings);
}
.err__lead { font-size: 20px; line-height: 24px; color: var(--text-body); }
.err__buttons { display: flex; align-items: center; gap: var(--sp-lg); }
.err__buttons .i--back { width: 9px; height: 16px; }

/* Bottom band: the hill, then the cast standing on it. Both hang off the
   bottom edge exactly as the frame has them, and both are placed as a % of the
   1440 canvas so the group scales with the window instead of drifting apart. */
.err__art {
  position: absolute; bottom: 0;
  left: 50%; transform: translateX(-50%);
  /* Capped at the frame's canvas and centred, exactly like .hero__stage. The
     children are placed as a % of 1440, so without the cap the whole group
     keeps growing past the design size on a wide monitor — at 1890 it renders
     a third too big and climbs over the buttons. */
  width: 100%; max-width: 1440px;
  aspect-ratio: 1440 / 341.166;
  pointer-events: none;
}
.err__hill, .err__cast { position: absolute; bottom: 0; display: block; }
.err__hill {
  left: 36.2840%;                /* 522.49 / 1440 */
  width: 44.3070%;               /* 638.02 / 1440 */
  aspect-ratio: 638.021 / 218.187;
  background: url("assets/svg/404-hill.svg") center bottom / 100% 100% no-repeat;
}
.err__cast {
  left: 16.3534%;                /* 235.49 / 1440 */
  width: 38.4722%;               /* 554 / 1440 */
  aspect-ratio: 554 / 342;
  background: url("assets/svg/404-characters.svg") center bottom / contain no-repeat;
}

/* ── Hero ────────────────────────────────────────────────────────────── */
/* Intro and stage share one grid cell so the artwork tucks up behind the
   headline exactly as in the frame (text 140→414, stage 314→1102). */
.hero { background: var(--surface-information-light); display: grid; }
.hero > * { grid-area: 1 / 1; }
.hero__intro {
  position: relative; z-index: 2; align-self: start;
  max-width: 703px; margin: 0 auto; padding: 42px var(--sp-lg) 0;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-lg);
  text-align: center;
}
.hero__buttons { display: flex; align-items: center; gap: var(--sp-lg); }

/* Proportional 1440x788 stage: every decoration is placed in % of it, so the
   whole composition scales with the viewport instead of breaking apart. */
.hero__stage {
  position: relative; width: 100%; max-width: 1440px;
  margin: 216px auto 0;             /* 314 (stage top) − 98 (header) */
  aspect-ratio: 1440 / 788;
}
.deco { position: absolute; display: flex; align-items: center; justify-content: center; }
.deco > i {
  display: block; width: 100%; height: 100%;
  background: var(--art) center / contain no-repeat;
}
.deco--ceke-left  { left: -2.7778%; top: 39.0863%; width: 28.2319%; height: 53.2204%; }
.deco--ceke-left > i  { --art: var(--art-ceke-left); width: 65.404%; height: 77.519%; transform: rotate(-36.18deg); }
.deco--leaf       { left: 16.5278%; top: -0.0584%; width: 40.0038%; height: 72.4503%; }
.deco--leaf > i       { --art: var(--art-leaf); width: 91.844%; height: 91.671%; transform: rotate(-5.41deg); }
.deco--ceke-right { left: 60.2778%; top: 20.6853%; width: 28.1099%; height: 55.1105%; }
.deco--ceke-right > i { --art: var(--art-ceke-right); width: 70.161%; height: 79.674%; transform: rotate(25.35deg); }
.deco--mascot     { left: 79.4319%; top: 59.7487%; width: 13.5481%; height: 24.7578%; z-index: 2; }
/* The mascot is inline SVG rather than a background image so its eyes can
   blink. Light and dark differ only in a wrapper id, so one copy serves both. */
.mascot { display: block; width: 100%; height: 100%; }

/* Mockup placement comes straight off the frame (879.81x659.86 at x=280.59,
   y=128 in the 1440x788 stage). It is explicitly positioned, not centred, so
   nothing here needs a translateX.
   The fill is CROPPED in Figma, not fitted: the frame clips, and the image
   inside sits at 116.39% scale offset -8.19%/-10%. Rendering the whole image
   instead makes the phones too small and mispositioned. Mobile uses the very
   same crop, so only the frame's own position changes per breakpoint. */
.hero__phones {
  position: absolute;
  left: 19.4853%;              /* 280.589 / 1440 */
  top: 16.2437%;               /* 128 / 788 */
  width: 61.0982%;             /* 879.813 / 1440 */
  aspect-ratio: 4 / 3;
  z-index: 1;
  overflow: hidden;            /* clips the fill */
  /* No CSS shadow here. The frame defines one, but the mockup's bottom edge
     lands exactly on the boundary where the opaque Daily care panel begins, so
     the shadow's lower half was painted over — slicing a soft gradient off
     flat and leaving a hard horizontal line across the page. The artwork
     carries its own ground shadow anyway. */
}
.hero__phones::before {
  content: "";
  position: absolute;
  left: -8.19%;
  top: -10%;
  width: 116.39%;
  height: 116.39%;             /* same %, and the frame is 4:3, so no distortion */
  background-image: var(--art-hero-phones);
  background-position: center, center;
  background-size: 100% 100%, 100% 100%;
  background-repeat: no-repeat, no-repeat;
}

/* Hero reveal — the lettering is written on, then the character joins it.
   The artwork is three script letterforms (each one continuous stroke) plus
   the mascot; the phones stay put, they're the subject.

   The wipe is applied to the ROTATED child, so it sweeps along each letter's
   own axis rather than straight down the screen — that slant is what makes it
   read as a pen stroke. No fade: ink appears, it doesn't dissolve in. The
   letters barely overlap so you can follow the hand moving across. */
@media (prefers-reduced-motion: no-preference) {
  .deco--ceke-left > i,
  .deco--leaf > i,
  .deco--ceke-right > i {
    animation: letter-write .58s cubic-bezier(.65, 0, .35, 1) backwards;
  }
  .deco--ceke-left  > i { animation-delay: .20s; }
  .deco--leaf       > i { animation-delay: .62s; }
  .deco--ceke-right > i { animation-delay: 1.04s; }

  @keyframes letter-write {
    from { clip-path: inset(0 100% 0 0); }
    to   { clip-path: inset(0 0 0 0); }
  }

  /* The character is added last, the way you'd doodle it in after the word */
  .deco--mascot { animation: mascot-pop .5s cubic-bezier(.34, 1.56, .64, 1) 1.5s backwards; }
  @keyframes mascot-pop {
    from { opacity: 0; transform: scale(.5) rotate(-14deg); }
    to   { opacity: 1; transform: scale(1) rotate(0deg); }
  }

  /* The phones drift once the writing has finished. Transform only, so this
     stays on the compositor. */
  .hero__phones { animation: phones-bob 4.5s ease-in-out 2.2s infinite; }
  @keyframes phones-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-10px); }
  }

  /* ...and once it has landed, it rolls its eyes. The pupils orbit their
     sockets at exactly the radius they already sit at (6.22 of a possible
     10.77), so they leave and return to rest with no jump, and never reach
     the edge of the white. Linear, so the roll turns at a constant speed
     rather than easing in and out of every keyframe. It idles for 70% of the
     cycle — the pause is what sells it. */
  .mascot__pupil { animation: mascot-eye-roll 6s linear 2.1s infinite; }
  @keyframes mascot-eye-roll {
    0%, 70%   { transform: translate(0, 0); }
    73.125%   { transform: translate(3.25px, -3.48px); }
    76.25%    { transform: translate(8.01px, -3.64px); }
    79.375%   { transform: translate(11.49px, -0.38px); }
    82.5%     { transform: translate(11.65px, 4.37px); }
    85.625%   { transform: translate(8.39px, 7.85px); }
    88.75%    { transform: translate(3.64px, 8.01px); }
    91.875%   { transform: translate(0.16px, 4.76px); }
    95%, 100% { transform: translate(0, 0); }
  }
}

/* ── Cards ───────────────────────────────────────────────────────────── */
.card {
  background: var(--surface-primary);
  border-radius: var(--r-xxlg);
  padding: var(--sp-md);
  box-shadow: var(--shadow-card);
  display: flex; flex-direction: column; gap: var(--sp-xs);
}
/* The identification + sharing cards space their rows by 16, not 8 */
.card--roomy { gap: var(--sp-md); }
.card__head { display: flex; align-items: flex-start; gap: var(--sp-sm); padding-inline: var(--sp-md); }
.card__head .h5 { flex: 1 1 0; min-width: 0; }
.link-info { color: var(--text-information); font-weight: 600; font-size: 14px; line-height: 16px; }
.card__toolbar {
  display: flex; align-items: center; gap: var(--sp-md);
  padding-bottom: var(--sp-sm); border-bottom: 1px solid var(--border-primary);
}
.card__toolbar--plain { border-bottom: 0; padding-bottom: 0; }
.card__toolbar-text { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2xs); }

.alert {
  display: flex; align-items: center; gap: var(--sp-2xs);
  padding: 7px 14px; border-radius: var(--r-xxlg);
  font-size: 12px; line-height: 16px; font-weight: 600;
}
.alert .i { width: 15px; height: 15px; }
.alert--warning { background: var(--surface-warning-light-2); color: var(--text-warning); }

/* Task rows */
.tasks { position: relative; display: flex; flex-direction: column; gap: var(--sp-sm); }
.task {
  display: flex; align-items: center; gap: var(--sp-sm);
  min-height: 80px; padding: var(--sp-xs) var(--sp-md) var(--sp-xs) var(--sp-xs);
  border-radius: var(--r-xlg); border: 1px solid transparent;
}
.task--done {
  background: var(--surface-action-pressed-light-2);
  border-color: var(--border-action);
}
.task__img {
  width: 64px; height: 64px; flex: none; border-radius: var(--r-xlg);
  object-fit: cover; border: 0.5px solid rgba(0, 0, 0, 0.15);
}
.task__body { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2xs); align-items: flex-start; }
.task__species { font-size: 12px; line-height: 16px; color: var(--text-body); }
.task__need {
  font-family: var(--font-head); font-weight: 600; font-size: 24px; line-height: 28px;
  color: var(--text-headings); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}

/* Undo snackbar, overlaid on the last row exactly as in the frame */
.undo {
  position: absolute; left: 0; top: 280px; width: 100%;
  display: flex; flex-direction: column; justify-content: center; gap: var(--sp-sm);
  min-height: 84px; padding: var(--sp-sm) var(--sp-md);
  border-radius: var(--r-xlg);
  background: var(--surface-action-pressed-light-2);
  border: 1px solid var(--border-action);
}
.undo__row { display: flex; align-items: center; gap: 2px; }
.undo__row .i { width: 15px; height: 15px; color: var(--icon-action); }
.undo__label { flex: 1 1 0; min-width: 0; font-size: 12px; line-height: 16px; font-weight: 600; color: var(--text-body); }
.undo__action { font-size: 12px; line-height: 16px; font-weight: 600; color: var(--text-action); }
.undo__track { height: 6px; border-radius: 3px; background: var(--fills-primary); overflow: hidden; }
.undo__fill { height: 100%; width: 13.51%; border-radius: 3px; background: var(--surface-action); }

/* Feature rows */
.feature {
  display: flex; align-items: center; gap: var(--sp-sm);
  padding: var(--sp-xs); border-radius: var(--r-xlg);
  background: var(--surface-primary);
  font-weight: 600; font-size: 16px; line-height: 20px; color: var(--text-body);
}
.feature--premium {
  /* Its own surface rather than --surface-action-pressed: the pale label on
     the brand #0e9f4f only reaches 3.18:1. Darkened just enough for 4.5:1,
     and only here — the token itself is untouched for everything else. */
  background: var(--surface-premium-row, #0b8140);
  color: var(--surface-action-pressed-light);
}
:root[data-theme="dark"] .feature--premium {
  /* dark already clears AA (5.74:1), so it keeps the brand value */
  background: var(--surface-action-pressed);
}

/* Three-across info cards */
.info-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-lg); }
.info-card {
  background: var(--surface-primary); border-radius: var(--r-xxlg);
  padding: var(--sp-lg); display: flex; flex-direction: column; gap: var(--sp-xs);
}
.info-card p { font-size: 16px; line-height: 20px; color: var(--text-body); }

/* The "Going away?" support banner (2614:8700 / 2625:10183) used to sit here.
   Pulled from both breakpoints at Marija's request, so the markup and its rules
   are gone rather than hidden. Its mascot-banner.svg artwork went with it —
   path written without the folder prefix on purpose, so the artifact bundler
   doesn't try to inline a file that no longer exists. */

/* Plant identification card */
.id-photo {
  width: 100%; aspect-ratio: 370 / 240; border-radius: var(--r-xlg);
  object-fit: cover; display: block;
}
.id-stats {
  display: flex; align-items: center; width: 100%;
  padding-block: var(--sp-sm); border-radius: var(--r-xlg);
  background: var(--surface-page);
}
.id-stats__cell {
  flex: 1 1 0; min-width: 0; min-height: 20px;
  display: flex; align-items: center; justify-content: center; gap: var(--sp-2xs);
  padding-inline: var(--sp-sm);
  font-size: 14px; line-height: 16px; color: var(--text-body);
}
.id-stats__cell + .id-stats__cell { border-left: 1px solid var(--border-primary); }
.id-stats__cell .i { width: 13.5px; height: 13.5px; }
.id-stats__cell .i--drop { color: var(--icon-information); }
.id-stats__cell .i--sun { color: var(--icon-warning); }
.id-stats__cell .i--leaf { color: var(--icon-error); }

/* "Need help?" AI banner */
.ai-banner {
  background: var(--surface-information-light-2);
  border: 1px solid var(--border-information);
  border-radius: var(--r-xlg); padding: var(--sp-md);
  display: flex; flex-direction: column; gap: var(--sp-sm);
  box-shadow: var(--shadow-card);
}
.ai-banner__title { color: var(--text-information); }
.ai-ask {
  display: flex; align-items: center; gap: var(--sp-sm);
  padding: var(--sp-2xs) var(--sp-xs) var(--sp-2xs) var(--sp-2xs);
  border-radius: var(--r-lg); background: var(--surface-primary);
}
.ai-ask img {
  width: 32px; height: 32px; flex: none; border-radius: var(--r-lg);
  object-fit: cover; border: 0.5px solid rgba(0, 0, 0, 0.15);
}
.ai-ask span { font-size: 12px; line-height: 16px; color: var(--text-headings-tertiary); }

/* ── Premium band ────────────────────────────────────────────────────── */
.premium__text { width: 494px; max-width: 100%; position: relative; z-index: 1; }
.premium__text .h2, .premium__text .lead { color: var(--text-light); }

/* The artwork is placed on the 1440 design grid, NOT on the full-bleed section —
   sized off the section it keeps growing past 1440 and outgrows the band, which
   clipped the phone on wide windows. top/bottom:0 + contain means it can never
   be clipped, whatever height the band's content ends up being. */
.premium__stage {
  position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 100%; max-width: 1440px; pointer-events: none;
}
.premium__hand {
  position: absolute; top: 0; bottom: 0;
  left: 35.046%;                                  /* 504.67/1440 */
  width: 72.407%;                                 /* 1042.67/1440 */
  background-image: var(--art-hand-phone);
  background-position: center top, center top;
  background-size: contain, contain;
  background-repeat: no-repeat, no-repeat;
}

/* ── Closing call to action ──────────────────────────────────────────── */
.cta {
  display: flex; align-items: center; gap: var(--sp-lg);
  padding: var(--sp-xl); border-radius: var(--r-xxlg);
  background: var(--surface-action-pressed-light);
  border: 1px solid var(--border-action);
}
/* align-items:start keeps the button hugging its label (271px in the frame)
   instead of stretching to the full column width */
.cta__text { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-lg); }
.mascots { display: grid; grid-template-columns: repeat(3, 90px); gap: var(--sp-md); flex: none; }
.mascots i { display: block; width: 90px; height: 90px; background: var(--m) center / contain no-repeat; }

/* ── Responsive · mobile frames 2625:9511 / 2625:10530 ───────────────── */
@media (max-width: 899px) {
  :root { --section-y: 48px; --gutter: 24px; --bar-gutter: 24px; }
  html { scroll-padding-top: 88px; }   /* the bar is shorter here */

  /* Frame 2625:9533 is 40/48 — the same as the section headings below it.
     Held at 40 wherever it fits; "Ceke - Plant care" measures 7.5em in our
     Baloo, so on a screen under ~356px it would split mid-phrase. Dividing the
     available width by 7.7 gives the largest size that still holds the frame's
     two-line break. Ratio line-height, so it stays 48/40 at every size. */
  .h1 { font-size: min(40px, (100vw - var(--gutter) * 2) / 7.7); line-height: 1.2; }
  .h2 { font-size: 40px; line-height: 48px; }   /* frames 2625:9542 / 2625:10392 */
  .lead { font-size: 16px; line-height: 20px; }
  .br-desktop { display: none; }

  /* Header collapses to menu + toggle; the nav links live in the footer */
  .site-header .nav { display: none; }
  /* Scoped to the bar: the full-screen menu has its own copy of this button
     and must keep it. */
  .site-header__actions .btn--dark { display: none; }
  .bar { padding-block: var(--sp-sm); }
  /* Frame 2625:9826 — a 72px bar with both controls 24 in from the edge and 12
     from the top, which is also where the full-screen menu puts its logo and
     close button, so the two line up when the menu opens over the header. */
  .site-header .bar { padding: var(--sp-sm) var(--sp-lg); }
  /* The pill has to land on that same 24 or the controls jump sideways the
     moment it detaches. Its inset and its own padding both halve to 12, which
     adds back up to 24 — the 48px circles still clear the stadium caps. */
  .site-header[data-past-hero="true"] .bar {
    width: calc(100% - var(--sp-sm) * 2);
    padding-inline: var(--sp-sm);
  }
  /* With the nav hidden, the actions group is the bar's only child, so
     space-between leaves it stranded on the left. Spread it across instead:
     menu button far left, theme toggle far right, as the mobile frame has it. */
  .site-header__actions { width: 100%; justify-content: space-between; }

  /* Mobile hero: artwork first, then the copy (Frame 2625:9531).
     The frame starts the hero at y=12 — above the 72px header's lower edge — so
     the wordmark tucks up behind the bar and only the two round buttons float
     over it. Hence the pull-up, and hence the bar going transparent at rest:
     it sits on the hero's own background, so nothing changes visually except
     that the artwork is no longer cut off. */
  /* Frame 2625:9531 runs the hero 0→740 with its buttons ending at 692, and
     Care starts at 740 — so the hero sits fully behind the 72px header, and
     there are 48px below the buttons before Care begins (96 to Care's chip,
     since Care pads itself by 48).
     The trailing space has to be padding on the hero rather than margin on
     Care: Care is a sticky panel, and a top margin would move the point it
     pins at. */
  .hero { display: flex; flex-direction: column; margin-top: -72px; padding-bottom: 48px; }
  .site-header:not([data-past-hero="true"]) { background: transparent; }
  .hero__intro { align-self: auto; }
  .hero__stage { order: -1; margin-top: 0; aspect-ratio: 360 / 386; }
  .hero__intro { padding: 24px var(--sp-lg) 0; gap: var(--sp-md); }
  .hero__buttons { flex-direction: column; gap: var(--sp-sm); width: 100%; }
  .hero__buttons .btn { width: 100%; }

  .deco--ceke-left  { left: 5.0222%; top: 26.7850%; width: 29.4261%; height: 28.3106%; }
  .deco--leaf       { left: 19.7194%; top: 0.3497%; width: 45.5378%; height: 42.1767%; }
  .deco--leaf > i       { width: 84.095%; height: 83.767%; transform: rotate(-12.4deg); }
  .deco--ceke-right { left: 60.3972%; top: 10.8756%; width: 29.2989%; height: 29.3161%; }
  /* The two frames stack these differently: on desktop the mascot is the last
     child of the Background group, so it sits in front of the phones; in the
     mobile frame the phone is last and the mascot is tucked inside the wordmark
     group behind it. Hence the z-index drop here only. It still paints over the
     wordmarks — being last in the DOM settles that among the level-0 decos. */
  .deco--mascot     { left: 81.1278%; top: 32.9016%; width: 14.1211%; height: 13.1699%; z-index: 0; }
  /* mobile frame: 357 x 267.75 at x=1.5, y=111 in the 360x386 stage */
  .hero__phones { left: 0.4167%; top: 28.7578%; width: 99.1667%; }

  /* 404 · frame 2703:11635. The mobile frame drops the hill and the cast
     entirely, and everything else steps down: the code row to 64 tall, the
     title to the h2 size, the lead to paragraph-sm, buttons stacked full width. */
  .err { padding: 0 var(--sp-lg); justify-content: flex-start; }
  .err__inner { width: 100%; margin-bottom: 0; padding-top: 99px; }
  .err__code { gap: 9.6px; font-size: 85.33px; line-height: 64px; }
  .err__mascot { width: 64px; height: 64px; }
  .err__title { font-size: 40px; line-height: 48px; }
  .err__lead { font-size: 14px; line-height: 16px; }
  .err__buttons { flex-direction: column; gap: var(--sp-sm); width: 100%; }
  .err__buttons .btn { width: 100%; }
  .err__art { display: none; }

  .two-col { flex-direction: column; align-items: stretch; gap: var(--sp-lg); }
  .two-col__aside { flex: 1 1 auto; max-width: none; }

  /* Frame 2625:9544 — the three feature rows are dropped from the mobile Care
     section. Scoped to #care: Premium has its own feature list that stays.
     display:none rather than visibility, so the flex gap above it goes too. */
  #care .two-col__text > ul { display: none; }

  /* Frame 2625:10163 reorders this section on mobile: the caregiver copy and the
     app card come first, then the three info cards. Desktop keeps the cards on
     top, so this is an `order` flip rather than a DOM change — .wrap is already
     a flex column, and the cards hold no focusable elements, so tab order is
     unaffected either way. */
  #sharing .wrap > .info-cards { order: 1; }

  /* Feature cards scroll sideways, matching the 942px row in the mobile frame */
  .info-cards {
    grid-template-columns: none; grid-auto-flow: column; grid-auto-columns: 84%;
    overflow-x: auto; scroll-snap-type: x mandatory;
    margin-inline: calc(var(--gutter) * -1); padding-inline: var(--gutter);
    /* Snap to the padding edge, not the border edge. Without this the first
       card snaps flush to x=0 and eats the gutter, so the row lost the 24px
       left inset the frame gives it (2625:10626 sits at x=24). */
    scroll-padding-inline: var(--gutter);
    scrollbar-width: none;
    /* Sideways only. Setting overflow-x alone makes the browser compute
       overflow-y to auto as well, so the row picked up a stray vertical drag of
       its own. The padding/margin pair gives the cards' shadows room to fall
       inside the clip instead of being sliced off. */
    overflow-y: hidden;
    /* Bottom only. --shadow-card casts straight down (every layer has a positive
       Y offset), so there was never anything to make room for above — and the
       8px that used to be there was quietly eating into the 32px gap the frame
       puts between the app card and this row. */
    padding-block: 0 var(--sp-lg);
    margin-block: 0 calc(var(--sp-lg) * -1);
  }
  .info-cards::-webkit-scrollbar { display: none; }
  .info-card { scroll-snap-align: start; }

  /* Premium: artwork above the copy, flush to the band's top edge
     (mobile frame: 680x510 at x=-160, y=0) */
  .section--premium { display: flex; flex-direction: column; padding-top: 0; }
  .premium__stage { position: relative; transform: none; left: auto; max-width: none; margin-bottom: 10px; }
  .premium__hand {
    /* display:block is load-bearing: it's a <span>, and on desktop only
       position:absolute was blockifying it. Switching to relative here dropped
       that, so width/aspect-ratio stopped applying and the mockup collapsed to
       0x0 — taking the section's whole top band with it. */
    display: block;
    position: relative; top: auto; bottom: auto;
    left: -44.4%; width: 188.9%; aspect-ratio: 680 / 510;
  }
  .premium__text { width: 100%; }

  .cta { flex-direction: column; align-items: stretch; gap: var(--sp-lg); padding: var(--sp-lg); }
  /* Frame 2625:10719 centres the heading and the line under it; the button
     still spans the full width (280 of 280 in the frame). */
  .cta__text { align-items: center; text-align: center; }
  .cta__text .btn { width: 100%; }
  .cta .btn { width: 100%; }
  .mascots {
    order: -1; margin: 0 auto;
    grid-template-columns: repeat(3, 70.84px);
  }
  .mascots i { width: 70.84px; height: 70.84px; }

  /* Frame 2625:9835 centres the copyright (299 wide at x=30.5 in a 360 frame)
     while the links stay spread across the full width. */
  .site-footer .bar { flex-direction: column; align-items: stretch; gap: var(--sp-lg); }
  .site-footer .nav { gap: var(--sp-lg); justify-content: space-between; width: 100%; }
  .site-footer__copy { text-align: center; }


  .card__head { padding-inline: 0; }
  .task__need { font-size: 20px; line-height: 24px; }
  .undo { top: 268px; }
}

@media (max-width: 420px) {
  /* .h1 and .h2 deliberately keep the frame's sizes — the mobile frame is 360
     wide, so this range is exactly what it was drawn for. */
  .task__img { width: 56px; height: 56px; }
  .undo { top: 250px; }
}

/* The hamburger and its panel only exist on small screens */
.menu-toggle, .mobile-nav { display: none; }
@media (max-width: 899px) {
  .menu-toggle {
    display: grid; place-items: center;
    width: 48px; height: 48px; border-radius: var(--r-round);
    border: 1px solid var(--border-primary); background: var(--surface-primary);
    color: var(--icon-default);
  }
  .menu-toggle svg { width: 22px; height: 22px; }

  /* Full-screen menu — frame 2676:8025. Fixed over everything, with its own
     top bar carrying the logo and a close button, so it replaces the header
     rather than hanging below it. */
  .mobile-nav {
    position: fixed;
    inset: 0;
    z-index: 20;
    flex-direction: column;
    padding: 0 var(--sp-lg) 48px;
    background: var(--surface-information-light);
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .mobile-nav[data-open="true"] { display: flex; }
  /* It only takes focus as a scroll/Tab anchor — never show a ring on the panel */
  .mobile-nav:focus { outline: none; }

  .mobile-nav__bar {
    display: flex; align-items: center; justify-content: space-between;
    margin-inline: calc(var(--sp-lg) * -1);        /* the bar spans the full width */
    padding: var(--sp-sm) var(--sp-lg);            /* 12 / 24, giving the 72px bar */
    flex: none;
  }
  .mobile-nav__logo {
    width: 112.863px; height: 48px; display: block;
    background: url("assets/svg/logo-ceke.svg") left center / contain no-repeat;
  }
  /* Same circular control as the theme toggle it replaces */
  .mobile-nav__close {
    width: 48px; height: 48px; flex: none;
    display: grid; place-items: center;
    border: 1px solid var(--border-primary);
    border-radius: var(--r-round);
    background: var(--surface-primary);
    color: var(--icon-default);
    transition: background-color .18s ease, border-color .18s ease, color .18s ease;
  }
  /* 24 to match the icon box, so the viewBox maps 1:1 and the stroke lands at
     exactly the 1px the component specifies */
  .mobile-nav__close svg { width: 24px; height: 24px; }

  .mobile-nav__links {
    display: flex; flex-direction: column; gap: var(--sp-xs);
    flex: none; padding-top: var(--sp-lg);
  }
  /* Character cluster (2694:10689), 312x193 in the 360 frame — full content
     width. margin-top:auto is what makes it sit against the CTA rather than the
     links: it takes the panel's slack, which is why the links above are flex:none
     now. bottom-aligned inside its own box so the characters keep standing on
     the button no matter how tall the panel gets. */
  .mobile-nav__art {
    display: block;
    margin-top: auto;
    margin-bottom: var(--sp-xs);
    width: 100%;
    aspect-ratio: 312 / 193;
    background: url("assets/svg/menu-characters.svg") center bottom / contain no-repeat;
  }
  /* Inter Regular here, not the semibold used in the desktop bar */
  .mobile-nav__links a {
    display: flex; align-items: center;
    padding: var(--sp-xs) var(--sp-sm);
    border: 1px solid transparent;
    border-radius: var(--r-xxlg);
    font-size: 16px; line-height: 20px; font-weight: 400;
    color: var(--text-body);
  }
  .mobile-nav__links a[aria-current="true"] {
    background: var(--surface-action-pressed-light-2);
    border-color: var(--border-action);
    color: var(--text-action);
  }
  .mobile-nav .btn { width: 100%; flex: none; }

  /* Nothing behind the overlay should scroll while it's open */
  body:has(.mobile-nav[data-open="true"]) { overflow: hidden; }
}
