/* ============================================================
   Packlings — packlings.app
   "Storybook Warm" — tokens mirror Packlings/Theme (DESIGN.md)
   ============================================================ */

:root {
  /* paper & surfaces */
  --paper: #FAF4E7;
  --paper-deep: #F3EAD7;
  --linen: #F6ECDA;
  --surface: #FFFCF5;
  /* inks */
  --ink: #2F2B22;
  --ink-2: #6B6353;
  --hairline: rgba(47, 43, 34, .10);
  /* brand greens */
  --moss: #4F6B46;
  --moss-deep: #3C5435;
  --moss-mist: #E9EAD8;
  --moss-soft: #ABC79B;
  --pine: #2E4A3B;
  --pine-deep: #16241C;
  --success: #3F7A54;
  /* warm accents */
  --pumpkin: #E8A16B;
  --rust: #A05532;
  --on-accent: #342414;
  --honey: #8F5A14;
  --sparkle: #D99A4E;
  /* device + decoration */
  --bezel: #33301F;
  --leaf-moss: #6E9060;
  --leaf-terra: #C97C4E;
  --leaf-blue: #8AA8C0;
  /* shape */
  --r-sm: 12px;
  --r-md: 18px;
  --r-lg: 24px;
  --r-kid: 28px;
  --r-hero: 36px;
  --shadow-rest: 0 2px 14px rgba(51, 48, 31, .07);
  --shadow-float: 0 10px 30px rgba(51, 48, 31, .12);
  /* type */
  --display: 'Baloo 2', 'Nunito', ui-rounded, system-ui, sans-serif;
  --body: 'Nunito', ui-rounded, system-ui, -apple-system, sans-serif;

  /* motion — the app's three SwiftUI springs (DESIGN.md §7), as CSS.
     Fallback tier: cubic-bezier approximations for browsers without linear().
     snappy = response .3 / damping .70 — selections, chips, hovers
     bouncy = response .4 / damping .55 — pops, hops, celebration entrances
     settle = response .5 / damping .90 — progress fills, reveals, layout shifts */
  --e-snappy: cubic-bezier(.34, 1.30, .64, 1);
  --e-bouncy: cubic-bezier(.34, 1.56, .64, 1);
  --e-settle: cubic-bezier(.22, .61, .36, 1);
  /* full spring = its own duration (visual arrival + settle) plus its curve */
  --d-snappy: 500ms;
  --d-bouncy: 850ms;
  --d-settle: 650ms;
  --spring-snappy: var(--d-snappy) var(--e-snappy);
  --spring-bouncy: var(--d-bouncy) var(--e-bouncy);
  --spring-settle: var(--d-settle) var(--e-settle);
  /* the "asked" (perceptual) durations — time sibling fades off these, never
     off the spring's full length, which includes the settle after arrival */
  --t-snappy: .3s;
  --t-bouncy: .4s;
  --t-settle: .5s;
}

/* Real springs where linear() is supported.
   Generated with Motion's generate-css-easing (AI Kit) from the same three
   spring definitions the app uses — visualDuration is the *perceptual* time,
   and each curve's own duration above is longer because it includes the
   settle after arrival. Regenerate: see website/README.md. */
@supports (transition-timing-function: linear(0, 1)) {
  :root {
    /* visualDuration .3s, bounce .30 */
    --e-snappy: linear(0, 0.1144, 0.3475, 0.5885, 0.7844, 0.9194, 0.9987, 1.0359, 1.046, 1.0413, 1.0308, 1.0196, 1.0104, 1.004, 1.0002, 0.9984, 1);
    /* visualDuration .4s, bounce .45 */
    --e-bouncy: linear(0, 0.0724, 0.2433, 0.4538, 0.6611, 0.8385, 0.9729, 1.062, 1.1103, 1.1261, 1.1192, 1.0988, 1.0725, 1.046, 1.023, 1.0053, 0.9933, 0.9866, 0.9841, 0.9847, 0.9871, 0.9904, 0.9938, 0.9968, 0.9991, 1.0007, 1.0016, 1);
    /* visualDuration .5s, bounce .10 */
    --e-settle: linear(0, 0.0433, 0.1429, 0.2661, 0.3928, 0.5114, 0.6161, 0.7048, 0.7776, 0.8358, 0.8812, 0.9159, 0.9419, 0.961, 0.9747, 0.9844, 0.991, 0.9954, 0.9982, 0.9999, 1.0009, 1);
  }
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img { max-width: 100%; }

a { color: var(--rust); text-decoration-thickness: 1.5px; text-underline-offset: 3px; }
a:hover { color: #7d3f24; }

::selection { background: var(--moss-mist); color: var(--ink); }

h1, h2, h3 {
  font-family: var(--display);
  color: var(--ink);
  line-height: 1.12;
  margin: 0 0 .35em;
  letter-spacing: -.01em;
  font-weight: 800;
}

p { margin: 0 0 1em; }

.container { width: min(1120px, calc(100% - 48px)); margin: 0 auto; }
@media (max-width: 640px) { .container { width: calc(100% - 40px); } }

/* ---------- paper grain (fixed, over everything, barely there) ---------- */
.grain {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: .05;
  mix-blend-mode: multiply;
  pointer-events: none;
  z-index: 2147483000;
}

/* ---------- eyebrow / micro ---------- */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--moss);
  margin-bottom: 14px;
}
.eyebrow .tag-plus { margin-left: 2px; }

.tag-plus {
  display: inline-block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .12em;
  padding: 3px 10px 2px;
  border-radius: 999px;
  background: var(--pumpkin);
  color: var(--on-accent);
  text-transform: uppercase;
}

.tag-free {
  display: inline-block;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: .12em;
  padding: 3px 10px 2px;
  border-radius: 999px;
  background: var(--moss-mist);
  color: var(--moss);
  text-transform: uppercase;
}

/* ---------- buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  font-family: var(--body);
  font-weight: 800;
  font-size: 16.5px;
  line-height: 1;
  padding: 17px 28px;
  border-radius: 999px;
  border: none;
  cursor: pointer;
  text-decoration: none;
  /* snappy = the app's spring for selections and taps */
  transition: transform var(--spring-snappy), box-shadow var(--t-snappy) ease, background var(--t-snappy) ease;
}
.btn:active { transform: scale(.98); }
.btn:focus-visible { outline: 3px solid var(--sparkle); outline-offset: 3px; }

.btn-primary { background: var(--moss); color: #fff; box-shadow: var(--shadow-rest); }
.btn-primary:hover { background: var(--moss-deep); color: #fff; transform: translateY(-1px); box-shadow: var(--shadow-float); }

.btn-ghost { background: transparent; color: var(--moss); box-shadow: inset 0 0 0 2px var(--moss-mist); }
.btn-ghost:hover { background: var(--moss-mist); color: var(--moss-deep); }

/* App Store pill (own artwork, swaps to a live link at launch) */
.store-badge {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  background: var(--bezel);
  color: #F6EBD8;
  border-radius: 16px;
  padding: 12px 22px 12px 18px;
  text-decoration: none;
  box-shadow: var(--shadow-rest);
  transition: transform var(--spring-snappy), box-shadow var(--t-snappy) ease;
}
.store-badge:active { transform: scale(.98); }
.store-badge:hover { transform: translateY(-1px); box-shadow: var(--shadow-float); color: #FFF9EF; }
.store-badge:focus-visible { outline: 3px solid var(--sparkle); outline-offset: 3px; }
.store-badge svg { width: 26px; height: 30px; flex: none; }
.store-badge .sb-text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.15; }
.store-badge .sb-text small { font-size: 11.5px; font-weight: 600; opacity: .85; letter-spacing: .02em; }
.store-badge .sb-text strong { font-family: var(--display); font-size: 21px; font-weight: 700; letter-spacing: .01em; }
.store-badge.is-soon { cursor: default; }

/* ---------- header ---------- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(250, 244, 231, .82);
  -webkit-backdrop-filter: blur(14px);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid transparent;
  transition: border-color .25s ease;
}
.site-header.scrolled { border-bottom-color: var(--hairline); }

.nav {
  display: flex;
  align-items: center;
  gap: 28px;
  padding: 14px 0;
}
.brand {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  text-decoration: none;
  margin-right: auto;
}
.brand img { width: 38px; height: 38px; border-radius: 10px; box-shadow: var(--shadow-rest); }
.brand span {
  font-family: var(--display);
  font-size: 24px;
  font-weight: 800;
  color: var(--ink);
  letter-spacing: -.01em;
}
.nav-links { display: flex; align-items: center; gap: 26px; }
.nav-links a {
  position: relative;
  font-weight: 700;
  font-size: 15.5px;
  color: var(--ink-2);
  text-decoration: none;
}
.nav-links a:hover { color: var(--moss); }
/* a little moss underline grows out from the middle — scaleX, so it never
   touches layout */
.nav-links a::after {
  content: "";
  position: absolute;
  left: 1px; right: 1px; bottom: -6px;
  height: 2.5px;
  border-radius: 2px;
  background: var(--moss-soft);
  transform: scaleX(0);
  transition: transform var(--spring-snappy);
}
.nav-links a:hover::after, .nav-links a:focus-visible::after { transform: scaleX(1); }
.nav .btn { padding: 12px 22px; font-size: 15px; }
@media (max-width: 820px) { .nav-links { display: none; } }

/* ---------- hero ---------- */
.hero { position: relative; padding: clamp(40px, 7vw, 84px) 0 clamp(48px, 7vw, 90px); }
.hero-grid {
  display: grid;
  grid-template-columns: 1.04fr .96fr;
  gap: clamp(32px, 5vw, 64px);
  align-items: center;
}
.hero h1 {
  font-size: clamp(42px, 5.6vw, 66px);
  font-weight: 800;
  letter-spacing: -.015em;
  margin-bottom: .3em;
}
.hero h1 em { font-style: normal; color: var(--moss); }
/* words land one at a time (heroEntrance in motion-init.js); inline-block so
   each can carry its own transform */
.hero h1 .w { display: inline-block; }
.hero .lede { font-size: clamp(17.5px, 1.6vw, 20px); color: var(--ink-2); max-width: 34em; margin-bottom: 28px; }
.hero .cta-row { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; margin-bottom: 18px; }
.hero .fine {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink-2);
}
.hero .fine span { display: inline-flex; align-items: center; gap: 7px; }
.hero .fine .dot { width: 5px; height: 5px; border-radius: 50%; background: var(--sparkle); flex: none; }

.hero-stage { position: relative; min-height: 520px; }
.hero-stage .sun {
  position: absolute;
  top: 50%; left: 50%;
  width: min(560px, 96%);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: radial-gradient(circle at 50% 42%, #F6E7C9 0%, #F3E3C4 55%, rgba(243, 227, 196, 0) 72%);
}
.hero-stage .phone { position: relative; margin: 0 auto; z-index: 3; }
/* the snapshot card behind the phone — white print border, a little tilt,
   the same "photo dropped on the table" language as the onboarding page */
.hero-stage .hero-photo {
  position: absolute;
  z-index: 2;
  top: -2%;
  right: -8%;
  width: 47%;
  height: auto;
  border-radius: 22px;
  border: 8px solid #FFFDF6;
  box-shadow: 0 18px 44px rgba(51, 48, 31, .16), var(--shadow-rest);
  transform: rotate(6deg);
}
.hero-joey {
  position: absolute;
  z-index: 4;
  width: clamp(170px, 42%, 235px);
  bottom: -30px;
  left: -4%;
  filter: drop-shadow(0 14px 22px rgba(51, 48, 31, .18));
}
/* .hero-joey / .joey-wave are wrappers now: the hop (JS) transforms the
   wrapper, the rock loop transforms the svg — never the same element */
.joey-hug svg { display: block; width: 100%; height: auto; }
.hero-sticker {
  position: absolute;
  z-index: 5;
  top: 34px;
  right: 0;
  transform: rotate(5deg);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 14px;
  box-shadow: var(--shadow-rest);
  padding: 10px 16px;
  font-weight: 800;
  font-size: 14.5px;
  color: var(--ink);
}
.hero-sticker small { display: block; font-size: 11.5px; font-weight: 700; color: var(--ink-2); }

@media (max-width: 900px) {
  .hero-grid { grid-template-columns: 1fr; text-align: center; }
  .hero .lede { margin-inline: auto; }
  .hero .cta-row, .hero .fine { justify-content: center; }
  .hero-stage { min-height: 0; margin-top: 12px; }
  .hero-stage .phone { max-width: min(300px, 78vw); }
  .hero-stage .hero-photo { display: none; }
  .hero-joey { width: clamp(130px, 34vw, 175px); left: 0; bottom: -16px; }
  .hero-sticker { right: 2%; }
  .night .spark, .night .star, .get-card .spark { display: none; }
  .night .moon { width: 52px; height: 52px; top: 22px; right: 6%; }
}

/* ---------- phone frame (matches the store-shot bezel) ---------- */
.phone {
  background: var(--bezel);
  border-radius: 54px;
  padding: 10px;
  width: 100%;
  max-width: 330px;
  box-shadow: 0 18px 44px rgba(51, 48, 31, .20), var(--shadow-rest);
}
.phone img { display: block; width: 100%; height: auto; border-radius: 44px; }
@media (max-width: 640px) {
  .phone { border-radius: 46px; padding: 9px; }
  .phone img { border-radius: 38px; }
}

/* Scroll-linked hero parallax. Motion's scroll() drives this now (see
   assets/motion-init.js) because `animation-timeline` is still Chromium-only,
   and Safari and Firefox were getting no drift at all. This block is the
   no-JS path: same 26px, same range, for Chromium visitors without the module.
   The `html:not(.motion-js)` guard is what stops the two from stacking. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    html:not(.motion-js) .hero-stage .phone {
      animation: heroDrift linear both;
      animation-timeline: view();
      animation-range: exit 0% exit 100%;
    }
  }
}
/* Only the phone drifts. Joey keeps his rock (§7's one allowed loop) — layering
   a scroll transform on him would just fight that animation for the property. */
@keyframes heroDrift { to { transform: translateY(-26px); } }

/* ---------- leaves & sparkles ---------- */
.leaf, .spark { position: absolute; pointer-events: none; }
.spark { color: var(--sparkle); }

@media (prefers-reduced-motion: no-preference) {
  .sway { animation: sway 9s ease-in-out infinite; transform-origin: 50% 0%; }
  .sway.s2 { animation-duration: 12s; animation-delay: -3s; }
  .sway.s3 { animation-duration: 11s; animation-delay: -6s; }
  .twinkle { animation: twinkle 5s ease-in-out infinite; }
  .twinkle.t2 { animation-delay: -2.2s; }
  .twinkle.t3 { animation-delay: -3.6s; }
  .joey-rock { animation: joeyrock 7s ease-in-out infinite; transform-origin: 50% 92%; }
  /* the big Joeys blink — a beat apart, so they never sync up like robots */
  .eye { transform-box: fill-box; transform-origin: 50% 55%; animation: blink 6.4s ease-in-out infinite; }
  .joey-wave .eye { animation-delay: -3.2s; }
  /* CTA Joey's wave. JS only flips .waving on the svg (joeyInteractions);
     the animation itself is pure CSS, one shot per hello. */
  .wave-arm { transform-box: fill-box; transform-origin: 16% 94%; }
  .waving .wave-arm { animation: joeywave 1.1s ease-in-out 1; }
}
@keyframes sway {
  0%, 100% { transform: rotate(-6deg) translateY(0); }
  50% { transform: rotate(7deg) translateY(6px); }
}
@keyframes twinkle {
  0%, 100% { opacity: .35; }
  50% { opacity: .95; }
}
@keyframes joeyrock {
  0%, 100% { transform: rotate(-2.4deg); }
  50% { transform: rotate(2.4deg); }
}
@keyframes blink {
  0%, 92%, 100% { transform: scaleY(1); }
  94.5%, 96% { transform: scaleY(.12); }
}
@keyframes joeywave {
  0%, 100% { transform: rotate(0deg); }
  28% { transform: rotate(14deg); }
  56% { transform: rotate(-9deg); }
  82% { transform: rotate(10deg); }
}

/* ---------- fact strip ---------- */
.strip { padding: 8px 0 clamp(40px, 6vw, 64px); }
.strip-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 14px;
}
.fact {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-rest);
  padding: 16px 18px;
  display: flex;
  align-items: center;
  gap: 12px;
  font-weight: 800;
  font-size: 15px;
  line-height: 1.35;
  transition: transform var(--spring-snappy), box-shadow var(--t-snappy) ease;
}
@media (hover: hover) {
  .fact:hover { transform: translateY(-3px); box-shadow: var(--shadow-float); }
  .fact:hover .fe { transform: rotate(-10deg) scale(1.18); }
}
.fact .fe { font-size: 24px; flex: none; transition: transform var(--spring-bouncy); }
@media (max-width: 900px) { .strip-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .strip-grid { grid-template-columns: 1fr 1fr; gap: 10px; } .fact { font-size: 13.5px; padding: 13px 14px; flex-direction: column; text-align: center; gap: 8px; } }

/* ---------- feature spreads ---------- */
.spread { padding: clamp(48px, 7vw, 96px) 0; }
.spread-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(36px, 6vw, 88px);
  align-items: center;
}
.spread-grid .phone { margin: 0 auto; }
.spread.flip .spread-copy { order: 2; }
.spread h2 { font-size: clamp(30px, 3.6vw, 44px); }
.spread .body { color: var(--ink-2); font-size: clamp(16.5px, 1.4vw, 18.5px); max-width: 33em; }
.checks { list-style: none; margin: 24px 0 0; padding: 0; display: grid; gap: 13px; }
.checks li { display: flex; gap: 12px; align-items: flex-start; font-weight: 700; font-size: 16px; }
.checks .ck {
  flex: none;
  width: 24px; height: 24px;
  border-radius: 50%;
  background: var(--success);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
}
.checks .ck svg { width: 12px; height: 12px; }
@media (max-width: 900px) {
  .spread-grid { grid-template-columns: 1fr; }
  .spread.flip .spread-copy { order: 0; }
  .spread-grid .phone { max-width: min(290px, 76vw); }
}

/* stage behind spread phones */
.spread-stage { position: relative; }
.spread-stage .blob {
  position: absolute;
  inset: auto;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: min(470px, 100%);
  aspect-ratio: 1;
  border-radius: 50%;
  z-index: 0;
}
.spread-stage .phone { position: relative; z-index: 2; }
.blob.mist { background: radial-gradient(circle at 50% 45%, #EBEDDA 0%, #E9EAD8 52%, rgba(233, 234, 216, 0) 72%); }
.blob.peach { background: radial-gradient(circle at 50% 45%, #F6E3CE 0%, #F4DFC6 52%, rgba(244, 223, 198, 0) 72%); }
.blob.linen { background: radial-gradient(circle at 50% 45%, #F4E8D3 0%, #F1E3CA 52%, rgba(241, 227, 202, 0) 72%); }

/* ---------- night band (Ask Joey) ---------- */
.night {
  position: relative;
  background: linear-gradient(180deg, #33503F 0%, var(--pine) 34%, var(--pine-deep) 100%);
  color: #F2E9D8;
  padding: clamp(64px, 8vw, 110px) 0;
  overflow: hidden;
}
.night h2 { color: #FBF4E4; font-size: clamp(30px, 3.6vw, 44px); }
.night .eyebrow { color: var(--moss-soft); }
.night .body { color: rgba(242, 233, 216, .82); font-size: clamp(16.5px, 1.4vw, 18.5px); max-width: 33em; }
.night .checks li { color: #F2E9D8; }
.night .checks .ck { background: rgba(127, 191, 149, .28); }
.night .spark { color: #E8D5A8; }
.night .moon {
  position: absolute;
  top: 64px; right: 8%;
  width: 74px; height: 74px;
  opacity: .9;
}
.night .star { position: absolute; border-radius: 50%; background: #E8D5A8; }
.night-note {
  margin-top: 22px;
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 14px;
  font-weight: 700;
  color: rgba(242, 233, 216, .75);
  background: rgba(250, 244, 231, .08);
  border: 1px solid rgba(250, 244, 231, .14);
  padding: 10px 16px;
  border-radius: 999px;
}

/* ---------- meet joey ---------- */
.joey-band { padding: clamp(56px, 8vw, 104px) 0; }
.joey-head { text-align: center; max-width: 640px; margin: 0 auto 46px; }
.joey-head h2 { font-size: clamp(30px, 3.6vw, 44px); }
.joey-head .body { color: var(--ink-2); font-size: clamp(16.5px, 1.4vw, 18.5px); }
.pose-row {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
  margin-bottom: 26px;
}
.pose-tile {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-rest);
  padding: 18px 12px 14px;
  text-align: center;
  transition: transform var(--spring-bouncy), box-shadow var(--t-bouncy) ease, opacity var(--t-bouncy) ease;
}
.pose-tile svg { width: min(150px, 78%); height: auto; }
.pose-tile .pname {
  font-family: var(--display);
  font-weight: 700;
  font-size: 17px;
  color: var(--moss);
  margin-top: 4px;
}
.pose-tile .pband { font-size: 12.5px; font-weight: 700; color: var(--ink-2); }
/* Each tile pops on its band with the app's `bouncy` spring — the same curve
   the kid card uses when an item is packed. */
@media (prefers-reduced-motion: no-preference) {
  .pose-tile { opacity: .62; animation-timing-function: var(--e-bouncy); }
  /* Timed loop only when JS isn't driving. With the module in, scrolling the
     band is what packs the bag — see joeyMeter() in assets/motion-init.js. */
  html:not(.motion-js) .pose-tile.k1 { animation: bandlift 16s var(--e-bouncy) infinite; }
  html:not(.motion-js) .pose-tile.k2 { animation: bandlift2 16s var(--e-bouncy) infinite; }
  html:not(.motion-js) .pose-tile.k3 { animation: bandlift3 16s var(--e-bouncy) infinite; }
  html:not(.motion-js) .pose-tile.k4 { animation: bandlift4 16s var(--e-bouncy) infinite; }
}
@keyframes bandlift  { 0%, 8% { opacity: 1; transform: translateY(-6px); } 12%, 100% { opacity: .62; transform: none; } }
@keyframes bandlift2 { 0%, 8% { opacity: .62; } 12%, 32% { opacity: 1; transform: translateY(-6px); } 36%, 100% { opacity: .62; transform: none; } }
@keyframes bandlift3 { 0%, 32% { opacity: .62; } 36%, 58% { opacity: 1; transform: translateY(-6px); } 62%, 100% { opacity: .62; transform: none; } }
@keyframes bandlift4 { 0%, 58% { opacity: .62; } 64%, 94% { opacity: 1; transform: translateY(-6px); } 98%, 100% { opacity: .62; transform: none; } }

.demo-meter {
  position: relative;      /* anchors the confetti bits */
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: 999px;
  box-shadow: var(--shadow-float);
  padding: 16px 20px;
  display: flex;
  align-items: center;
  gap: 16px;
  max-width: 620px;
  margin: 0 auto;
}
/* the quiet sparkle at 100% — little petals thrown by joeyMeter(), each one a
   single WAAPI flight on transform + opacity, removed when it fades */
.confetti-bit {
  position: absolute;
  width: 7px; height: 11px;
  margin: -6px 0 0 -4px;
  border-radius: 4px;
  pointer-events: none;
  z-index: 3;
}
.demo-track {
  position: relative;
  flex: 1;
  height: 14px;
  background: var(--moss-mist);
  border-radius: 999px;
  overflow: hidden;
}
.demo-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 100%;
  background: var(--success);
  border-radius: 999px;
  /* JS scrubs scaleX, not width — width would relayout the track every frame */
  transform-origin: left center;
}
.demo-count { display: grid; font-weight: 800; font-size: 15.5px; color: var(--ink-2); font-variant-numeric: tabular-nums; }
.demo-count > span { grid-area: 1 / 1; white-space: nowrap; text-align: right; }
.demo-count .dc-packing { opacity: 0; }
/* inline-block so the celebration pop can scale it */
.demo-count .seal { color: var(--success); display: inline-block; }
/* The fill uses `settle`, exactly as the app's progress meters do. Same story
   as the poses: the timed loop is the fallback, scroll is the real thing. */
@media (prefers-reduced-motion: no-preference) {
  html:not(.motion-js) .demo-fill { animation: meterfill 16s var(--e-settle) infinite; }
  html:not(.motion-js) .demo-count .dc-packing { animation: dcpack 16s linear infinite; }
  html:not(.motion-js) .demo-count .dc-done { animation: dcdone 16s linear infinite; }
}
@keyframes dcpack { 0%, 60% { opacity: 1; } 64%, 100% { opacity: 0; } }
@keyframes dcdone { 0%, 60% { opacity: 0; } 64%, 100% { opacity: 1; } }
/* scaleX, same as the JS scrub — the old width keyframes relaid out the track
   on every animated frame (MotionScore D); a transform stays on the compositor */
@keyframes meterfill {
  0%, 8%   { transform: scaleX(.05); }
  12%, 32% { transform: scaleX(.34); }
  36%, 58% { transform: scaleX(.66); }
  64%, 100% { transform: scaleX(1); }
}
.joey-caption { text-align: center; color: var(--ink-2); font-size: 14.5px; font-weight: 600; margin-top: 18px; }
@media (max-width: 760px) {
  .pose-row { grid-template-columns: 1fr 1fr; }
}

/* ---------- pricing ---------- */
.plus-band { padding: clamp(56px, 8vw, 104px) 0; background: var(--paper-deep); border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline); position: relative; overflow: hidden; }
.plus-head { text-align: center; max-width: 620px; margin: 0 auto 46px; }
.plus-head h2 { font-size: clamp(30px, 3.6vw, 44px); }
.plus-head .body { color: var(--ink-2); font-size: clamp(16.5px, 1.4vw, 18.5px); }
.price-grid {
  display: grid;
  grid-template-columns: 1fr 1.08fr;
  gap: 22px;
  align-items: stretch;
  max-width: 900px;
  margin: 0 auto;
}
.price-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-hero);
  box-shadow: var(--shadow-rest);
  padding: 34px 32px 30px;
  display: flex;
  flex-direction: column;
}
.price-card.plus { border: 2px solid var(--moss); box-shadow: var(--shadow-float); }
.price-card h3 { font-size: 26px; display: flex; align-items: center; gap: 10px; margin-bottom: 2px; }
.price-card .price {
  font-family: var(--display);
  font-weight: 800;
  font-size: 40px;
  color: var(--ink);
  line-height: 1.1;
}
.price-card .price small { display: block; margin-top: 4px; line-height: 1.4; font-size: 15px; font-weight: 700; color: var(--ink-2); font-family: var(--body); }
.price-card .checks { margin-top: 20px; }
.price-card .checks li { font-size: 15.5px; font-weight: 700; }
.price-note { max-width: 72ch; margin-inline: auto; text-align: center; color: var(--ink-2); font-size: 14.5px; font-weight: 600; margin-top: 26px; }
@media (max-width: 820px) { .price-grid { grid-template-columns: 1fr; } }

/* ---------- privacy ---------- */
.privacy-band { padding: clamp(56px, 8vw, 104px) 0; }
.privacy-card {
  background: var(--linen);
  border: 1px solid var(--hairline);
  border-radius: var(--r-hero);
  padding: clamp(32px, 5vw, 56px);
  display: grid;
  grid-template-columns: 1.1fr .9fr;
  gap: clamp(28px, 4vw, 56px);
  align-items: center;
  position: relative;
  overflow: hidden;
}
.privacy-card h2 { font-size: clamp(28px, 3.2vw, 40px); }
.privacy-card .body { color: var(--ink-2); }
.privacy-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.privacy-list li {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  padding: 13px 16px;
  font-weight: 700;
  font-size: 15px;
  display: flex;
  gap: 11px;
  align-items: flex-start;
  box-shadow: var(--shadow-rest);
}
.privacy-list .pe { flex: none; font-size: 19px; line-height: 1.4; }
@media (max-width: 820px) { .privacy-card { grid-template-columns: 1fr; } }

/* ---------- languages ---------- */
.lang-band { padding: 0 0 clamp(56px, 8vw, 96px); text-align: center; }
.lang-band h2 { font-size: clamp(26px, 3vw, 36px); }
.lang-band .body { color: var(--ink-2); max-width: 34em; margin: 0 auto 26px; }
.lang-chips { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; max-width: 760px; margin: 0 auto; }
.lang-chips span {
  background: var(--surface);
  border: 1px solid var(--hairline);
  box-shadow: var(--shadow-rest);
  border-radius: 999px;
  padding: 9px 18px;
  font-weight: 700;
  font-size: 15px;
}

/* ---------- FAQ ---------- */
.faq-band { padding: 0 0 clamp(64px, 9vw, 110px); }
.faq-band h2 { text-align: center; font-size: clamp(28px, 3.2vw, 40px); margin-bottom: 34px; }
.faq-list { max-width: 760px; margin: 0 auto; display: grid; gap: 12px; }
.faq-list details {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-rest);
  padding: 0 22px;
}
.faq-list summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 0;
  font-weight: 800;
  font-size: 16.5px;
}
.faq-list summary::-webkit-details-marker { display: none; }
.faq-list summary .plus-ic {
  flex: none;
  width: 26px; height: 26px;
  border-radius: 50%;
  background: var(--moss-mist);
  color: var(--moss);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 800;
  font-size: 17px;
  transition: transform var(--spring-snappy), background var(--t-snappy) ease;
}
.faq-list details[open] summary .plus-ic { transform: rotate(45deg); }
.faq-list summary:hover .plus-ic { background: var(--moss); color: #fff; }
.faq-list .answer { padding: 0 0 20px; color: var(--ink-2); font-size: 15.5px; max-width: 60ch; }
.faq-list .answer p { margin-bottom: .7em; }
.faq-list .answer p:last-child { margin-bottom: 0; }

/* ---------- final CTA ---------- */
.get-band { padding: 0 0 clamp(72px, 9vw, 120px); }
.get-card {
  position: relative;
  overflow: hidden;
  background: linear-gradient(180deg, #3F6751 0%, var(--pine) 78%);
  border-radius: var(--r-hero);
  color: #FBF4E4;
  text-align: center;
  padding: clamp(48px, 7vw, 84px) 24px clamp(44px, 6vw, 72px);
  box-shadow: var(--shadow-float);
}
.get-card h2 { color: #FBF4E4; font-size: clamp(32px, 4vw, 48px); }
.get-card .body { color: rgba(251, 244, 228, .85); font-size: clamp(16.5px, 1.5vw, 19px); max-width: 30em; margin: 0 auto 30px; }
.get-card .joey-wave { width: clamp(150px, 20vw, 200px); margin: 0 auto 6px; filter: drop-shadow(0 10px 18px rgba(22, 36, 28, .35)); }
.get-card .reqs { margin-top: 22px; font-size: 13.5px; font-weight: 700; color: rgba(251, 244, 228, .72); }
.get-card .soon-note { margin-top: 14px; font-size: 14.5px; font-weight: 700; color: rgba(251, 244, 228, .85); }
.get-card .soon-note a { color: #F2D9A4; }
.get-card .store-badge { background: #FBF4E4; color: var(--pine); }
.get-card .store-badge:hover { color: var(--pine-deep); }
.get-card .spark { color: #E8D5A8; }

/* ---------- footer ---------- */
.site-footer { border-top: 1px solid var(--hairline); background: var(--paper); padding: 40px 0 46px; }
.footer-grid { display: flex; flex-wrap: wrap; gap: 28px; align-items: flex-start; justify-content: space-between; }
.footer-brand { display: flex; align-items: center; gap: 11px; }
.footer-brand img { width: 34px; height: 34px; border-radius: 9px; }
.footer-brand div { line-height: 1.3; }
.footer-brand strong { font-family: var(--display); font-size: 19px; }
.footer-brand small { color: var(--ink-2); font-weight: 600; font-size: 13px; }
.footer-links { display: flex; gap: 26px; flex-wrap: wrap; }
.footer-links a { font-weight: 700; font-size: 14.5px; color: var(--ink-2); text-decoration: none; }
.footer-links a:hover { color: var(--moss); }
.footer-fine { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--hairline); color: var(--ink-2); font-size: 13px; font-weight: 600; display: flex; flex-wrap: wrap; gap: 6px 18px; justify-content: space-between; }
.footer-fine a { color: var(--ink-2); }

/* ---------- reveal on scroll ---------- */
/* CSS holds nothing but the start state; Motion's inView + animate does the
   move (assets/motion-init.js), which is what buys the spring on transform and
   a plain linear fade running alongside it rather than sharing its curve.
   Both guards matter: `.motion-js` lands on <html> only if JS actually ran, so
   a blocked or broken module leaves the page fully visible, never blank. */
@media (prefers-reduced-motion: no-preference) {
  html.motion-js .reveal { opacity: 0; }
  /* Thirteen chips land in sequence rather than as one block — see
     languageChips(). Hidden here so they never flash in first. */
  html.motion-js .lang-chips span { opacity: 0; }
  /* The hero entrance (heroEntrance in motion-init.js). Same failure story as
     .reveal: these are hidden only while .motion-js proves the module is
     alive, so a blocked script still means a whole page, instantly. The
     sticker fades but never moves — its transform belongs to the scroll
     parallax, and two writers on one property is the bug we keep not having. */
  html.motion-js :is(.hero .eyebrow, .hero h1 .w, .hero .lede, .hero .cta-row,
    .hero .fine, .hero-stage, .hero-joey, .hero-sticker) { opacity: 0; }
}

/* ---------- subpages (privacy / support / 404) ---------- */
.page { padding: clamp(40px, 6vw, 72px) 0 clamp(64px, 8vw, 110px); }
.page-narrow { max-width: 720px; margin: 0 auto; }
.page-head { text-align: center; margin-bottom: 40px; }
.page-head svg, .page-head .page-joey { width: 150px; height: auto; margin: 0 auto 10px; }

/* The subpages (privacy / support / 404) carry a single Joey and nothing else
   that moves, so they deliberately don't load the Motion module — 26kb to
   animate one mascot is a bad trade. He gets the same `settle` spring in pure
   CSS instead, then hands the transform over to the idle rock. Both animate
   `transform`, and the later entry in the list wins once it starts, so the
   delay on the loop is what sequences them. */
@media (prefers-reduced-motion: no-preference) {
  /* Not scoped to .page-head — the 404's Joey sits straight in the container */
  .page-joey {
    transform-origin: 50% 92%;
    animation:
      pageJoeyIn var(--d-settle) var(--e-settle) both,
      joeyrock 7s ease-in-out var(--d-settle) infinite;
  }
}
@keyframes pageJoeyIn {
  from { opacity: 0; transform: translateY(16px) scale(.94); }
  to   { opacity: 1; transform: none; }
}
.page-head h1 { font-size: clamp(34px, 4.4vw, 48px); }
.page-head .sub { color: var(--ink-2); font-size: 17.5px; max-width: 34em; margin: 0 auto; }
.page h2 { font-size: 24px; margin-top: 44px; }
.page h2:first-of-type { margin-top: 0; }
.page .updated { text-align: center; font-size: 13.5px; font-weight: 700; color: var(--ink-2); margin-top: -26px; margin-bottom: 40px; }
.tldr {
  background: var(--linen);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  padding: 24px 28px;
  margin-bottom: 40px;
}
.tldr strong { font-family: var(--display); font-size: 18px; }
.tldr ul { margin: 12px 0 0; padding-left: 0; list-style: none; display: grid; gap: 8px; }
.tldr li { display: flex; gap: 10px; font-weight: 700; font-size: 15px; }
.tldr li::before { content: "✓"; color: var(--success); font-weight: 800; }
.page p, .page li { color: #4A4437; }
.page ul.plain { padding-left: 22px; }
.page ul.plain li { margin-bottom: 8px; }
.contact-card {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-rest);
  padding: 26px 28px;
  margin: 26px 0;
}
.contact-card .mail {
  font-family: var(--display);
  font-size: clamp(20px, 3vw, 26px);
  font-weight: 700;
  color: var(--moss);
  text-decoration: none;
  word-break: break-all;
}
.contact-card .mail:hover { color: var(--moss-deep); }
.back-home { display: inline-flex; margin-bottom: 28px; font-weight: 700; font-size: 15px; color: var(--ink-2); text-decoration: none; }
.back-home:hover { color: var(--moss); }
