/* Self-hosted so no visitor IP reaches a font CDN, and so the page
   renders its own typefaces with nothing render-blocking off-origin. */
@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-400-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-400-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-500-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-600-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-700-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-700-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-800-vietnamese.woff2) format('woff2');
  unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}

@font-face {
  font-family: 'Be Vietnam Pro';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url(/fonts/BeVietnamPro-800-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Jost';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/Jost-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url(/fonts/SpaceGrotesk-500-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url(/fonts/SpaceGrotesk-600-latin.woff2) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* The app's design system restated for the web. Each annotated line names the
   Dart constant it came from, and landing/tools/check-tokens.mjs re-reads those
   on every deploy, so a drifted token fails instead of shipping a second
   palette. */

:root{
  /* Night ground. The app's month-story takeover is its one dark surface, so
     the landing borrows that register rather than inventing a dark theme. */
  --night-top:#3A2F63;      /* AppColors.storyNightTop */
  --night-bottom:#272044;   /* AppColors.storyNightBottom */
  --night-ink:#F2EEFC;      /* AppColors.storyNightInk */
  --night-muted:#CFC6EC;    /* AppColors.storyNightMuted */
  --night-accent:#B3A6F0;   /* AppColors.storyNightAccent */

  /* palette.dart, dark theme */
  --bg:#1E2229;             /* AppColors.darkBg */
  --surface:#262A31;        /* AppColors.darkSurface */
  --elevated:#30343D;       /* AppColors.darkElevated */
  --line:#E0E6F214;         /* AppColors.darkLine */
  --line-strong:#E0E6F226;  /* AppColors.darkLineStrong */
  --ink:#E2E6EE;            /* AppColors.darkText */
  --muted:#E2E6EE8F;        /* AppColors.darkMuted */
  --vio-bg:#282142;         /* AppColors.darkVioBg */
  --vio-line:#382E58;       /* AppColors.darkVioLine */
  --vio-ink:#B3A6F0;        /* AppColors.darkVioInk */
  --mint-ink:#6FCB9A;       /* AppColors.darkMintInk */
  --mint-bg-app:#E6F3EB;    /* AppColors.lightMintBg */
  --join-ink:#7FB3F0;       /* AppColors.darkJoinInk */
  --brand:#1FA567;          /* AppColors.brand */

  /* Light tokens, used only inside the phone: that is the real app, and it is
     light. The contrast against the night page is the point. */
  --app-bg:#F7F7F7;         /* AppColors.lightBg */
  --app-surface:#FFFFFF;    /* AppColors.lightSurface */
  --app-elevated:#EEF1F3;   /* AppColors.lightElevated */
  --app-line:#E9EDF0;       /* AppColors.lightLine */
  --app-line-strong:#DCE2E7;/* AppColors.lightLineStrong */
  --app-ink:#141D26;        /* AppColors.lightText */
  --app-muted:#7B8794;      /* AppColors.lightMuted */
  --icon-neutral:#5A6874;   /* AppColors.iconNeutral */
  --icon-tile:#F5F7F8;      /* AppColors.iconTileFill */
  --debit:#E0604F;          /* AppColors.debit */

  /* spacing.dart */
  --s-xs:4px;   /* AppSpacing.xs */
  --s-s:8px;    /* AppSpacing.s */
  --s-m:12px;   /* AppSpacing.m */
  --s-m14:14px; /* AppSpacing.m14 */
  --s-l:16px;   /* AppSpacing.l */
  --s-xl:20px;  /* AppSpacing.xl */
  --s-xxl:24px; /* AppSpacing.xxl */
  /* Page rhythm above the app's scale: a landing section needs air a phone
     screen never has. Deliberately not in spacing.dart, so nothing to check. */
  --s-3xl:40px; --s-4xl:64px; --s-5xl:96px;

  --r-xs:10px;    /* AppRadius.xs */
  --r-sm:12px;    /* AppRadius.sm */
  --r-md:14px;    /* AppRadius.md */
  --r-lg:20px;    /* AppRadius.lg */
  --r-xl:28px;    /* AppRadius.xl */
  --r-full:999px; /* AppRadius.full */

  /* Film grain for the sky, so its large flat areas never band on a cheap
     panel. Deliberately not reused over the orb, see .phone below. */
  --grain:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");

  --font:"Be Vietnam Pro",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  --font-num:"Space Grotesk","Be Vietnam Pro",ui-monospace,monospace;
  --font-brand:"Jost","Be Vietnam Pro",sans-serif;

  --wrap:1120px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --glass:#E2E6EE0A;

  /* The sticky nav's own height. The hero subtracts it to fill exactly one
     screen, so it has to be a number both can read. */
  --nav-h:68px;
  /* The phone's design box, inside its bezel: 346-2*9 wide, 716-2*9 tall.
     main.js scales the screen to whatever the hero can spare by zooming this
     box, so every value inside the phone stays the one the app uses. */
  --app-w:328px;
  --app-h:698px;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--bg);
  color:var(--ink);
  font-family:var(--font);
  font-size:16px;
  line-height:1.55;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

h1,h2,h3{margin:0;letter-spacing:-.026em;line-height:1.1;font-weight:800;text-wrap:balance}
h1{font-size:clamp(36px,5.4vw,62px)}
h2{font-size:clamp(27px,3.5vw,40px)}
p{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
img,svg,canvas{display:block;max-width:100%}
a{color:inherit}

.wrap{position:relative;z-index:1;width:100%;max-width:var(--wrap);margin:0 auto;padding:0 var(--s-xxl)}
.wrap-narrow{max-width:780px}
.center{text-align:center}

/* Type that carries the AI register: the headline resolves from ink into the
   coach's violet, the same accent→violet travel the Aurora ribbon draws. */
.grad{
  background:linear-gradient(102deg,var(--night-ink) 6%,var(--night-accent) 58%,var(--mint-ink) 104%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}

.skip{
  position:absolute;left:var(--s-l);top:-60px;z-index:60;
  background:var(--night-ink);color:var(--night-bottom);padding:12px var(--s-l);
  border-radius:var(--r-sm);font-weight:700;font-size:14px;
  transition:top .18s var(--ease);
}
.skip:focus-visible{top:var(--s-l)}
:focus-visible{outline:2px solid var(--night-accent);outline-offset:3px;border-radius:var(--r-xs)}

/* The sprite is markup, never layout: it must not claim a line box before the
   stylesheet's own rules ever apply to it. */
.sprite{position:absolute;width:0;height:0;overflow:hidden}

/* Stroke properties are set on the <svg>, not on its paths, because they have
   to inherit into a <use> element's shadow tree, which a `.ic path` selector
   cannot reach. */
.ic{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}

/* ── Ambient layers ──────────────────────────────────────────────
   Three fixed layers under everything: a violet-to-charcoal wash, a faint
   grid that fades out from the top, and film grain so the large flat areas
   never band on a cheap panel. */
.sky{position:fixed;inset:0;z-index:0;pointer-events:none}
.sky-wash{
  position:absolute;inset:0;
  background:
    radial-gradient(118% 76% at 12% -14%,#3A2F6373,transparent 62%),
    radial-gradient(94% 60% at 92% 2%,#282142a6,transparent 60%),
    radial-gradient(74% 48% at 84% 86%,#2E2552b3,transparent 66%),
    radial-gradient(62% 42% at 2% 68%,#1B3A3266,transparent 68%),
    linear-gradient(180deg,var(--night-bottom) 0%,var(--bg) 46%,var(--bg) 100%);
}
.sky-grid{
  position:absolute;inset:0;opacity:.5;
  background-image:
    linear-gradient(var(--line) 1px,transparent 1px),
    linear-gradient(90deg,var(--line) 1px,transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:radial-gradient(120% 62% at 50% 0%,#000 8%,transparent 72%);
          mask-image:radial-gradient(120% 62% at 50% 0%,#000 8%,transparent 72%);
}
.sky-grain{position:absolute;inset:0;opacity:.05;mix-blend-mode:overlay;background-image:var(--grain)}

/* ── Glass ───────────────────────────────────────────────────────
   One surface recipe, everywhere. The ::before draws a hairline that is bright
   at the top-left and gone by the middle, which is what reads as "lit from
   above" rather than "outlined". */
.glass{position:relative;background:var(--glass);border-radius:var(--r-lg);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}
.glass::before,.glass::after{
  content:"";position:absolute;inset:0;border-radius:inherit;padding:1px;pointer-events:none;
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;mask-composite:exclude;
}
.glass::before{background:linear-gradient(155deg,#E2E6EE40,#E2E6EE0F 34%,transparent 62%)}
/* The lit ring is its own layer faded in, not a gradient swapped on ::before:
   background-image is not interpolable, so a transition on it resolves to a
   discrete swap and the ring snapped on instead of arriving. */
.glass::after{
  background:linear-gradient(155deg,#B3A6F0a6,#E2E6EE29 40%,transparent 66%);
  opacity:0;transition:opacity .34s var(--ease);
}
.glass:hover::after{opacity:1}

/* ── Buttons ─────────────────────────────────────────────────────
   The landing CTA is deliberately not the app's primary button (Nhan,
   2026-08-31): a slab of brand green reads heavy at this size and on this
   ground. It is a light capsule instead, ringed and lit by green and coach
   violet, so the brand colours arrive as light rather than as fill. Inside the
   phone the app's own green button stays green, because that one is the app. */
/* Hover is the Aurora trace wrapped round the capsule, not a green shadow: the
   ring's accent→violet stops travel round the rim while a soft band of the same
   light tides across the face. The trace's two rules hold here too — that
   colour pair only, and nothing moves under reduced motion. */
.btn{
  position:relative;overflow:hidden;isolation:isolate;
  display:inline-flex;align-items:center;gap:var(--s-m);
  min-height:48px;padding:0 6px 0 var(--s-xl);
  border:1px solid transparent;border-radius:var(--r-full);
  background:
    linear-gradient(180deg,#FFFFFF,#EDE8F8) padding-box,
    repeating-linear-gradient(100deg,
      var(--brand) 0px,var(--mint-ink) 44px,var(--night-accent) 88px,
      #7C5CBF 132px,var(--brand) 176px) border-box;
  background-position:0 0,0 0;
  color:var(--night-bottom);
  font:inherit;font-weight:700;font-size:15px;letter-spacing:-.012em;
  text-decoration:none;white-space:nowrap;
  box-shadow:0 10px 30px -16px #1FA567b3,0 14px 40px -18px #B3A6F0e6;
  transition:transform .2s var(--ease),box-shadow .2s var(--ease);
}
/* The band that tides across the face. Sits behind the label, which is why the
   label and chevron are lifted a layer. */
.btn::after{
  content:"";position:absolute;inset:-40% -30%;z-index:-1;pointer-events:none;
  background:linear-gradient(100deg,
    transparent 24%,#1FA56761 38%,#B3A6F0a6 52%,#7C5CBF5c 62%,transparent 76%);
  filter:blur(10px);opacity:0;
  transition:opacity .28s var(--ease);
}
.btn-label,.btn .chev{position:relative;z-index:1}
.btn:hover{
  transform:translateY(-1px);
  box-shadow:0 14px 36px -14px #1FA567,0 20px 54px -18px #B3A6F0;
  /* Period equals the ring gradient's, so the loop has no seam. */
  animation:auroraRing 2.8s linear infinite;
}
.btn:hover::after{opacity:1;animation:auroraTide 3.4s var(--ease) infinite alternate}
@keyframes auroraRing{to{background-position:0 0,176px 0}}
@keyframes auroraTide{from{transform:translateX(-26%)}to{transform:translateX(26%)}}
.btn:active{transform:translateY(0)}
.btn-lg{min-height:58px;padding:0 8px 0 var(--s-xxl);font-size:16.5px}
/* Both sizes reserve their right side for the chevron pill, so a button
   without one sits lopsided. This gives that side its padding back. */
.btn-plain{padding-inline:var(--s-xxl)}
.btn-sm{min-height:44px;padding:0 5px 0 var(--s-l);font-size:14px;gap:var(--s-s);
  box-shadow:0 6px 18px -12px #1FA56799,0 8px 22px -14px #B3A6F0b3}

/* The chevron carries its own chip, so the capsule has a detail of its own
   rather than a bare glyph floating at the end. */
.chev{
  width:28px;height:28px;padding:7px;flex:0 0 auto;box-sizing:border-box;
  border-radius:var(--r-full);background:#7C5CBF1f;color:#4E3D8F;
  stroke-width:2.4;
  transition:background .2s var(--ease),transform .2s var(--ease);
}
.btn-lg .chev{width:32px;height:32px;padding:8px}
.btn-sm .chev{width:25px;height:25px;padding:6px}
.btn:hover .chev{background:#7C5CBF3d;transform:translateX(2px)}

.link{
  display:inline-flex;align-items:center;gap:6px;padding:10px 0;
  color:var(--night-accent);font-weight:700;font-size:15px;text-decoration:none;
  border-bottom:1px solid transparent;
}
.link:hover{border-bottom-color:currentColor}
.link .chev{width:15px;height:15px;padding:0;background:none;color:inherit;border-radius:0;stroke-width:2.2}
.link:hover .chev{background:none;transform:translateX(2px)}

.eyebrow{
  display:inline-flex;align-items:center;gap:var(--s-s);
  font-size:12px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--night-muted);margin-bottom:var(--s-l);
}
.eyebrow::before{
  content:"";width:22px;height:1px;flex:0 0 auto;
  background:linear-gradient(90deg,transparent,var(--night-accent));
}
.section-label{
  font-size:12px;font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:var(--night-muted);margin-bottom:var(--s-xl);
}
.section-lede{color:var(--muted);font-size:clamp(16px,1.6vw,18.5px);max-width:60ch;margin-top:var(--s-l)}
.center .section-lede{margin-left:auto;margin-right:auto}

/* ── Nav ─────────────────────────────────────────────────────── */
.nav{position:sticky;top:0;z-index:50;transition:background .25s var(--ease),box-shadow .25s var(--ease),backdrop-filter .25s var(--ease)}
.nav.stuck{background:#1E2229c7;backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px);box-shadow:0 1px 0 var(--line)}
/* Height is pinned rather than left to the tallest child: the hero subtracts
   --nav-h to fill exactly one screen, and a nav half a pixel taller than the
   number pushes the hero into a scrollbar. */
.nav-inner{
  position:relative;z-index:1;height:var(--nav-h);
  width:100%;max-width:var(--wrap);margin:0 auto;padding:0 var(--s-xxl);
  display:flex;align-items:center;gap:var(--s-xxl);
}
.nav-links{display:flex;gap:var(--s-xl);margin-left:auto;font-size:14.5px;font-weight:600;color:var(--muted)}
.nav-links a{text-decoration:none;padding:11px 0;transition:color .16s var(--ease)}
.nav-links a:hover{color:var(--night-ink)}
.nav-inner .btn-sm{margin-left:var(--s-s)}

/* ── Wordmark ────────────────────────────────────────────────────
   Geometry copied from lib/shared/widgets/app_wordmark.dart: the bubble claims
   .447em of the line, its ink is wider and overhangs, and it is biased left
   because a swapped glyph inherits no Ta kern. */
.wordmark{
  display:inline-flex;align-items:baseline;text-decoration:none;padding:5px 0;
  font-family:var(--font-brand);font-weight:600;font-size:22px;
  letter-spacing:-.015em;color:var(--night-ink);
}
.wordmark-sm{font-size:19px;padding:8px 0}
.wm-a{position:relative;display:inline-block;width:.447em;height:.46em;vertical-align:baseline}
.wm-a svg{position:absolute;left:-.0727em;top:0;width:.4784em;height:.6072em;overflow:visible}

/* ── Hero ────────────────────────────────────────────────────── */
/* overflow-x:clip, not hidden: the orb is wider than a phone viewport on
   purpose, and clip contains it without making the hero a scroll container. */
/* The first screen has to land whole: a visitor should never scroll to find
   out what the product looks like. The hero claims exactly one viewport under
   the nav, and the phone is sized off whatever height is left after the copy,
   the caption and the padding have taken theirs. */
.hero{
  position:relative;overflow-x:clip;
  min-height:calc(100svh - var(--nav-h));
  display:flex;align-items:center;
  padding:clamp(12px,2.2vh,36px) 0 clamp(20px,3vh,48px);
}
.hero-inner{
  position:relative;z-index:1;
  width:100%;max-width:var(--wrap);margin:0 auto;padding:0 var(--s-xxl);
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,470px);
  gap:clamp(28px,4vw,64px);align-items:center;
}
.hero h1{margin-bottom:var(--s-xl)}
.lede{color:var(--muted);font-size:clamp(16.5px,1.75vw,19px);max-width:50ch}
.hero-cta{margin-top:var(--s-3xl)}
.cta-note{margin-top:var(--s-l);color:var(--muted);font-size:13.5px}

/* ── Stage: orb behind, phone in front, coach card lifted out ─── */
/* The canvas animates the light; this static bloom guarantees the phone reads
   as backlit even on the first frame and under reduced motion. */
.stage{position:relative;display:grid;place-items:center;padding:var(--s-xxl) 0;isolation:isolate}
.stage::before{
  content:"";position:absolute;z-index:0;top:50%;left:50%;translate:-50% -50%;
  width:min(620px,124%);aspect-ratio:1;pointer-events:none;
  background:
    radial-gradient(closest-side,#B3A6F03D,transparent 74%),
    radial-gradient(closest-side at 50% 68%,#1FA5672E,transparent 70%);
}
.orb{
  /* Has to be much wider than the phone, which is opaque and stands in front of
     it: what reads is the halo around the edges, not the middle. Biased right
     of the phone rather than centred on it, because a symmetric orb this size
     spills into the copy column and takes the lede's contrast with it.
     max-width is reset because the global canvas rule would clamp it back. */
  position:absolute;top:50%;left:56%;translate:-50% -50%;
  width:min(880px,182%);max-width:none;aspect-ratio:1;
  pointer-events:none;
}
/* No dither layer over the orb. One was tried and reverted (2026-09-01): a
   grain overlay is a hard-edged box, so it drew a visible square around the
   orb and its tiled noise read as blocks against the soft light. If banding
   ever needs fixing here, it has to be masked to the same radial falloff the
   orb itself uses, not laid over it as a rectangle. */
.phone{
  /* Third term is the height budget: what one viewport has left once the nav,
     the hero's padding and the stage's are taken, converted to a width by the
     phone's own 346:716 ratio. Dropped from 236 when the demo caption below
     the stage was removed and gave its height back. */
  position:relative;z-index:2;
  width:min(346px,84vw,max(252px,(100svh - 200px) * .4832));
  aspect-ratio:346/716;
  background:linear-gradient(160deg,#3A404B,#14181E 46%);
  border-radius:44px;padding:9px;
  box-shadow:
    0 0 0 1px #E0E6F226,
    0 40px 90px -24px #0A0D12e6,
    0 0 70px -6px #B3A6F073,
    0 0 160px -20px #7C5CBF99,
    0 30px 150px -30px #1FA56773;
  transform:perspective(1600px) rotateY(-9deg) rotateX(2.5deg) rotateZ(.6deg);
  transition:transform .6s var(--ease);
}
.stage:hover .phone{transform:perspective(1600px) rotateY(-3deg) rotateX(1deg)}
.phone-glare{position:absolute;inset:0;border-radius:44px;pointer-events:none;background:linear-gradient(145deg,#FFFFFF2E,transparent 34%)}
.screen{position:relative;width:100%;height:100%;border-radius:36px;overflow:hidden;background:var(--app-bg)}
/* Laid out at the app's real size and then zoomed to fit the bezel, so every
   value below stays the one the Flutter widget uses instead of being
   re-guessed at whatever width the viewport allows. */
.app{
  position:absolute;left:0;top:0;
  width:var(--app-w);height:var(--app-h);
  zoom:var(--app-zoom,1);
  padding:var(--s-l) var(--s-m14) 0;font-size:13px;color:var(--app-ink);
}

/* The coach speaking, lifted clear of the phone so the page shows two product
   surfaces, not one. Violet because in this app violet is the coach's voice. */
.coach-float{
  /* Overhangs the phone's left edge by about a third of its width: far enough
     out to read as a second surface rather than a layer of the first, and
     narrow enough that its left edge still clears the copy column at every
     width the two-column hero applies to. Reaching further left put it over
     the CTA note. */
  position:absolute;z-index:3;left:-5%;bottom:13%;width:min(268px,62%);
  padding:var(--s-l);border-radius:var(--r-md);
  background:linear-gradient(150deg,#2E2650f2,#231D3df2);
  border:1px solid var(--vio-line);
  box-shadow:0 24px 60px -22px #0A0D12,0 0 40px -18px #B3A6F0;
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  opacity:0;transform:translateY(14px) scale(.97);
  transition:opacity .5s var(--ease),transform .5s var(--ease);
}
[data-state="saving"] .coach-float,
[data-state="idle"] .coach-float{opacity:1;transform:none}
.coach-head{display:flex;align-items:center;gap:7px;margin-bottom:var(--s-s);font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;color:var(--night-accent)}
.spark{width:13px;height:13px;flex:0 0 auto}
.coach-float p{font-size:13.5px;line-height:1.5;color:var(--night-ink)}
.coach-float b{color:var(--night-accent);font-weight:700}

/* ── Phone internals ─────────────────────────────────────────────
   The real Home, not an impression of it: app bar, month nav, HeroAmountsCard,
   the four-tile LauncherRow, a RECENT AppFieldGroup ending in the centred dots
   row, and the CaptureBar resting as a pill. Values follow the widgets in
   lib/screens/home_screen.dart and lib/features/, scaled to this phone. */

.app-bar{display:flex;align-items:center;justify-content:space-between;height:38px}
.app-wm{font-family:var(--font-brand);font-weight:600;font-size:17px;letter-spacing:-.015em;display:inline-flex;align-items:baseline;color:var(--app-ink)}
.app-avatar{width:28px;height:28px;border-radius:var(--r-full);background:var(--app-surface);border:1px solid var(--app-line-strong);display:grid;place-items:center;font-size:11px;font-weight:700;color:var(--app-muted)}

.app-month{display:flex;align-items:center;justify-content:space-between;height:40px;margin-bottom:2px}
.month-title{display:inline-flex;align-items:center;gap:5px;font-size:17px;font-weight:500;letter-spacing:-.02em}
.caret-down{width:15px;height:15px;stroke-width:2;color:var(--app-line-strong)}
.month-today{font-size:11.5px;color:var(--app-muted)}

/* HomeBriefStack: the numbers in front, the month's trend tucked 18px behind
   them (kBriefTuck). Column order puts the hero card first so it paints in
   front; the under-layer's negative margin is the tuck itself. */
.brief{position:relative;display:flex;flex-direction:column}
.brief-under{
  margin-top:-18px;
  padding:calc(18px + var(--s-m)) var(--s-l) var(--s-m);
  background:#FBFBFB;                 /* layerBg, lerp(lightSurface,lightBg,.5) */
  border-radius:0 0 var(--r-lg) var(--r-lg);
  box-shadow:0 4px 12px #141d260d;
}
.trend-title{display:block;font-size:10px;font-weight:600;letter-spacing:.07em;text-transform:uppercase;color:var(--app-muted)}
/* SpendLineChart: the month's cumulative spend in brand, the usual month's
   own curve dashed behind it. Drawn in main.js. */
.trend-chart{width:100%;height:32px;margin-top:6px}
.trend-axis{display:flex;justify-content:space-between;margin-top:var(--s-xs);font-size:9px;font-weight:600;color:var(--app-muted)}

/* HeroAmountsCard: income is hidden by default, so the expense figure takes
   the whole card, exactly as a new user sees it. */
.hero-card{
  position:relative;z-index:1;
  background:var(--app-surface);border-radius:var(--r-lg);
  padding:var(--s-l) var(--s-l) var(--s-m14);
  box-shadow:0 6px 16px #141d2612;
}
.hero-amount{font-family:var(--font-num);font-size:31px;font-weight:600;letter-spacing:-.03em;line-height:1;font-variant-numeric:tabular-nums}
.hero-label{margin-top:7px;font-size:11.5px;font-weight:500;color:var(--app-muted)}

/* LauncherRow: four equal tiles, a 28px glyph slot over a label. Budget and
   Recurring show ConcentricRings of their most-at-risk items. */
.launchers{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s-s);margin-top:var(--s-m)}
.launch{
  min-height:66px;background:var(--app-surface);border-radius:var(--r-md);
  padding:var(--s-m) 3px;display:grid;justify-items:center;align-content:center;gap:6px;
}
.launch-glyph{width:26px;height:26px;display:grid;place-items:center}
.launch-glyph.tile{background:var(--icon-tile);border-radius:9px;color:var(--icon-neutral)}
.launch-glyph.tile .ic{width:16px;height:16px}
.rings{width:26px;height:26px}
.launch-label{font-size:9.5px;font-weight:600;color:var(--app-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}

.app-label{margin:var(--s-l) 0 var(--s-s) var(--s-xs);font-size:10px;font-weight:600;letter-spacing:.08em;color:#A6B0BA}

/* AppFieldGroup: one card, hairline between rows, never a stack of cards. */
.group{background:var(--app-surface);border:1px solid var(--app-line);border-radius:var(--r-md);overflow:hidden}
.group > * + *{border-top:1px solid var(--app-line)}
.tx{display:flex;align-items:center;gap:var(--s-m);padding:8px 11px;min-height:50px}
.tx-tile{width:30px;height:30px;flex:0 0 auto;border-radius:var(--r-xs);background:var(--icon-tile);color:var(--icon-neutral);display:grid;place-items:center}
.tx-tile .ic{width:16px;height:16px}
.tx-main{flex:1;min-width:0}
.tx-main b{display:block;font-size:12.5px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--app-ink)}
.tx-main span{display:block;font-size:10.5px;color:var(--app-muted)}
.tx-amt{font-family:var(--font-num);font-size:12.5px;font-weight:600;color:var(--debit);font-variant-numeric:tabular-nums;white-space:nowrap}
.tx.landing{animation:rowLand .5s var(--ease) both}
@keyframes rowLand{from{opacity:0;transform:translateY(-10px) scale(.97)}to{opacity:1;transform:none}}
/* The saved row winding back out at the end of a cycle. Height and padding go
   with the opacity so the rows below close the gap instead of jumping when the
   node is finally removed. */
.tx.leaving{animation:rowLeave .6s var(--ease) forwards}
@keyframes rowLeave{
  from{opacity:1;min-height:50px;padding-top:8px;padding-bottom:8px}
  to{opacity:0;min-height:0;height:0;padding-top:0;padding-bottom:0;transform:translateY(-6px)}
}

/* The see-all row reads as a glyph, not a link: its label is the accessible
   name only, which is why there is no visible text here either. */
.see-all{min-height:44px;display:grid;place-items:center;color:var(--app-line-strong)}
.see-all .dots{width:20px;height:20px}

/* ── The capture surface ─────────────────────────────────────────
   One object in two shapes. Resting, the clip shows exactly the bar and it
   floats as a pill; growing the clip reveals the timeline and header above the
   same bar, so it is "the pill expanded", never a second sheet. */
.surface{
  position:absolute;left:var(--s-l);right:var(--s-l);bottom:var(--s-xl);
  height:56px;overflow:hidden;
  border-radius:28px;background:var(--app-bg);
  box-shadow:0 1px 2px #141d261f,0 10px 24px -6px #141d2626;
  transition:height .44s var(--ease),left .44s var(--ease),right .44s var(--ease),
             bottom .44s var(--ease),border-radius .44s var(--ease),box-shadow .44s var(--ease);
}
.surface::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  border:1px solid var(--app-line);opacity:1;transition:opacity .3s var(--ease);
}
[data-state="listening"] .surface,
[data-state="parsing"] .surface,
[data-state="parsed"] .surface{
  left:0;right:0;bottom:0;height:392px;
  border-radius:var(--r-xl) var(--r-xl) 0 0;
  box-shadow:0 -18px 44px #141d262e;
}
[data-state="listening"] .surface::after,
[data-state="parsing"] .surface::after,
[data-state="parsed"] .surface::after{opacity:0}
.surface-inner{position:absolute;left:0;right:0;bottom:0;height:392px;display:flex;flex-direction:column}

.sheet-head{display:flex;align-items:center;justify-content:space-between;padding:var(--s-m14) var(--s-l) var(--s-s);color:var(--app-ink)}
.sheet-head b{font-size:15px;font-weight:600}
.sheet-head .ic{width:18px;height:18px;color:var(--app-muted)}

/* Bottom-anchored and clipped, so older turns run off the top the way a
   scrolled timeline does rather than compressing. */
.timeline{flex:1;min-height:0;overflow:hidden;padding:0 var(--s-s) var(--s-s);display:flex;flex-direction:column;justify-content:flex-end;gap:var(--s-s)}
.marker{display:flex;align-items:center;gap:var(--s-s);padding:0 var(--s-s)}
.marker i{flex:1;height:1px;background:var(--app-line)}
.marker span{font-size:9.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:var(--app-muted)}

/* TurnBlock: a tinted wrapper whose radius is the card's plus the padding, so
   the two curves stay parallel. */
.turn{background:#F2F4F6;border-radius:calc(var(--r-md) + var(--s-xs));padding:var(--s-xs);margin:0 var(--s-s)}
.turn-src{display:flex;align-items:flex-start;gap:7px;padding:5px 8px 7px;color:var(--app-ink)}
.turn-src .ic{width:15px;height:15px;flex:0 0 auto;margin-top:1px;color:var(--app-muted)}
.turn-src span{font-size:12.5px;line-height:1.4;font-weight:500}
.turn-card{
  background:var(--app-surface);border-radius:var(--r-md);overflow:hidden;
  display:grid;grid-template-rows:0fr;opacity:0;
  transition:grid-template-rows .4s var(--ease),opacity .3s var(--ease);
}
.turn-card > *{overflow:hidden;min-height:0}
[data-state="parsed"] .turn-card{grid-template-rows:1fr;opacity:1}
/* A turn already in history is open; only the live one grows. */
.turn-card.landed{grid-template-rows:1fr;opacity:1;transition:none}
.turn-card .tx + .tx{border-top:1px solid var(--app-line)}
/* Inherits its ink from whatever ground it sits on. Pinned to the app's near
   black, it disappeared the moment the same caret was reused on the night
   page, where the field behind it is dark. */
.caret{display:inline-block;width:2px;height:1em;background:currentColor;vertical-align:-2px;margin-left:2px;animation:blink 1.05s steps(1) infinite}
@keyframes blink{0%,49%{opacity:1}50%,100%{opacity:0}}
[data-state="parsed"] .caret,[data-state="saving"] .caret{display:none}

/* CaptureBar: the voice field, then the text, manual and scan registers,
   parted by hairlines. */
.bar{display:flex;align-items:center;gap:0;padding:var(--s-xs);height:56px}
.bar-field{
  flex:1;min-width:0;height:42px;display:flex;align-items:center;gap:var(--s-s);
  padding:0 var(--s-m);border-radius:var(--r-full);background:transparent;
  transition:background .3s var(--ease);
}
.bar-field .ic{width:17px;height:17px;flex:0 0 auto;color:var(--app-line-strong)}
.bar-hint{font-size:13px;color:#A6B0BA;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.trace{display:none;width:100%;height:34px}
/* Listening hands the whole field to the trace: the words live in the turn
   above, so a label beside it would only repeat them. */
[data-state="listening"] .bar-field,
[data-state="parsing"] .bar-field{background:var(--mint-bg-app)}
[data-state="listening"] .bar-hint,
[data-state="parsing"] .bar-hint,
[data-state="listening"] .bar-field > .ic,
[data-state="parsing"] .bar-field > .ic{display:none}
[data-state="listening"] .trace,
[data-state="parsing"] .trace{display:block}
.bar-rule{width:1px;height:20px;margin:0 var(--s-xs);background:var(--app-line);flex:0 0 auto}
.bar-btn{width:40px;height:42px;display:grid;place-items:center;color:var(--app-muted);flex:0 0 auto}
.bar-btn .ic{width:18px;height:18px}

/* ── Proof strip ─────────────────────────────────────────────── */
.proof{padding:clamp(36px,5vw,64px) 0}
.proof-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--s-l)}
.proof-item{padding:var(--s-xl);display:grid;gap:6px;align-content:start;transition:transform .3s var(--ease)}
.proof-item:hover{transform:translateY(-3px)}
.proof-item .ic{width:22px;height:22px;color:var(--mint-ink);margin-bottom:var(--s-m)}
.proof-item b{font-size:15px;font-weight:700;color:var(--night-ink)}
.proof-item span{color:var(--muted);font-size:14px;line-height:1.5}
.proof-note{margin-top:var(--s-xxl);color:var(--muted);font-size:13.5px;max-width:72ch}

/* A divider that fades out at both ends, so the dark ground gets structure
   without four hard rules down the page. */
.ruled{position:relative}
.ruled::before{
  content:"";position:absolute;top:0;left:50%;translate:-50% 0;
  width:min(var(--wrap),100%);height:1px;
  background:linear-gradient(90deg,transparent,var(--line-strong) 22%,var(--line-strong) 78%,transparent);
}

/* ── How it works ────────────────────────────────────────────── */
.how{padding:clamp(52px,6.5vw,88px) 0}
.steps{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--s-l);margin-top:var(--s-3xl)}
.step{padding:var(--s-xl);display:grid;gap:var(--s-s);align-content:start;transition:transform .3s var(--ease)}
.step:hover{transform:translateY(-4px)}
.step-art{
  height:96px;display:grid;place-items:center;position:relative;overflow:hidden;
  background:radial-gradient(120% 130% at 50% 120%,#B3A6F01F,transparent 66%),#E2E6EE08;
  border:1px solid var(--line);border-radius:var(--r-md);margin-bottom:var(--s-m);
}
.step-n{font-family:var(--font-num);font-size:11px;font-weight:600;letter-spacing:.16em;color:var(--night-accent)}
.step b{font-size:17.5px;font-weight:700;letter-spacing:-.02em;color:var(--night-ink)}
.step p{color:var(--muted);font-size:14.5px}

/* AuroraOrb (lib/shared/widgets/aurora_orb.dart), which is what the app's
   voice control actually looks like: a green→violet base with bright bands
   tiding across it, round rather than a squircle, and no ribbon silhouette to
   fight the circle. The flat green disc this replaces was the old button. */
.mic-orb{
  position:relative;z-index:1;width:52px;height:52px;
  border-radius:var(--r-full);overflow:hidden;isolation:isolate;
  display:grid;place-items:center;
  background:linear-gradient(90deg,#17784F,#2E7A72 34%,#63799C 68%,#9B92D6);
  box-shadow:0 0 28px -6px #1FA567,0 0 34px -8px #B3A6F0;
}
/* Screened, so a band lightens the base it crosses instead of covering it,
   and masked vertically so it stays a band rather than a fill. Carried at
   part alpha: at full strength two screened bands bleached the base and the
   green→violet journey, which is the whole point, stopped reading. */
.mic-orb .tide{
  position:absolute;left:-30%;right:-30%;height:44%;
  mix-blend-mode:screen;pointer-events:none;
  background:linear-gradient(90deg,#8FD2B300,#8FD2B3a8 20%,#ADCED18f 52%,#D5CEF7b8 84%,#D5CEF700);
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 42%,#000 58%,transparent);
          mask-image:linear-gradient(180deg,transparent,#000 42%,#000 58%,transparent);
  animation:tide 4.6s ease-in-out infinite alternate;
}
.mic-orb .tide-2{height:26%;opacity:.5;animation-duration:6.2s;animation-delay:-2.4s}
@keyframes tide{
  from{transform:translateY(34%) rotate(-5deg)}
  to{transform:translateY(-30%) rotate(5deg)}
}
.mic-orb .ic{
  position:relative;width:18px;height:18px;color:#fff;
  filter:drop-shadow(0 1px 3px #03181259);
}
.mic-halo{position:absolute;width:52px;height:52px;border-radius:var(--r-full);background:linear-gradient(100deg,var(--brand),var(--night-accent));opacity:.24;animation:halo 2.6s ease-out infinite}
@keyframes halo{0%{transform:scale(1);opacity:.3}70%,100%{transform:scale(2.3);opacity:0}}

.chip-row{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;padding:0 var(--s-m)}
.chip{background:#E2E6EE0F;border:1px solid var(--line-strong);border-radius:var(--r-full);padding:6px 11px;font-size:12px;font-weight:600;color:var(--night-ink);opacity:0;animation:chipIn .5s var(--ease) forwards}
.chip.c1{animation-delay:.1s}.chip.c2{animation-delay:.45s}.chip.c3{animation-delay:.8s}
@keyframes chipIn{from{opacity:0;transform:translateY(6px) scale(.94)}to{opacity:1;transform:none}}

.save-row{display:flex;align-items:center;gap:var(--s-m)}
.save-bar{width:88px;height:8px;border-radius:var(--r-full);background:var(--line-strong);overflow:hidden;position:relative}
.save-bar::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,var(--brand),var(--night-accent));transform-origin:left;animation:fill 2.8s var(--ease) infinite}
@keyframes fill{0%{transform:scaleX(0)}55%,100%{transform:scaleX(1)}}
/* The tick is the bar's result, so it has to happen *at* the bar's 55%: it
   waits dim, then draws and pops as the fill lands. */
.save-tick{
  width:32px;height:32px;border-radius:var(--r-full);
  display:grid;place-items:center;
  background:#1FA5672E;color:var(--mint-ink);
  animation:tickLand 2.8s var(--ease) infinite;
}
.save-tick .ic{width:16px;height:16px;stroke-width:2.6;stroke-dasharray:22;animation:tickDraw 2.8s var(--ease) infinite}
@keyframes tickLand{
  0%,50%{transform:scale(1);background:#1FA5671a;color:#6FCB9A5c;box-shadow:0 0 0 0 #1FA56700}
  64%{transform:scale(1.16);background:#1FA5675c;color:#8FD2B3;box-shadow:0 0 0 5px #1FA5671f,0 0 18px 2px #1FA56773}
  76%,100%{transform:scale(1);background:#1FA5672E;color:var(--mint-ink);box-shadow:0 0 0 0 #1FA56700}
}
@keyframes tickDraw{0%,52%{stroke-dashoffset:22}66%,100%{stroke-dashoffset:0}}

.aside{
  margin-top:var(--s-l);display:grid;grid-template-columns:248px 1fr;gap:var(--s-xxl);align-items:center;
  padding:var(--s-xxl);
}
.aside b{font-size:17.5px;font-weight:700;display:block;margin-bottom:6px;color:var(--night-ink)}
.aside p{color:var(--muted);font-size:14.5px;max-width:66ch}
.aside-art{display:grid;place-items:center}
/* The capture pill typing, and a pill does not grow a character at a time:
   the field holds one width and the sentence runs inside it. Animating the
   field's own width was the tell that this was a caption, not a control. */
.key-line{
  display:inline-flex;align-items:center;gap:var(--s-s);
  width:248px;background:#E2E6EE0F;
  border:1px solid var(--line-strong);border-radius:var(--r-full);
  padding:11px var(--s-m14);font-size:13.5px;font-weight:600;color:var(--night-ink);
}
.key-line .ic{width:15px;height:15px;flex:0 0 auto;color:var(--muted)}
.key-window{flex:1;min-width:0;display:flex;align-items:center;overflow:hidden;white-space:nowrap}
.key-txt{overflow:hidden;max-width:0;animation:typeIn 6.4s steps(26) infinite}
@keyframes typeIn{0%{max-width:0}45%,88%{max-width:26ch}100%{max-width:0}}

/* ── Benefits ────────────────────────────────────────────────── */
.benefits{padding:clamp(52px,6.5vw,88px) 0}
.ben-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--s-l);margin-top:var(--s-3xl)}
.ben{padding:var(--s-xxl);transition:transform .3s var(--ease)}
.ben:hover{transform:translateY(-3px)}
.ben b{display:block;font-size:19.5px;font-weight:700;letter-spacing:-.022em;margin-bottom:var(--s-m);color:var(--night-ink)}
.ben p{color:var(--muted);font-size:15px}

/* ── Privacy ─────────────────────────────────────────────────── */
.privacy{padding:clamp(52px,6.5vw,88px) 0}
.privacy-wrap{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);gap:clamp(32px,5vw,72px);align-items:start}
.privacy-list{display:grid;gap:var(--s-m)}
.privacy-list li{padding:var(--s-l) var(--s-xl) var(--s-l) var(--s-3xl);display:grid;gap:4px}
/* On the heading rather than the row, because the row's own ::after is the
   glass hover ring. Still positioned against the row: `b` sets no containing
   block of its own. */
.privacy-list b::before{
  content:"";position:absolute;left:var(--s-xl);top:23px;
  width:8px;height:8px;border-radius:var(--r-full);
  background:var(--mint-ink);box-shadow:0 0 12px 1px #6FCB9A99;
}
.privacy-list b{font-size:15px;font-weight:700;color:var(--night-ink)}
.privacy-list span{color:var(--muted);font-size:14px}

/* ── FAQ ─────────────────────────────────────────────────────── */
.faq{padding:clamp(52px,6.5vw,88px) 0}
.faq-list{margin-top:var(--s-3xl);border-top:1px solid var(--line)}
.qa{border-bottom:1px solid var(--line)}
.qa h3{margin:0;font-size:inherit;font-weight:inherit;letter-spacing:normal}
.q{
  width:100%;display:flex;align-items:center;gap:var(--s-l);
  background:none;border:0;padding:var(--s-xl) 0;
  font:inherit;font-size:17px;font-weight:700;letter-spacing:-.018em;color:var(--night-ink);
  text-align:left;cursor:pointer;transition:color .16s var(--ease);
}
.q::after{
  content:"";width:11px;height:11px;flex:0 0 auto;margin-left:auto;
  border-right:2px solid var(--muted);border-bottom:2px solid var(--muted);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform .28s var(--ease);
}
.q[aria-expanded="true"]::after{transform:rotate(225deg) translate(-2px,-2px)}
.q:hover{color:var(--night-accent)}
.a{display:grid;grid-template-rows:0fr;transition:grid-template-rows .34s var(--ease)}
.a > div{overflow:hidden;min-height:0}
.a p{color:var(--muted);font-size:15px;padding-bottom:var(--s-xxl);max-width:70ch}
.qa.open .a{grid-template-rows:1fr}

/* ── Closing CTA ─────────────────────────────────────────────── */
.close{position:relative;padding:clamp(60px,7.5vw,100px) 0 clamp(64px,8vw,110px);text-align:center}
.close::before{
  content:"";position:absolute;left:50%;top:34%;translate:-50% -50%;
  width:min(760px,92vw);aspect-ratio:2/1;pointer-events:none;
  background:radial-gradient(closest-side,#B3A6F029,transparent 72%);
}
.close .btn{margin-top:var(--s-3xl)}
.stores{display:flex;gap:var(--s-m);justify-content:center;flex-wrap:wrap;margin-top:var(--s-3xl)}
.store{
  display:inline-flex;align-items:center;gap:var(--s-s);
  border:1px dashed var(--line-strong);border-radius:var(--r-full);
  padding:10px var(--s-l);color:var(--muted);font-size:13px;font-weight:600;
}
.store .ic{width:17px;height:17px}

/* ── Footer ──────────────────────────────────────────────────── */
.foot{border-top:1px solid var(--line);padding:var(--s-4xl) 0 var(--s-xxl);background:#1A1D23}
.foot-inner{display:flex;flex-wrap:wrap;gap:var(--s-xxl);justify-content:space-between;align-items:flex-start}
.foot-tag{color:var(--muted);font-size:14px;margin-top:var(--s-s);max-width:34ch}
.foot-links{display:flex;flex-wrap:wrap;gap:0 var(--s-xl);font-size:14px;font-weight:600;color:var(--muted)}
.foot-links a{text-decoration:none;padding:11px 0;transition:color .16s var(--ease)}
.foot-links a:hover{color:var(--night-ink)}
.foot-legal{margin-top:var(--s-3xl);padding-top:var(--s-l);border-top:1px solid var(--line);color:var(--muted)}

/* ── 404 ─────────────────────────────────────────────────────── */
/* One viewport, centred, no nav and no footer: the only thing this page owes
   its visitor is a way out, and every extra link is one more thing to read
   before finding it. */
.gone{min-height:100svh;display:flex;align-items:center;padding:var(--s-4xl) 0}
.gone-code{
  font-family:var(--font-num);font-size:clamp(58px,11vw,104px);font-weight:600;
  line-height:1;letter-spacing:-.04em;margin:var(--s-xxl) 0 var(--s-l);
  background:linear-gradient(102deg,var(--night-accent) 8%,var(--mint-ink) 96%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.gone h1{font-size:clamp(24px,3.4vw,36px);color:var(--night-ink)}
/* Its own line at every width. Two sentences in two languages wrapping into
   each other is the one way this page could be harder to read than the URL. */
.gone-vi{display:block;color:var(--muted);font-weight:700}
.gone-cta{margin-top:var(--s-3xl)}

/* ── Scroll reveal ───────────────────────────────────────────── */
.reveal{opacity:0;transform:translateY(20px);transition:opacity .65s var(--ease),transform .65s var(--ease)}
.reveal.in{opacity:1;transform:none}
.proof-item.in,.step.in,.ben.in,.privacy-list li.in,.qa.in{transition-delay:var(--rd,0ms)}

/* ── Responsive ──────────────────────────────────────────────── */
@media (max-width:1040px){
  /* Stacked, the copy and the phone cannot share one screen and pretending
     otherwise would only squeeze the phone to nothing. */
  .hero{min-height:0;padding:clamp(24px,5vw,56px) 0 clamp(40px,7vw,80px)}
  .hero-inner{grid-template-columns:1fr}
  .phone{width:min(346px,84vw);transform:none}
  .stage:hover .phone{transform:none}
  .coach-float{left:auto;right:0;bottom:4%}
}
@media (max-width:960px){
  .proof-grid{grid-template-columns:repeat(2,1fr)}
  .steps{grid-template-columns:1fr}
  .ben-grid{grid-template-columns:1fr}
  .privacy-wrap{grid-template-columns:1fr}
  .aside{grid-template-columns:1fr}
  .aside-art{justify-items:start}
  .nav-links{display:none}
  /* With the links gone the button is the only auto-margin left, so it has to
     claim the gap the links used to. */
  .nav-inner .btn-sm{margin-left:auto}
}
@media (max-width:600px){
  .wrap,.nav-inner,.hero-inner{padding-left:var(--s-l);padding-right:var(--s-l)}
  .proof-grid{grid-template-columns:1fr}
  .btn-lg{width:100%;justify-content:center}
  .foot-inner{flex-direction:column}
  .coach-float{position:static;width:100%;margin-top:var(--s-l);opacity:1;transform:none}
  .sky-grid{background-size:44px 44px}
}

/* ── Reduced motion: restraint is the app's identity, so honour it fully.
   The orb paints one frame and the demo jumps to its finished state. ── */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition-duration:.01ms!important}
  .reveal{opacity:1;transform:none}
  .chip{opacity:1}
  .key-txt{max-width:none}
  .save-bar::after{transform:scaleX(1)}
  /* Every animated path lands on its finished state, never a blank one: an
     undrawn tick would read as a bug, not as restraint. */
  .save-tick .ic{stroke-dashoffset:0}
  /* The collapse is an animation, so with animations off the pushed-out row
     would sit there at full height and the list would show three. */
  .tx.leaving{display:none}
  .coach-float{opacity:1;transform:none}
}
