/* ==========================================================================
   BonjourPro — public landing page
   Dark, cinematic, tricolore. Distinct from the app's light interior, which
   is deliberate: marketing sells, the product works. Every effect degrades to
   a readable static page — content never depends on script or motion.
   ========================================================================== */

:root {
  --ink: #0b1533;
  --paper: #f5f7fd;
  --muted: #5a6178;

  --bleu: #10428f;
  --blue: #3a63c4;
  --blue-lift: #6d8fea;
  --rouge: #ce2b37;
  --rouge-deep: #a3202a;
  --gold: #d9a63c;
  --green: #1f7a55;
  --violet: #7a5ce2;

  --night: #060a18;
  --night-2: #0b1533;
  --night-3: #12224d;

  --line: rgba(255, 255, 255, 0.1);
  --line-strong: rgba(255, 255, 255, 0.18);
  --dim: rgba(255, 255, 255, 0.62);
  /* 0.44 measured 4.36:1 against --night — just under AA, and it carries real
     copy (prices, plan notes, footer links), not decoration. 0.56 reads as the
     same quiet grey and clears AA at 6.4:1. */
  --dimmer: rgba(255, 255, 255, 0.56);

  --shell: 1140px;
  --radius: 22px;

  --font: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --ease: cubic-bezier(0.22, 1, 0.36, 1);
}

*, *::before, *::after { box-sizing: border-box; }

html {
  scroll-behavior: smooth;
  scroll-padding-top: 92px;
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--night);
  color: #fff;
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.6;
  overflow-x: hidden;
  /* Fine grain over the whole page. Stops large dark gradients from banding
     and reads as film rather than flat CSS. */
  position: relative;
}

body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9999;
  pointer-events: none;
  opacity: 0.035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

img { max-width: 100%; height: auto; display: block; }
svg { fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }

.shell { width: min(100% - 40px); max-width: var(--shell); margin-inline: auto; }
.shell.narrow { max-width: 760px; }

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

.skip {
  position: absolute; top: -60px; left: 16px; z-index: 200;
  padding: 10px 16px; border-radius: 0 0 12px 12px;
  background: #fff; color: var(--ink); font-weight: 700; text-decoration: none;
  transition: top 180ms var(--ease);
}
.skip:focus { top: 0; }

:focus-visible { outline: 2px solid var(--blue-lift); outline-offset: 3px; border-radius: 6px; }

/* ── Reading progress ─────────────────────────────────────────────────── */
.progress-rail {
  position: fixed; inset: 0 0 auto; height: 2px; z-index: 120;
  background: transparent; pointer-events: none;
}
.progress-rail i {
  display: block; height: 100%; width: 0%;
  background: linear-gradient(90deg, var(--bleu), var(--blue-lift) 45%, var(--rouge));
  box-shadow: 0 0 14px rgba(109, 143, 234, 0.6);
}

/* ── Cursor light ─────────────────────────────────────────────────────── */
.cursor-light {
  position: fixed; z-index: 0; pointer-events: none;
  width: 620px; height: 620px; border-radius: 50%;
  left: 0; top: 0; margin: -310px 0 0 -310px;
  background: radial-gradient(circle, rgba(109, 143, 234, 0.16), transparent 62%);
  opacity: 0; transition: opacity 500ms ease;
  will-change: transform;
}
body.pointer-fine .cursor-light { opacity: 1; }

/* ── Buttons ──────────────────────────────────────────────────────────── */
.btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  gap: 9px; padding: 13px 22px; border: 0; border-radius: 14px;
  font: inherit; font-size: 14.5px; font-weight: 680; letter-spacing: -0.01em;
  text-decoration: none; cursor: pointer; overflow: hidden;
  transition: transform 200ms var(--ease), box-shadow 200ms var(--ease), background 200ms ease;
}
.btn svg { width: 17px; height: 17px; }
.btn > * { position: relative; z-index: 1; }
.btn-sm { padding: 10px 17px; font-size: 13.5px; }
.btn-lg { padding: 16px 30px; font-size: 16px; border-radius: 16px; }

.btn-primary {
  color: #fff;
  background: linear-gradient(135deg, #e0404c, var(--rouge) 48%, var(--rouge-deep));
  box-shadow: 0 10px 30px rgba(163, 32, 42, 0.42), inset 0 1px 0 rgba(255,255,255,0.28);
}
/* Sheen sweeps across on hover — the "wet" highlight that makes a button
   read as a physical object. */
.btn-primary::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(100deg, transparent 30%, rgba(255,255,255,0.34) 50%, transparent 70%);
  transform: translateX(-120%);
  transition: transform 700ms var(--ease);
}
.btn-primary:hover::after { transform: translateX(120%); }
.btn-primary:hover { box-shadow: 0 16px 40px rgba(163, 32, 42, 0.55); }

.btn-ghost {
  color: #fff; background: rgba(255,255,255,0.07);
  border: 1px solid var(--line-strong);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.btn-ghost:hover { background: rgba(255,255,255,0.14); border-color: rgba(255,255,255,0.34); }

/* ── Nav ──────────────────────────────────────────────────────────────── */
.nav {
  position: fixed; inset: 0 0 auto; z-index: 110;
  padding: 16px 0;
  transition: padding 300ms var(--ease), background 300ms ease, border-color 300ms ease, backdrop-filter 300ms ease;
  border-bottom: 1px solid transparent;
}
.nav.stuck {
  padding: 9px 0;
  background: rgba(6, 10, 24, 0.76);
  border-bottom-color: var(--line);
  backdrop-filter: blur(18px) saturate(140%);
  -webkit-backdrop-filter: blur(18px) saturate(140%);
}
.nav-inner { display: flex; align-items: center; gap: 20px; }

.brand {
  display: inline-flex; align-items: center; gap: 9px;
  color: #fff; text-decoration: none; font-size: 18px; font-weight: 560; letter-spacing: -0.02em;
}
.brand img { border-radius: 9px; }
.brand strong { font-weight: 800; }

.nav-links { display: flex; gap: 4px; margin-left: auto; }
.nav-links a {
  position: relative; padding: 8px 13px; border-radius: 10px;
  color: var(--dim); text-decoration: none; font-size: 14px; font-weight: 560;
  transition: color 180ms ease, background 180ms ease;
}
.nav-links a:hover { color: #fff; background: rgba(255,255,255,0.07); }

.nav-signin {
  position: relative; padding: 8px 2px; margin-right: 4px;
  color: var(--dim); text-decoration: none;
  font-size: 14px; font-weight: 600; letter-spacing: -0.01em;
  transition: color 180ms ease;
}
.nav-signin::after {
  content: ''; position: absolute; left: 2px; right: 2px; bottom: 4px;
  height: 1.5px; border-radius: 2px; background: var(--rouge);
  transform: scaleX(0); transform-origin: left; transition: transform 260ms var(--ease);
}
.nav-signin:hover { color: #fff; }
.nav-signin:hover::after { transform: scaleX(1); }

.nav-toggle {
  display: none; width: 42px; height: 42px; padding: 0; margin-left: auto;
  border: 1px solid var(--line-strong); border-radius: 12px;
  background: rgba(255,255,255,0.06); cursor: pointer;
  flex-direction: column; align-items: center; justify-content: center; gap: 5px;
}
.nav-toggle span { display: block; width: 17px; height: 1.8px; border-radius: 2px; background: #fff; transition: transform 260ms var(--ease), opacity 200ms ease; }
.nav-toggle[aria-expanded="true"] span:first-child { transform: translateY(3.4px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:last-child { transform: translateY(-3.4px) rotate(-45deg); }

/* `display: grid` is an author rule and outranks the browser's own
   [hidden] { display: none }, so without this the drawer rendered open at
   every width — stacked under the desktop nav on first load. Anything given a
   display value here must restate hidden. */
.nav-drawer[hidden] { display: none; }

.nav-drawer {
  display: grid; gap: 4px; padding: 14px 20px 20px;
  background: rgba(6, 10, 24, 0.96);
  border-bottom: 1px solid var(--line);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  animation: drawer-in 260ms var(--ease) both;
}
@keyframes drawer-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
.nav-drawer a:not(.btn) {
  padding: 12px 4px; color: var(--dim); text-decoration: none;
  font-size: 15px; font-weight: 560; border-bottom: 1px solid var(--line);
}
.nav-drawer .btn { margin-top: 12px; }

/* ── Hero ─────────────────────────────────────────────────────────────── */
.hero { position: relative; padding: 168px 0 0; isolation: isolate; overflow: hidden; }

.hero-field { position: absolute; inset: -10% -10% 0; z-index: -1; pointer-events: none; }
.hero-field .orb { position: absolute; display: block; border-radius: 50%; filter: blur(70px); }
.orb-1 { width: 62%; height: 60%; top: -14%; left: -12%; background: radial-gradient(circle, rgba(58,99,196,0.62), transparent 66%); animation: float-a 19s ease-in-out infinite; }
.orb-2 { width: 54%; height: 56%; top: 6%; right: -14%; background: radial-gradient(circle, rgba(206,43,55,0.42), transparent 64%); animation: float-b 23s ease-in-out infinite; }
.orb-3 { width: 66%; height: 58%; bottom: -22%; left: 14%; background: radial-gradient(circle, rgba(122,92,226,0.44), transparent 68%); animation: float-c 27s ease-in-out infinite; }

@keyframes float-a { 0%,100% { transform: translate3d(0,0,0) scale(1); } 50% { transform: translate3d(9%,8%,0) scale(1.16); } }
@keyframes float-b { 0%,100% { transform: translate3d(0,0,0) scale(1.08); } 50% { transform: translate3d(-11%,7%,0) scale(0.92); } }
@keyframes float-c { 0%,100% { transform: translate3d(0,0,0) scale(1); } 50% { transform: translate3d(8%,-10%,0) scale(1.14); } }

.grid-veil {
  position: absolute; inset: 0;
  background-image:
    linear-gradient(rgba(255,255,255,0.05) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,0.05) 1px, transparent 1px);
  background-size: 58px 58px;
  mask-image: radial-gradient(90% 65% at 50% 34%, #000 38%, transparent 76%);
  -webkit-mask-image: radial-gradient(90% 65% at 50% 34%, #000 38%, transparent 76%);
}

.hero-inner { position: relative; text-align: center; }

.eyebrow {
  display: inline-flex; align-items: center; gap: 9px;
  margin: 0 0 22px; padding: 8px 15px 8px 12px;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  color: var(--dim); font-size: 12.5px; font-weight: 640; letter-spacing: 0.02em;
}
.pip { width: 7px; height: 7px; border-radius: 50%; background: #4ade9b; box-shadow: 0 0 0 3px rgba(74,222,155,0.2); animation: pulse 2.4s ease-in-out infinite; }
@keyframes pulse { 0%,100% { box-shadow: 0 0 0 3px rgba(74,222,155,0.2); } 50% { box-shadow: 0 0 0 6px rgba(74,222,155,0.06); } }

.hero-title {
  margin: 0 0 22px;
  font-size: clamp(38px, 7.4vw, 82px);
  font-weight: 720; line-height: 1.02; letter-spacing: -0.038em;
}
.hero-title .line { display: block; }
.hero-title .grad {
  background: linear-gradient(102deg, #fff 6%, #b8cdff 40%, #ffb3ad 78%, #fff);
  background-size: 220% auto;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  /* Opaque floor so a glyph the gradient misses still paints. */
  background-color: #dfe8ff;
  animation: sheen 9s linear infinite;
}
@keyframes sheen { to { background-position: 220% center; } }
.nowrap { white-space: nowrap; }

.hero-lead {
  max-width: 640px; margin: 0 auto 32px;
  color: var(--dim); font-size: clamp(15.5px, 1.9vw, 19px); line-height: 1.62;
}
.hero-lead em { color: #cfdcff; font-style: italic; }

.hero-cta { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; margin-bottom: 16px; }
.hero-note { margin: 0 0 60px; color: var(--dimmer); font-size: 13px; }

.hero-card {
  position: relative; display: grid; grid-template-columns: auto minmax(0,1fr);
  align-items: center; gap: 20px;
  width: min(100%, 620px); margin: 0 auto; padding: 20px;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: linear-gradient(150deg, rgba(255,255,255,0.11), rgba(255,255,255,0.04));
  backdrop-filter: blur(22px) saturate(140%); -webkit-backdrop-filter: blur(22px) saturate(140%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.24), 0 30px 70px rgba(0,0,0,0.5);
  text-align: left;
  transform-style: preserve-3d;
}
.hero-card-glow {
  position: absolute; inset: -1px; border-radius: inherit; pointer-events: none;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%), rgba(255,255,255,0.14), transparent 70%);
  opacity: 0; transition: opacity 300ms ease;
}
.hero-card:hover .hero-card-glow { opacity: 1; }

/* ── Camille, popping out of the card ─────────────────────────────────────
   The stage is a fixed-size cell in the card's grid. The portrait is taller
   than the stage and anchored to its bottom, so the overshoot happens at the
   top — her head clears the card's edge while her shoulders stay inside it.
   The card must not clip, and she must paint above it. */
.hero-card { overflow: visible; }

.tutor-stage {
  position: relative;
  width: 132px; height: 116px;
  align-self: end;
}
.hero-card .tutor {
  position: absolute; left: 50%; bottom: -20px;
  width: 168px; height: auto; max-width: none;
  transform: translateX(-50%);
  z-index: 2;
  /* A real drop shadow follows the alpha channel, so it traces her outline
     instead of boxing her — the whole point of cutting her out. */
  filter:
    drop-shadow(0 18px 22px rgba(0, 0, 0, 0.55))
    drop-shadow(0 2px 3px rgba(0, 0, 0, 0.4));
  transform-origin: 50% 100%;
}

/* Behind her: a warm glow so her silhouette separates from the dark page. */
.tutor-halo {
  position: absolute; z-index: 0;
  left: 50%; bottom: 4px; width: 190px; height: 190px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(circle, rgba(109, 143, 234, 0.42), rgba(109, 143, 234, 0.12) 45%, transparent 68%);
  filter: blur(8px);
}

/* Beneath her: a soft ellipse where she meets the card. Without a contact
   shadow a cutout floats rather than stands. */
.tutor-shadow {
  position: absolute; z-index: 1;
  left: 50%; bottom: -12px; width: 120px; height: 20px;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(ellipse, rgba(0, 0, 0, 0.6), transparent 70%);
  filter: blur(6px);
}

/* She drifts a little, and the shadow tightens as she rises — the shadow
   reacting is what reads as depth rather than a sticker sliding around. */
/* The idle float, the contact-shadow breath and the hover lift all live in
   the "HERO — ALIVE" block at the end of this file. They were rewritten when
   Camille gained a body wrapper: the old rules here centred with
   translateX(-50%), which the wrapper now does, and the old shadow animation
   drove opacity — and a running animation outranks a declaration, so it
   silently cancelled the reaction to her voice. */
.hero-card-copy p { margin: 10px 0 0; color: var(--dim); font-size: 13.5px; line-height: 1.5; }
.bubble {
  display: inline-block; padding: 8px 14px; border-radius: 999px;
  background: rgba(255,255,255,0.12); border: 1px solid var(--line-strong);
  font-size: 13.5px; font-weight: 600;
  transition: opacity 300ms ease, transform 300ms var(--ease);
}
.bubble.swap { animation: bubble-in 420ms var(--ease); }
@keyframes bubble-in { from { opacity: 0.2; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* ── Marquee ──────────────────────────────────────────────────────────── */
.marquee {
  margin-top: 72px; padding: 18px 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.marquee-track { display: flex; align-items: center; gap: 26px; width: max-content; animation: slide 46s linear infinite; }
.marquee-track span { color: rgba(255,255,255,0.5); font-size: 19px; font-weight: 560; white-space: nowrap; }
.marquee-track i { color: rgba(255,255,255,0.22); font-style: normal; }
@keyframes slide { to { transform: translateX(-50%); } }

/* ── Scroll reveal ────────────────────────────────────────────────────── */
/* Content is visible by default; JS adds .reveal-armed only when it can
   observe. A visitor with JS disabled sees the finished page, not a blank one. */
.reveal-armed .reveal { opacity: 0; transform: translateY(22px); }
.reveal-armed .reveal.in {
  opacity: 1; transform: none;
  transition: opacity 760ms var(--ease), transform 760ms var(--ease);
  transition-delay: var(--d, 0ms);
}

/* ── Responsive ───────────────────────────────────────────────────────── */
@media (max-width: 860px) {
  .nav-links { display: none; }
  .nav .btn-sm,
  .nav-signin { display: none; }
  .nav-toggle { display: flex; }
  .hero { padding-top: 128px; }
}

/* Leaving the drawer open and widening the window would otherwise strand it
   below the restored desktop nav. */
@media (min-width: 861px) {
  .nav-drawer { display: none; }
}

@media (max-width: 620px) {
  .shell { width: min(100% - 32px); }
  .band { padding: 68px 0; }
  .hero-card { grid-template-columns: 1fr; text-align: center; justify-items: center; }
  .hero-card .tutor { margin-inline: auto; }
}

/* ── Reduced motion ───────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .reveal-armed .reveal { opacity: 1 !important; transform: none !important; }
  .cursor-light { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   MOTION LAYER
   Everything below is choreography. All of it is transform/opacity only so it
   composites on the GPU, and all of it is switched off wholesale at the end
   of this file for anyone who asks for reduced motion.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Hero curtain ──────────────────────────────────────────────────────────
   The headline rises from behind its own baseline rather than fading in. JS
   wraps each .line in a .line-mask, so the gradient fill on .line is
   untouched. Overflow needs a little vertical slack or descenders clip. */
   These run as animations with `both` fill rather than as a hidden resting
   state that JS later un-hides. The difference matters: a keyframe animation
   carries its own start and end, so the text cannot get stranded invisible if
   the frame callback that was meant to release it never runs — a background
   tab, a stalled main thread, a script error further down the file. The worst
   case here is text that appears without moving. */
.line-mask { display: block; overflow: hidden; padding-bottom: 0.12em; margin-bottom: -0.12em; }
.curtain .line-mask > .line {
  animation: curtain-rise 1180ms cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: var(--curtain-delay, 0ms);
}
@keyframes curtain-rise {
  from { transform: translate3d(0, 108%, 0) rotate(1.6deg); opacity: 0; }
  to   { transform: none; opacity: 1; }
}

/* ── Cinematic entrance for the rest of the hero ──────────────────────────
   Slower and softer than the scroll reveals further down the page, because
   this one plays on arrival and sets the tone for them. */
.hero-stagger {
  animation: hero-in 1000ms cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: var(--d, 0ms);
}
@keyframes hero-in {
  from { opacity: 0; transform: translate3d(0, 26px, 0); filter: blur(6px); }
  to   { opacity: 1; transform: none; filter: none; }
}

/* ── Parallax ──────────────────────────────────────────────────────────────
   JS writes --par (pixels of scroll drift) per layer. Kept off the main
   thread by only ever touching transform. */
   The hero card is deliberately excluded: the tilt handler writes its
   transform as an inline style, which would win over anything set here and
   leave the two effects fighting. */
.orb, .grid-veil { will-change: transform; }
.orb { transform: translate3d(0, var(--par, 0px), 0); }
.grid-veil { transform: translate3d(0, var(--par, 0px), 0); }

/* ── Cursor spotlight inside cards ────────────────────────────────────────
   A soft light that tracks the pointer within each card, so hovering feels
   like moving a lamp over the surface instead of flipping a state. */
.why-card, .feature, .plan, .step, .hero-card { position: relative; isolation: isolate; }
.why-card::before, .feature::before, .plan::before, .step::before {
  content: ''; position: absolute; inset: 0; z-index: -1; border-radius: inherit;
  background: radial-gradient(340px circle at var(--mx, 50%) var(--my, 50%),
              rgba(109, 143, 234, 0.16), transparent 62%);
  opacity: 0; transition: opacity 420ms ease;
  pointer-events: none;
}
.why-card:hover::before, .feature:hover::before, .plan:hover::before, .step:hover::before { opacity: 1; }

/* The button sheen and the sticky-nav condense already exist further up this
   file (.btn-primary::after, .nav.stuck). Not duplicated here. */

/* ── Section headings wipe in ─────────────────────────────────────────────
   A thin rule draws itself under each section kicker as it enters view. */
.kicker { position: relative; }
.kicker::after {
  content: ''; position: absolute; left: 0; bottom: -7px; height: 1.5px; width: 100%;
  background: linear-gradient(90deg, var(--rouge), transparent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 900ms cubic-bezier(0.16, 1, 0.3, 1) 160ms;
}
.reveal.in .kicker::after, .kicker.reveal.in::after { transform: scaleX(1); }

/* ── Reduced motion ───────────────────────────────────────────────────────
   The global block earlier in this file already flattens durations, but it
   cannot rescue anything whose resting state is invisible. These are the
   states that would otherwise leave the page blank rather than still. */
@media (prefers-reduced-motion: reduce) {
  .curtain .line-mask > .line,
  .hero-stagger { animation: none !important; opacity: 1 !important; transform: none !important; filter: none !important; }
  .orb, .grid-veil { transform: none !important; }
  .kicker::after { transform: scaleX(1) !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   CONVERSION LAYER — try-it lesson, pricing choreography, sticky CTA,
   section transitions. Same rules as the motion layer: additive, GPU-only,
   and every hidden resting state has a reduced-motion escape hatch.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Sticky mobile CTA ────────────────────────────────────────────────── */
.sticky-cta {
  position: fixed; left: 12px; right: 12px; bottom: 12px; z-index: 105;
  display: none; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 10px 10px 16px; border-radius: 16px;
  border: 1px solid var(--line-strong);
  background: rgba(6, 10, 24, 0.82); backdrop-filter: blur(18px) saturate(140%); -webkit-backdrop-filter: blur(18px) saturate(140%);
  box-shadow: 0 -8px 40px rgba(0,0,0,0.5), 0 14px 40px rgba(0,0,0,0.5);
  transform: translateY(calc(100% + 24px)); opacity: 0;
  transition: transform 460ms cubic-bezier(0.16, 1, 0.3, 1), opacity 320ms ease;
  padding-bottom: max(10px, env(safe-area-inset-bottom));
}
.sticky-cta.show { transform: none; opacity: 1; }
.sticky-cta-copy { font-size: 13px; color: var(--dim); }
.sticky-cta-copy strong { color: #fff; }
@media (max-width: 860px) { .sticky-cta { display: flex; } }

/* ══════════════════════════════════════════════════════════════════════════
   HEADER — rebuilt
   Taller, quieter, and typographically deliberate. The old bar was 16px of
   padding with five 14px links at equal weight, which is the default every
   template ships. These rules replace the ones earlier in this file.
   ══════════════════════════════════════════════════════════════════════════ */

.nav { padding: 24px 0; }
.nav.stuck { padding: 13px 0; }
.nav-inner { gap: 28px; }

/* Brand ── larger mark, tighter wordmark, and a hairline that separates it
   from the links so the bar reads as two zones instead of one row. */
.brand { gap: 11px; font-size: 20.5px; font-weight: 600; letter-spacing: -0.035em; }
.brand img { width: 40px; height: 40px; border-radius: 11px; transition: transform 420ms var(--ease); }
.nav.stuck .brand img { width: 34px; height: 34px; }
.brand:hover img { transform: rotate(-6deg) scale(1.06); }
.brand strong { font-weight: 820; }

/* Links ── fewer of them, set larger with real tracking, and centred in the
   bar rather than shoved against the buttons. */
.nav-links {
  position: relative;
  display: flex; gap: 2px;
  margin-left: auto; margin-right: auto;
  padding: 5px;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background: rgba(255, 255, 255, 0.03);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.nav-links a {
  position: relative; z-index: 1;
  padding: 9px 18px; border-radius: 999px;
  color: rgba(255, 255, 255, 0.68);
  text-decoration: none;
  font-size: 14.5px; font-weight: 560; letter-spacing: -0.006em;
  transition: color 220ms ease;
  background: none;
}
.nav-links a:hover, .nav-links a.current { color: #fff; background: none; }

/* One pill that slides between items instead of each link painting its own
   background. Position and width are written by JS as --x / --w. */
.nav-indicator {
  position: absolute; top: 5px; bottom: 5px; left: 0; z-index: 0;
  width: var(--w, 0px);
  transform: translateX(var(--x, 0px));
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(255,255,255,0.14), rgba(255,255,255,0.07));
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.18);
  opacity: 0;
  transition: transform 420ms var(--ease), width 420ms var(--ease), opacity 260ms ease;
  pointer-events: none;
}
.nav-links.lit .nav-indicator { opacity: 1; }

.nav-signin { margin-right: 2px; font-size: 14.5px; font-weight: 620; }

/* ── The signature button ──────────────────────────────────────────────────
   The one control on the page that does not reuse the red recipe. A conic
   gradient rotates continuously behind a dark glass face, so the border is a
   travelling light rather than a static stroke; the face keeps the text on a
   solid ground so it stays readable while the light moves. */
.btn-signature {
  position: relative; display: inline-flex; isolation: isolate;
  padding: 2px; border-radius: 999px;
  text-decoration: none;
  transition: transform 260ms var(--ease), filter 260ms ease;
}
.btn-signature .sig-ring {
  position: absolute; inset: 0; border-radius: inherit; z-index: 0;
  background: conic-gradient(from var(--spin, 0deg),
    var(--rouge), #ff8a5c, var(--gold), #6d8fea, var(--bleu), var(--rouge));
  animation: sig-spin 4.5s linear infinite;
}
@property --spin { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
@keyframes sig-spin { to { --spin: 360deg; } }

/* Soft bloom of the same light, thrown outward. */
.btn-signature::before {
  content: ''; position: absolute; inset: -3px; border-radius: inherit; z-index: -1;
  background: inherit;
  background: conic-gradient(from var(--spin, 0deg), var(--rouge), #ff8a5c, var(--gold), #6d8fea, var(--bleu), var(--rouge));
  filter: blur(11px); opacity: 0.55;
  animation: sig-spin 4.5s linear infinite;
  transition: opacity 260ms ease;
}

.btn-signature .sig-face {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 8px;
  padding: 10px 20px; border-radius: inherit;
  background: linear-gradient(180deg, #121a33, #0a0f22);
  color: #fff; font-size: 14.5px; font-weight: 720; letter-spacing: -0.012em;
  overflow: hidden;
}
/* A highlight sweeps across the face on hover. */
.btn-signature .sig-face::after {
  content: ''; position: absolute; top: 0; bottom: 0; left: -50%; width: 40%;
  background: linear-gradient(100deg, transparent, rgba(255,255,255,0.3), transparent);
  transform: skewX(-18deg); opacity: 0;
}
.btn-signature:hover .sig-face::after { animation: sig-sheen 780ms ease-out forwards; }
@keyframes sig-sheen { 0% { left: -50%; opacity: 0; } 15% { opacity: 1; } 100% { left: 120%; opacity: 0; } }

.sig-label { position: relative; }
.sig-arrow { width: 15px; height: 15px; transform: translateX(-2px); opacity: 0; transition: transform 320ms var(--ease), opacity 320ms ease; }
.btn-signature:hover { transform: translateY(-1px); }
.btn-signature:hover .sig-arrow { transform: none; opacity: 1; }
.btn-signature:hover::before { opacity: 0.85; }
.btn-signature:active { transform: translateY(0) scale(0.98); }

/* In the mobile drawer it goes full width. */
.btn-signature.drawer { display: flex; margin-top: 14px; }
.btn-signature.drawer .sig-face { flex: 1; justify-content: center; padding: 14px 20px; font-size: 15.5px; }

/* Drawer typography, matched to the new bar. */
.nav-drawer a:not(.btn-signature) { padding: 14px 4px; font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }

@media (max-width: 860px) {
  /* This block sits after the base .nav-links rule above, which is what makes
     it win. The original mobile hide is earlier in the file and lost the
     cascade the moment the header was restyled — leaving the link pill on
     screen and pushing the bar past the viewport edge. */
  .nav-links { display: none; }
  .nav { padding: 14px 0; }
  .nav.stuck { padding: 10px 0; }
  .nav-inner { gap: 14px; }
  .brand { font-size: 19px; }
  .brand img, .nav.stuck .brand img { width: 36px; height: 36px; }
  /* The signature button is the whole point of the bar on mobile, so it stays
     while the ordinary links collapse into the drawer. */
  .nav .btn-signature { display: inline-flex; }
  .nav .btn-signature .sig-face { padding: 9px 15px; font-size: 13.5px; }
  .nav .btn-signature .sig-arrow { display: none; }
}
@media (max-width: 420px) {
  .nav-signin { display: none; }
  .nav .btn-signature .sig-face { padding: 9px 13px; font-size: 13px; }
}

@media (prefers-reduced-motion: reduce) {
  .btn-signature .sig-ring, .btn-signature::before { animation: none; }
  .btn-signature:hover .sig-face::after { animation: none; }
  .nav-indicator { transition: none; }
  .brand img, .btn-signature { transition: none; }
}

/* ── Footer: parent company link ───────────────────────────────────────── */
.footer-parent {
  color: var(--dim); text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,0.22);
  transition: color 200ms ease, border-color 200ms ease;
}
.footer-parent:hover { color: #fff; border-bottom-color: var(--rouge); }
.footer-parent::after {
  content: '↗'; margin-left: 3px; font-size: 0.85em; opacity: 0.7;
}

/* ══════════════════════════════════════════════════════════════════════════
   BRAND MARK + PRICING — precision pass
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Brand mark ────────────────────────────────────────────────────────────
   The icon's own blue sits close to the bar's navy, so at a glance the mark
   dissolved into the header. It now carries the same travelling-light ring as
   the signature button: a conic gradient behind the icon, revealed as a
   1.5px rim by an inset mask. That reads as a deliberate frame rather than a
   border, and it ties the two ends of the bar together. */
.brand-mark {
  position: relative; display: inline-grid; place-items: center;
  padding: 1.5px; border-radius: 13px;
  isolation: isolate;
}
.brand-mark::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; z-index: 0;
  background: conic-gradient(from var(--spin, 0deg),
    var(--rouge), #ff8a5c, var(--gold), #6d8fea, var(--bleu), var(--rouge));
  animation: sig-spin 4.5s linear infinite;
}
/* A faint bloom so the mark separates from the dark bar even when the ring
   colour momentarily matches the background behind it. */
.brand-mark::after {
  content: ''; position: absolute; inset: -2px; border-radius: inherit; z-index: -1;
  background: conic-gradient(from var(--spin, 0deg),
    var(--rouge), #ff8a5c, var(--gold), #6d8fea, var(--bleu), var(--rouge));
  filter: blur(7px); opacity: 0.5;
  animation: sig-spin 4.5s linear infinite;
  transition: opacity 300ms ease;
}
.brand-mark img {
  position: relative; z-index: 1;
  width: 40px; height: 40px; border-radius: 11.5px;
  display: block;
}
.brand:hover .brand-mark::after { opacity: 0.9; }
.nav.stuck .brand-mark img { width: 34px; height: 34px; border-radius: 10px; }

/* ══════════════════════════════════════════════════════════════════════════
   HERO — ALIVE
   Word-by-word headline, the two hero buttons, and Camille as a presence
   rather than a picture: idle drift, gaze that follows the pointer, a smile
   on approach, and a speaking state driven by her recorded voice.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Headline choreography ─────────────────────────────────────────────────
   JS splits each line into words. Every word rises out of its own clip mask
   with a small rotation and a blur that clears as it lands — a stagger of
   70ms per word, so the sentence is *read* into place left to right rather
   than dropped in as a block. Keyframes with `both` fill: no frame callback
   has to run for the text to appear. */
.hero-title .w {
  display: inline-block; overflow: hidden;
  vertical-align: bottom;
  /* The mask must never touch the glyphs at rest. With line-height 1.02 the
     type's content area overhangs the inline box by ~0.16em above and below,
     so the mask is padded past that (and the padding cancelled with equal
     negative margins, so line spacing is unchanged). Measured, not guessed. */
  padding: 0.2em 0.04em 0.22em; margin: -0.2em -0.04em -0.22em;
}
.hero-title .w > i {
  display: inline-block; font-style: normal;
  animation: word-rise 1120ms cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: var(--wd, 0ms);
  will-change: transform, filter, opacity;
}
@keyframes word-rise {
  0%   { transform: translate3d(0, 128%, 0) rotate(2.4deg) scale(0.98); opacity: 0; filter: blur(7px); }
  60%  { filter: blur(0); }
  100% { transform: none; opacity: 1; filter: blur(0); }
}
/* The gradient stays on the line, not the word: a background clipped to text
   paints through inline children, so one gradient spans the whole line and
   the existing slow sheen keeps running across it. After the words land, a
   brighter highlight passes once every eight seconds — a persistent sign of
   life on an otherwise settled headline. */
.hero-title .grad {
  background-image:
    linear-gradient(102deg, transparent 0%, transparent 42%, rgba(255,255,255,0.85) 50%, transparent 58%, transparent 100%),
    linear-gradient(102deg, #fff 6%, #b8cdff 40%, #ffb3ad 78%, #fff);
  background-size: 260% 100%, 220% auto;
  background-position: 130% 0, 0 0;
  /* The glint must not tile. background-repeat defaults to repeat, and with a
     2.6x-wide image the neighbouring tile re-enters the box for the last ~9%
     of every cycle — a second white band that sweeps halfway across and then
     vanishes at the loop point. The colour layer keeps repeat: at the end of
     its travel the image sits outside the box, so no-repeat there would drop
     the headline to its flat background-color for half of every cycle. */
  background-repeat: no-repeat, repeat;
  /* This line rises as ONE element rather than word by word. Its text clip is
     built from its own paint chunk, and a word wrapped in a promoted layer
     (will-change + filter) cannot contribute glyphs to that mask — the line
     would render invisible. A filter on the clipping element itself is safe;
     only the ancestor/descendant split breaks. */
  overflow: hidden;
  padding: 0.2em 0.04em 0.22em; margin: -0.2em -0.04em -0.22em;
  animation:
    word-rise 1120ms cubic-bezier(0.16, 1, 0.3, 1) 320ms both,
    title-life 9s linear 1.8s infinite;
}
/* One keyframe drives both layers: the soft colour sheen travels the whole
   nine seconds; the bright glint waits until 62% and then crosses in the
   last third. Two animations on background-position would not compose per
   layer — the later one would simply win — so they share a timeline. */
/* The colour layer travels exactly one tile (183.333% of a 220%-wide image),
   so the loop is seamless; travelling 220% covered 1.2 tiles and snapped. Both
   stops move together to keep the rate constant across the two segments. */
@keyframes title-life {
  0%   { background-position: 130% 0,   0% center; }
  62%  { background-position: 130% 0,   113.6667% center; }
  100% { background-position: -130% 0,  183.3333% center; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-title .w > i,
  .hero-title .grad { animation: none !important; transform: none !important; opacity: 1 !important; filter: none !important; }
}

/* ── Hero buttons ──────────────────────────────────────────────────────────
   Primary: the signature ring at hero scale, with a warmer face so it reads
   as the action rather than as chrome. Secondary: real glass, a light that
   runs its border on hover, and a play glyph that says "show me". */
.btn-signature.hero { padding: 2.5px; border-radius: 18px; }
.btn-signature.hero .sig-ring { border-radius: inherit; }
.btn-signature.hero .sig-face {
  gap: 10px; padding: 17px 30px 17px 32px; border-radius: 15.5px;
  background: linear-gradient(180deg, #d63a46 0%, #b6252f 60%, #9a1e28 100%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.32), inset 0 -1px 0 rgba(0,0,0,0.25);
  font-size: 16.5px; font-weight: 740; letter-spacing: -0.014em;
}
.btn-signature.hero .sig-arrow { width: 18px; height: 18px; opacity: 0.9; transform: none; }
.btn-signature.hero:hover .sig-arrow { transform: translateX(4px); }
.btn-signature.hero::before { inset: -5px; filter: blur(16px); opacity: 0.6; }
.btn-signature.hero:hover { transform: translateY(-2px); }
.btn-signature.hero:hover::before { opacity: 0.95; }
/* A slow breath on the bloom at rest, so the button is alive before you reach it. */
@media (prefers-reduced-motion: no-preference) {
  .btn-signature.hero::before { animation: sig-spin 4.5s linear infinite, sig-breathe 3.6s ease-in-out infinite; }
}
@keyframes sig-breathe { 0%, 100% { opacity: 0.5; } 50% { opacity: 0.78; } }

.btn-glass {
  position: relative; display: inline-flex; isolation: isolate;
  padding: 1.5px; border-radius: 18px;
  text-decoration: none; color: #fff;
  transition: transform 260ms var(--ease);
}
/* Border ring: quiet at rest, a travelling light on hover. */
.btn-glass::before {
  content: ''; position: absolute; inset: 0; border-radius: inherit; z-index: 0;
  background: rgba(255,255,255,0.18);
  transition: opacity 260ms ease;
}
.btn-glass::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; z-index: 0;
  background: conic-gradient(from var(--spin, 0deg), transparent 0 60%, rgba(159,192,255,0.9) 78%, #fff 84%, rgba(159,192,255,0.9) 90%, transparent 100%);
  opacity: 0; transition: opacity 320ms ease;
}
.btn-glass:hover::after { opacity: 1; animation: sig-spin 2.6s linear infinite; }
.btn-glass .glass-face {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; gap: 12px;
  padding: 16px 28px 16px 20px; border-radius: 16.5px;
  background: linear-gradient(180deg, rgba(20, 30, 62, 0.82), rgba(10, 16, 36, 0.86));
  backdrop-filter: blur(16px) saturate(140%); -webkit-backdrop-filter: blur(16px) saturate(140%);
  box-shadow: inset 0 1px 0 rgba(255,255,255,0.14);
  font-size: 16px; font-weight: 700; letter-spacing: -0.012em;
}
.btn-glass:hover { transform: translateY(-2px); }
.btn-glass:active { transform: translateY(0) scale(0.98); }
/* Play glyph: a small disc with a triangle; the disc fills on hover. */
.glass-play {
  display: grid; place-items: center; width: 30px; height: 30px; border-radius: 50%;
  border: 1.5px solid rgba(255,255,255,0.35);
  background: rgba(255,255,255,0.06);
  transition: background 260ms ease, border-color 260ms ease, transform 260ms var(--ease);
}
.glass-play i {
  display: block; width: 0; height: 0; margin-left: 2px;
  border-style: solid; border-width: 5.5px 0 5.5px 9px;
  border-color: transparent transparent transparent #fff;
}
.btn-glass:hover .glass-play { background: rgba(255,255,255,0.18); border-color: rgba(255,255,255,0.7); transform: scale(1.06); }

/* ── Camille: presence ────────────────────────────────────────────────────── */
.tutor-stage { perspective: 800px; }

/* The body wrapper takes the position the image used to hold, plus the gaze
   turn (--gx/--gy from the pointer) and the speaking cadence (--amp from her
   voice). No transition: JS eases the variables itself, every frame. */
.tutor-body {
  position: absolute; left: 50%; bottom: -20px; width: 168px;
  z-index: 2;
  transform-origin: 50% 88%;
  transform:
    translateX(-50%)
    rotateY(calc(var(--gx, 0) * -7deg))
    rotateX(calc(var(--gy, 0) * 4deg))
    translateX(calc(var(--gx, 0) * 3px))
    scale(calc(1 + var(--amp, 0) * 0.024));
  will-change: transform;
}
/* The image is now static inside the body: it keeps only its own idle drift. */
.hero-card .tutor {
  position: static; left: auto; bottom: auto;
  width: 100%; height: auto; max-width: none;
  transform: none;
  transform-origin: 50% 92%;
  filter: drop-shadow(0 18px 22px rgba(0, 0, 0, 0.55)) drop-shadow(0 2px 3px rgba(0, 0, 0, 0.4));
}
/* Idle: a slow float with a barely-there sway and breath. Ported from the
   app's living avatar so she moves the same way in both places. */
/* No idle drift. Her body stays still; only her face, her gaze and — while
   she speaks — a breath in her posture move. Requested, and right: a cutout
   that bobs reads as a sticker, one that holds still and blinks reads as
   someone standing there. */
@keyframes tutor-idle {
  0%, 100% { transform: translateY(0)    rotate(0deg)    scale(1); }
  24%      { transform: translateY(-5px) rotate(-0.7deg) scale(1.012); }
  46%      { transform: translateY(-2px) rotate(0.35deg) scale(1.006); }
  68%      { transform: translateY(-6px) rotate(0.8deg)  scale(1.014); }
  84%      { transform: translateY(-1px) rotate(-0.25deg) scale(1.003); }
}
/* Hover lift. It cannot be a transform declaration on .tutor: the idle
   animation runs on that element and animations outrank declarations, so the
   lift would silently do nothing. Instead it is a length the wrapper's own
   transform adds, transitioned through @property so it eases rather than
   snapping — and because the wrapper's transform is recomputed from variables
   every frame anyway, the two compose instead of fighting. */
/* No hover lift either — hovering the card brings a smile, nothing more. */
/* Halo brightens with her voice; contact shadow tightens as she rises. */
.tutor-halo { transition: opacity 120ms linear, transform 120ms linear; opacity: calc(0.75 + var(--amp, 0) * 0.5); }
/* The shadow's resting breath is a transform, not an opacity animation: a
   running animation outranks a plain declaration, so animating opacity here
   would cancel the reaction to her voice below. */
.tutor-shadow {
  transition: opacity 120ms linear;
  opacity: calc(0.85 - var(--amp, 0) * 0.35);
}

@keyframes tutor-shadow-breath {
  0%, 100% { transform: translateX(-50%) scaleX(1);    }
  24%      { transform: translateX(-50%) scaleX(0.9);  }
  46%      { transform: translateX(-50%) scaleX(0.96); }
  68%      { transform: translateX(-50%) scaleX(0.88); }
  84%      { transform: translateX(-50%) scaleX(0.98); }
}
.hero-card.speaking .tutor-halo {
  background: radial-gradient(circle, rgba(255, 179, 173, 0.5), rgba(109, 143, 234, 0.18) 45%, transparent 68%);
}

/* ── Hear Camille ─────────────────────────────────────────────────────────── */
.hear {
  display: flex; align-items: center; gap: 12px;
  margin-top: 14px; padding: 8px 12px 8px 8px;
  border-radius: 999px;
  border: 1px solid rgba(255,255,255,0.16);
  background: rgba(255,255,255,0.07);
  width: fit-content; max-width: 100%;
  transition: border-color 260ms ease, background 260ms ease;
}
.hear[hidden] { display: none; }
.hear:hover { border-color: rgba(255,255,255,0.32); background: rgba(255,255,255,0.1); }
.hear.playing { border-color: rgba(255, 179, 173, 0.55); background: rgba(206, 43, 55, 0.12); }

.hear-btn {
  position: relative; flex: none;
  display: grid; place-items: center;
  width: 42px; height: 42px; border-radius: 50%; border: 0; padding: 0;
  cursor: pointer; color: #fff;
  background: linear-gradient(135deg, #e0404c, var(--rouge) 48%, var(--rouge-deep));
  box-shadow: 0 8px 22px rgba(163, 32, 42, 0.45), inset 0 1px 0 rgba(255,255,255,0.3);
  transition: transform 220ms var(--ease), box-shadow 220ms var(--ease);
}
.hear-btn:hover { transform: scale(1.06); box-shadow: 0 12px 28px rgba(163, 32, 42, 0.6), inset 0 1px 0 rgba(255,255,255,0.35); }
.hear-btn:active { transform: scale(0.97); }
.hear-btn svg { width: 20px; height: 20px; position: absolute; transition: opacity 180ms ease, transform 220ms var(--ease); }
.hear-icon-play { opacity: 1; transform: translateX(1px); }
.hear-icon-pause { opacity: 0; transform: scale(0.7); }
.hear.playing .hear-icon-play { opacity: 0; transform: scale(0.7); }
.hear.playing .hear-icon-pause { opacity: 1; transform: none; }
/* Pulse rings while playing, so the control itself reads as sound. */
.hear-btn::before, .hear-btn::after {
  content: ''; position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid rgba(255, 179, 173, 0.6);
  opacity: 0; pointer-events: none;
}
.hear.playing .hear-btn::before { animation: hear-ring 1.8s ease-out infinite; }
.hear.playing .hear-btn::after  { animation: hear-ring 1.8s ease-out 0.9s infinite; }
@keyframes hear-ring { 0% { transform: scale(1); opacity: 0.7; } 100% { transform: scale(1.9); opacity: 0; } }

/* Seven bars, each scaled from her actual amplitude via --amp with a phase
   offset, so the wave follows her voice rather than looping a canned shape. */
.hear-wave { display: flex; align-items: center; gap: 2.5px; height: 22px; padding: 0 2px; }
.hear-wave i {
  display: block; width: 3px; height: 100%; border-radius: 3px;
  background: linear-gradient(180deg, #ffb3ad, #ff8a92);
  transform: scaleY(0.18); transform-origin: 50% 50%;
  transition: transform 90ms linear;
  --k: 1;
}
.hear-wave i:nth-child(1) { --k: 0.55; } .hear-wave i:nth-child(2) { --k: 0.8; } .hear-wave i:nth-child(3) { --k: 1; }
.hear-wave i:nth-child(4) { --k: 0.9; } .hear-wave i:nth-child(5) { --k: 1; } .hear-wave i:nth-child(6) { --k: 0.75; } .hear-wave i:nth-child(7) { --k: 0.5; }
.hear.playing .hear-wave i { transform: scaleY(calc(0.18 + var(--amp, 0) * var(--k) * 0.82)); }

.hear-label { display: grid; line-height: 1.2; }
.hear-label strong { font-size: 13.5px; font-weight: 720; letter-spacing: -0.01em; }
.hear-label small { color: var(--dimmer); font-size: 11.5px; }
.hear.playing .hear-label small { color: #ffb3ad; }

/* Subtitles: the bubble carries her French, the line beneath the English.
   The swap uses the bubble's existing entrance so it feels like the greeting
   rotator, just synchronised to her voice. */
.hero-card.speaking .bubble { background: rgba(206, 43, 55, 0.18); border-color: rgba(255, 179, 173, 0.45); }
[data-camille-caption] { transition: opacity 240ms ease; }
[data-camille-caption].swap { animation: caption-in 360ms var(--ease); }
@keyframes caption-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

@media (max-width: 620px) {
  .hear { width: 100%; }
  .hear-label small { display: none; }
  .btn-signature.hero .sig-face { padding: 15px 22px 15px 24px; font-size: 15.5px; }
  .btn-glass .glass-face { padding: 14px 22px 14px 16px; font-size: 15px; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-card .tutor, .tutor-shadow { animation: none !important; }
  .tutor-shadow { transform: translateX(-50%) !important; }
  .tutor-body { transform: translateX(-50%) !important; }
  .btn-signature.hero::before, .btn-glass:hover::after { animation: none !important; }
  .hear.playing .hear-btn::before, .hear.playing .hear-btn::after { animation: none; }
  .hear-wave i { transition: none; }
}

/* ── Subtitle height reserve ───────────────────────────────────────────────
   Her cues differ in length, so as they advance the card grew and shrank and
   everything below it moved with it. Measured at 375px: the card ran from 248
   to 311px and the marquee under it jumped 63px, four times, mid-sentence.
   (At 1280 there is no shift — Camille's stage sets the card height and the
   copy has room — which is exactly why this needed measuring at both widths
   rather than one.)

   The reserve is scoped to .speaking, so the resting card that most visitors
   see is not permanently taller to prevent a shift only pressing play can
   cause. The card grows once, at the moment of a deliberate action, then
   holds still for the whole clip.

   Values are the measured maxima: bubble 83px (3 pill lines), caption 41px
   (2 lines at 20.25px). */
/* The reserve goes on the COLUMN, never on the pill. Putting min-height on
   .bubble meant also making it a flex container, which turned the pill from
   inline-block into a block box: it stopped hugging its text and stretched to
   the full 426px column — ugly, and it grew the card by the very 64px the
   reserve was meant to prevent. Reserving on the column leaves the pill alone.
   Values are measured maxima across the five cues; see the numbers in the
   commit message. */
.hero-card { --cue-reserve: 186px; }
.hero-card.speaking .hero-card-copy { min-height: var(--cue-reserve, 0px); }
@media (max-width: 620px) {
  /* The narrow single-column card gives the copy 301px instead of 426px, so
     the longest cue wraps one line further. */
  .hero-card { --cue-reserve: 207px; }
}


/* ══════════════════════════════════════════════════════════════════════════
   HERO — FIELD & HEADLINE
   A moving background and a rotating headline. Everything transform/opacity
   (the floor and scanner ride the compositor; the particles are individual
   layers), and everything stops under reduced motion.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Orbs: visibly alive ─────────────────────────────────────────────────
   Faster and wider than before, plus a fourth warm one low-left, so the
   colour field is seen to move rather than inferred. */
.orb-1 { animation-duration: 13s; }
.orb-2 { animation-duration: 16s; }
.orb-3 { animation-duration: 19s; }
.orb-4 { width: 44%; height: 46%; bottom: -8%; left: -10%; background: radial-gradient(circle, rgba(217,166,60,0.30), transparent 66%); animation: float-d 17s ease-in-out infinite; }
@keyframes float-a { 0%,100% { transform: translate3d(0,0,0) scale(1); }    50% { transform: translate3d(14%,11%,0) scale(1.22); } }
@keyframes float-b { 0%,100% { transform: translate3d(0,0,0) scale(1.08); } 50% { transform: translate3d(-16%,10%,0) scale(0.9); } }
@keyframes float-c { 0%,100% { transform: translate3d(0,0,0) scale(1); }    50% { transform: translate3d(12%,-14%,0) scale(1.18); } }
@keyframes float-d { 0%,100% { transform: translate3d(0,0,0) scale(1); }    50% { transform: translate3d(18%,-9%,0) scale(1.14); } }

/* ── Floor: a grid running toward the viewer ─────────────────────────────
   The outer element is a plane tilted 64° in perspective; the inner tile is
   twice as tall as the plane and slides down by exactly one cell per loop,
   so the motion is seamless. Masked to fade at the horizon and the sides. */
.hero-floor {
  position: absolute; left: -25%; right: -25%; bottom: -4%; height: 46%;
  perspective: 620px; perspective-origin: 50% 0%;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 74%, transparent 100%), linear-gradient(to right, transparent 0%, #000 22%, #000 78%, transparent 100%);
  mask-image: linear-gradient(to bottom, transparent 0%, #000 22%, #000 74%, transparent 100%), linear-gradient(to right, transparent 0%, #000 22%, #000 78%, transparent 100%);
  -webkit-mask-composite: source-in; mask-composite: intersect;
  opacity: 0.55;
}
.hero-floor i {
  position: absolute; left: 0; right: 0; top: -100%; height: 200%;
  transform-origin: 50% 0%;
  transform: rotateX(64deg) translateY(0);
  background-image:
    linear-gradient(rgba(159,192,255,0.16) 1.5px, transparent 1.5px),
    linear-gradient(90deg, rgba(159,192,255,0.16) 1.5px, transparent 1.5px);
  background-size: 64px 64px;
  will-change: transform;
}
@media (prefers-reduced-motion: no-preference) {
  .hero-floor i { animation: floor-run 2.4s linear infinite; }
}
@keyframes floor-run { from { transform: rotateX(64deg) translateY(0); } to { transform: rotateX(64deg) translateY(64px); } }
/* Horizon glow where the floor meets the dark. */
.hero-floor::before {
  content: ''; position: absolute; left: 10%; right: 10%; top: 20%; height: 2px;
  background: linear-gradient(90deg, transparent, rgba(159,192,255,0.55), rgba(255,255,255,0.7), rgba(159,192,255,0.55), transparent);
  filter: blur(1px); opacity: 0.7;
}

/* ── Particles ─────────────────────────────────────────────────────────── */
.hero-particles { position: absolute; inset: 0; overflow: hidden; }
.hero-particles i {
  position: absolute; bottom: -8px; display: block; border-radius: 50%;
  background: #cfe0ff;
  box-shadow: 0 0 6px 1px rgba(159,192,255,0.55);
  opacity: 0;
  animation: particle-rise var(--dur, 20s) linear var(--delay, 0s) infinite;
  will-change: transform, opacity;
}
@keyframes particle-rise {
  0%   { transform: translate3d(0, 0, 0) scale(0.6); opacity: 0; }
  8%   { opacity: var(--peak, 0.6); }
  85%  { opacity: var(--peak, 0.6); }
  100% { transform: translate3d(var(--drift, 0px), -110vh, 0) scale(1.1); opacity: 0; }
}

/* ── Scanner: a light that crosses the hero every few seconds ─────────── */
.hero-scan {
  position: absolute; top: -20%; bottom: -20%; left: -30%; width: 22%;
  background: linear-gradient(90deg, transparent, rgba(159,192,255,0.06) 35%, rgba(255,255,255,0.10) 50%, rgba(159,192,255,0.06) 65%, transparent);
  transform: skewX(-14deg) translateX(-100%);
  will-change: transform;
}
@media (prefers-reduced-motion: no-preference) {
  .hero-scan { animation: hero-scan 11s cubic-bezier(0.4, 0, 0.2, 1) 2s infinite; }
}
@keyframes hero-scan {
  0%, 68% { transform: skewX(-14deg) translateX(-100%); }
  100%    { transform: skewX(-14deg) translateX(720%); }
}

/* (The glyph-decode entrance was removed: cycling accented characters read
   as another language. The headline now rotates through situations instead —
   see "Headline rotation" at the end of this file.) */

/* ── Tech rule ─────────────────────────────────────────────────────────────
   A hairline under the headline with a light that travels its length and
   short tick marks at the ends. It gives the type an instrument-panel base
   without adding a second colour. */
.hero-rule {
  position: relative; display: block; width: min(56%, 420px); height: 1px;
  margin: 24px auto 26px;
  background: linear-gradient(90deg, transparent, rgba(159,192,255,0.5) 18%, rgba(159,192,255,0.5) 82%, transparent);
}
.hero-rule::before, .hero-rule::after {
  content: ''; position: absolute; top: -3px; width: 1px; height: 7px; background: rgba(159,192,255,0.6);
}
.hero-rule::before { left: 18%; } .hero-rule::after { right: 18%; }
.hero-rule i {
  /* The track: from tick to tick. */
  position: absolute; top: -2px; left: 18%; width: 64%; height: 5px;
}
.hero-rule i::before {
  content: ''; position: absolute; top: 0; left: 0; width: 46px; height: 5px; border-radius: 3px;
  background: linear-gradient(90deg, transparent, #fff, transparent);
  box-shadow: 0 0 12px rgba(159,192,255,0.9);
}
/* Animating  here is deliberate: the light must travel a distance
   defined by its track (64% minus its own width), which a transform on the
   light cannot express. It is a 46×5px box — the layout cost is nil. */
@media (prefers-reduced-motion: no-preference) {
  .hero-rule i::before { animation: rule-run 3.6s cubic-bezier(0.45, 0, 0.55, 1) 1.4s infinite alternate; }
}
@keyframes rule-run { from { left: 0; } to { left: calc(100% - 46px); } }

@media (max-width: 620px) {
  .hero-floor { height: 38%; opacity: 0.45; }
  .hero-rule { width: 70%; margin: 18px auto 20px; }
}
@media (prefers-reduced-motion: reduce) {
  .hero-floor i, .hero-particles i, .hero-scan, .hero-rule i::before, .orb { animation: none !important; }
  .hero-particles { display: none; }
}


/* ── Headline rotation ─────────────────────────────────────────────────────
   Exit: line 1's words lift up into their masks, staggered from the left;
   the gradient line lifts as one. Enter: the fresh words rise as on load
   (their base rule), and the gradient line rises with line-in — a distinct
   name from the load-time word-rise, because only a change of animation-name
   restarts an animation. title-life stays on the second layer throughout so
   the sheen never resets. */
.hero-title.swap-out .line:not(.grad) .w > i {
  animation: word-out 320ms cubic-bezier(0.4, 0, 0.7, 0.2) both;
  animation-delay: calc(var(--wi, 0) * 28ms);
}
@keyframes word-out {
  to { transform: translate3d(0, -118%, 0) rotate(-1.6deg) scale(0.985); opacity: 0; filter: blur(5px); }
}
.hero-title.swap-out .grad {
  animation:
    line-out 320ms cubic-bezier(0.4, 0, 0.7, 0.2) 60ms both,
    title-life 9s linear 1.8s infinite;
}
@keyframes line-out {
  to { transform: translate3d(0, -118%, 0) rotate(-1.6deg) scale(0.985); opacity: 0; filter: blur(5px); }
}
.hero-title.swap-in .grad {
  animation:
    line-in 1000ms cubic-bezier(0.16, 1, 0.3, 1) 140ms both,
    title-life 9s linear 1.8s infinite;
}
@keyframes line-in {
  0%   { transform: translate3d(0, 128%, 0) rotate(2.4deg) scale(0.98); opacity: 0; filter: blur(7px); }
  60%  { filter: blur(0); }
  100% { transform: none; opacity: 1; filter: blur(0); }
}
@media (prefers-reduced-motion: reduce) {
  .hero-title.swap-out .line:not(.grad) .w > i,
  .hero-title.swap-out .grad,
  .hero-title.swap-in .grad { animation: none !important; transform: none !important; opacity: 1 !important; filter: none !important; }
}

/* ==========================================================================
   CONTACT PAGE — /contact
   Contact used to be a band at the bottom of the landing page. It is its own
   destination now, which changes what it has to do: a landing-page strip only
   has to catch someone already scrolling, but a page someone navigated to on
   purpose has to answer "will this actually reach a human, and when".

   Hence two columns rather than a form on its own. Most people arriving here
   have a question the FAQ or the help centre already answers, and routing
   them there is a better outcome for them than a day's wait for a reply.
   ========================================================================== */

/* The nav is fixed, so any page without a hero has to buy its own clearance. */
.page { padding-top: 128px; }

.page-head { position: relative; padding: 34px 0 8px; overflow: clip; }
.page-head-glow {
  position: absolute; left: 50%; top: -220px; width: 900px; height: 420px;
  transform: translateX(-50%); pointer-events: none; z-index: -1;
  background: radial-gradient(closest-side, rgba(58, 99, 196, 0.30), transparent 72%);
  filter: blur(46px);
}
.page-title {
  margin: 0 0 14px;
  font-size: clamp(34px, 5.6vw, 60px); font-weight: 760;
  line-height: 1.06; letter-spacing: -0.038em;
  text-wrap: balance;
}
.page-lead { max-width: 560px; margin: 0; color: var(--dim); font-size: 16.5px; line-height: 1.62; }

.contact-band { padding-top: 48px; }
.contact-layout {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
  gap: 30px; align-items: start;
}

.contact-h {
  margin: 0 0 20px; font-size: 12px; font-weight: 760;
  letter-spacing: 0.13em; text-transform: uppercase; color: #9fc0ff;
}

.contact-main {
  padding: 30px; border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: linear-gradient(165deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.02));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.32);
}
.contact-main .contact-form { margin-top: 0; }
.contact-fine { margin: 4px 0 0; color: var(--dimmer); font-size: 12.5px; }
.contact-fine a { color: var(--dim); text-underline-offset: 3px; }
.contact-fine a:hover { color: #fff; }

/* The select needs its own chevron because appearance:none removes the native
   one, and a select with no affordance reads as a dead text field. */
.field-chevron {
  position: absolute; right: 15px; top: 50%; width: 17px; height: 17px;
  transform: translateY(-50%); pointer-events: none; color: var(--dimmer);
  stroke-width: 2;
}
.field select { padding-right: 42px; }

/* ── The routes that beat waiting for a reply ───────────────────────────── */
.contact-aside { display: grid; gap: 10px; align-content: start; }
.contact-route {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 14px; align-items: center;
  padding: 16px 18px; border: 1px solid var(--line); border-radius: 16px;
  background: rgba(255, 255, 255, 0.035); text-decoration: none; color: inherit;
  transition: border-color 240ms ease, background 240ms ease, transform 240ms var(--ease);
}
.contact-route:hover {
  border-color: var(--line-strong); background: rgba(255, 255, 255, 0.075);
  transform: translateX(3px);
}
.route-icon {
  display: grid; place-items: center; width: 40px; height: 40px;
  border-radius: 12px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.route-icon svg { width: 19px; height: 19px; }
.contact-route strong { display: block; font-size: 14.5px; font-weight: 660; letter-spacing: -0.012em; }
.contact-route small { display: block; margin-top: 3px; color: var(--dimmer); font-size: 12.5px; line-height: 1.45; }
.route-arrow {
  width: 17px; height: 17px; color: var(--dimmer);
  opacity: 0; transform: translateX(-4px);
  transition: opacity 240ms ease, transform 240ms var(--ease);
}
.contact-route:hover .route-arrow { opacity: 1; transform: none; }

.contact-note {
  margin-top: 8px; padding: 18px 20px;
  border: 1px solid var(--line); border-radius: 16px;
  background: rgba(16, 66, 143, 0.16);
}
.contact-note.quiet { background: rgba(255, 255, 255, 0.025); margin-top: 0; }
.contact-note h3 { margin: 0 0 7px; font-size: 14px; font-weight: 680; letter-spacing: -0.012em; }
.contact-note p { margin: 0; color: var(--dim); font-size: 13px; line-height: 1.58; }

@media (max-width: 900px) {
  .contact-layout { grid-template-columns: 1fr; gap: 22px; }
  .page { padding-top: 112px; }
}
@media (max-width: 620px) {
  .contact-main { padding: 22px 18px; }
  .contact-form .btn { width: 100%; }
}

/* The arrow is the only thing here that is hover-only, and it is decorative —
   the whole row is the target, so touch users lose nothing. */
@media (prefers-reduced-motion: reduce) {
  .contact-route:hover { transform: none; }
  .route-arrow { opacity: 1; transform: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   BELOW THE HERO — rebuilt
   One authoritative layer for everything under the fold. What this replaces
   had drifted across four places in this file, including two near-identical
   plan-button blocks where the later silently won. That accumulated override
   layer is most of why the page read as generic: nothing had one definition,
   and every section was built from the same rounded rectangle.

   Three things change. Type gets a real display/text split and a genuine
   range. Each section gets its own material and its own structural idea, so
   a step is not shaped like a price. Motion varies by section instead of one
   uniform fade for eighty elements.

   Nothing here touches the hero, the nav, or the shared button recipes.
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Type ──────────────────────────────────────────────────────────────────
   No webfont is available — the CSP is default-src 'self' and there is no
   build step to vendor one. So the hierarchy is built from optical cuts that
   already ship on the audience's devices: Segoe UI Variable on Windows 11 and
   SF Pro on Apple both have separate Display and Text designs, and asking for
   them by name is what stops small copy looking muddy and large copy looking
   soft. Everything falls back through the previous stack, so nothing breaks.

   --font-fr is the one editorial move: this product teaches French, and the
   page quotes real French throughout. Setting the language being taught in a
   serif, and the interface describing it in a sans, gives those quotes the
   weight of something printed rather than something UI. Every listed serif
   ships on either Windows, macOS or Android. */
:root {
  --font-display: "Segoe UI Variable Display", "SF Pro Display", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-text: "Segoe UI Variable Text", "SF Pro Text", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-fr: "Iowan Old Style", "Palatino Linotype", Palatino, "Book Antiqua", Georgia, "Times New Roman", serif;

  /* Spacing rhythm. The old page ran 92px between every band regardless of
     what the band contained, which is why it read as a list rather than a
     composition. */
  --gap-band: clamp(84px, 9vw, 132px);
  --gap-tight: clamp(56px, 6vw, 78px);
}

/* Body copy drops from the old 560–780 weights to 400. Heavy small text is
   the most reliable tell of a template; the contrast has to come from the
   display end instead, which is why the headings below go up to 780. */
body { font-family: var(--font-text); font-weight: 400; letter-spacing: -0.006em; }

h1, h2, h3, h4, .amount, .stat-n, .step-n {
  font-family: var(--font-display);
  font-variant-numeric: tabular-nums;
}

/* ── Bands ─────────────────────────────────────────────────────────────────
   Alternating one flat tint gave the page no rhythm. Bands now differ in
   what separates them: a hairline that fades at both ends reads as a rule
   drawn across a page rather than a box edge. */
.band { position: relative; padding: var(--gap-band) 0; overflow: clip; }
.band.alt {
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(18, 34, 77, 0.62), transparent 68%),
    linear-gradient(180deg, transparent, rgba(18, 34, 77, 0.34) 30%, rgba(18, 34, 77, 0.34) 70%, transparent);
}
.band + .band::before {
  content: ''; position: absolute; left: 8%; right: 8%; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-strong) 22%, var(--line-strong) 78%, transparent);
  pointer-events: none;
}

/* The scanner that crosses a band as it enters. JS adds .lit (landing.js,
   "Band scanner"). Kept from the old layer because it is a good effect —
   rewritten here only because its old home was inside a region being cut. */
.band::after {
  content: ''; position: absolute; left: 12%; right: 12%; top: 0; height: 1px; z-index: 1;
  background: linear-gradient(90deg, transparent, rgba(159, 192, 255, 0.85) 30%, rgba(255, 255, 255, 0.95) 50%, rgba(159, 192, 255, 0.85) 70%, transparent);
  box-shadow: 0 0 22px rgba(109, 143, 234, 0.7);
  transform: scaleX(0); transform-origin: 50% 50%;
  transition: transform 1400ms cubic-bezier(0.16, 1, 0.3, 1);
  pointer-events: none;
}
.band.lit::after { transform: scaleX(1); }

/* ── Section header chrome ─────────────────────────────────────────────── */
.kicker {
  display: inline-flex; align-items: center; gap: 9px;
  margin: 0 0 18px; color: #9fc0ff;
  font-family: var(--font-text);
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
}
.kicker::before {
  content: ''; width: 22px; height: 1px; flex: none;
  background: linear-gradient(90deg, transparent, currentColor);
}
.kicker.light { color: #cfe0ff; }

.section-title {
  margin: 0 0 18px;
  font-size: clamp(31px, 4.9vw, 56px);
  font-weight: 780; line-height: 1.04; letter-spacing: -0.042em;
  text-wrap: balance;
}
.section-lead {
  max-width: 600px; margin: 0 0 var(--gap-tight);
  color: var(--dim); font-size: 16.5px; line-height: 1.66; font-weight: 400;
  text-wrap: pretty;
}

/* Titles wipe in rather than fade, so the words look printed onto the page. */
.reveal-armed .section-title.reveal { clip-path: inset(0 100% 0 0); opacity: 1; transform: none; }
.reveal-armed .section-title.reveal.in {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1100ms cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0ms);
}

/* French, wherever it is quoted on the page. */
.fr { font-family: var(--font-fr); font-style: italic; letter-spacing: 0; }

/* ══ STATS ═════════════════════════════════════════════════════════════════
   Four facts, not four cards. The boxes went: figures at display scale on a
   ruled band carry far more authority than the same numbers in bordered
   rectangles, and this is the first thing seen after the hero.

   The dividers are borders on the cells rather than separate elements so they
   collapse correctly when the grid reflows to 2×2. */
.stats { padding: var(--gap-tight) 0; }
.stats-grid {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 0;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
}
.stat { padding: 34px 26px; text-align: center; border-left: 1px solid var(--line); }
.stat:first-child { border-left: 0; }
.stat strong {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(38px, 5vw, 62px); font-weight: 780;
  letter-spacing: -0.05em; line-height: 1;
  font-variant-numeric: tabular-nums;
  background: linear-gradient(150deg, #fff 20%, #a8c6ff);
  -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
  background-color: #cfe0ff;
  /* The count-up writes increasing digit counts into this element. Reserving
     the width stops the row jittering as it climbs. */
  min-width: 3ch;
}
.stat span {
  display: block; margin-top: 11px; color: var(--dimmer);
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.11em; text-transform: uppercase;
}

/* ══ WHY ═══════════════════════════════════════════════════════════════════
   The emotional argument of the page: what NCLC 7 actually changes. It was
   three identical bordered cards, which gave three quite different claims the
   same visual weight and flattened the argument into a feature list.

   Now they are numbered entries under a lit rule — closer to an editorial
   list than a card grid. The rule is the only thing that moves on hover,
   which is enough: the eye follows a colour change more readily than a lift,
   and nothing shifts position. */
.why-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; margin-top: 8px; }
.why-card {
  position: relative; padding: 34px 28px 30px;
  border-radius: 18px;
  background: transparent;
  transition: background 380ms ease;
}
/* ::after, not ::before. The cursor-spotlight block further up this file
   already owns .why-card::before, and a second rule on the same pseudo-element
   does not replace it — the two merge, so the spotlight's `opacity: 0` would
   carry into this one and the rule would be invisible until hover. */
.why-card::after {
  content: ''; position: absolute; left: 28px; right: 28px; top: 0; height: 2px;
  border-radius: 2px;
  background: var(--line-strong);
  transition: background 420ms ease, box-shadow 420ms ease;
}
.why-card:hover { background: rgba(255, 255, 255, 0.035); }
.why-card:nth-child(1):hover::after { background: linear-gradient(90deg, var(--blue-lift), transparent); box-shadow: 0 0 18px rgba(109, 143, 234, 0.5); }
.why-card:nth-child(2):hover::after { background: linear-gradient(90deg, #ff8a80, transparent); box-shadow: 0 0 18px rgba(206, 43, 55, 0.5); }
.why-card:nth-child(3):hover::after { background: linear-gradient(90deg, #6ee7a8, transparent); box-shadow: 0 0 18px rgba(31, 122, 85, 0.5); }

.why-icon {
  display: grid; place-items: center; width: 44px; height: 44px; margin-bottom: 22px;
  border-radius: 13px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.why-icon svg { width: 21px; height: 21px; }
.why-card h3 {
  margin: 0 0 11px; font-size: 21px; font-weight: 700; letter-spacing: -0.026em;
  text-wrap: balance;
}
.why-card p { margin: 0; color: var(--dim); font-size: 15px; line-height: 1.62; text-wrap: pretty; }

/* Shared with the features grid. */
.tone-bleu { color: #cfe0ff; background: linear-gradient(150deg, rgba(58,99,196,0.5), rgba(16,66,143,0.32)); }
.tone-rouge { color: #ffd3cf; background: linear-gradient(150deg, rgba(206,43,55,0.46), rgba(163,32,42,0.3)); }
.tone-green { color: #c7f2dd; background: linear-gradient(150deg, rgba(31,122,85,0.5), rgba(31,122,85,0.28)); }
.tone-violet { color: #ded3ff; background: linear-gradient(150deg, rgba(122,92,226,0.5), rgba(122,92,226,0.28)); }
.tone-gold { color: #ffe6b8; background: linear-gradient(150deg, rgba(217,166,60,0.48), rgba(143,93,6,0.3)); }

/* ══ HOW — the six steps ═══════════════════════════════════════════════════
   This was a 2×3 grid, which actively fought the content: the eye reads
   01,02 / 03,04 / 05,06 and has to reconstruct the order. It is a sequence,
   so it is now drawn as one — a single spine with numbered nodes down it.

   The spine is a gradient on the list itself rather than a border on each
   item, so it runs continuously through the gaps and can fade out at the
   bottom instead of stopping dead after step six. */
.steps {
  position: relative; list-style: none; margin: 0; padding: 0 0 0 76px;
  display: grid; gap: 2px;
}
.steps::before {
  content: ''; position: absolute; left: 27px; top: 8px; bottom: 40px; width: 2px;
  background: linear-gradient(180deg, transparent, rgba(109, 143, 234, 0.55) 6%, rgba(109, 143, 234, 0.55) 72%, transparent);
}
.step {
  position: relative; padding: 22px 26px; border-radius: 16px;
  background: transparent;
  transition: background 320ms ease;
}
.step:hover { background: rgba(255, 255, 255, 0.045); }

/* The node sits on the spine, not in the text column. */
.step-n {
  position: absolute; left: -76px; top: 20px;
  display: grid; place-items: center; width: 56px; height: 56px;
  border-radius: 50%;
  border: 1px solid var(--line-strong);
  background: radial-gradient(circle at 50% 34%, var(--night-3), var(--night) 78%);
  color: #cfe0ff;
  font-family: var(--font-display);
  font-size: 15px; font-weight: 700; letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  transition: border-color 320ms ease, color 320ms ease, box-shadow 320ms ease;
}
.step:hover .step-n {
  border-color: rgba(109, 143, 234, 0.75); color: #fff;
  box-shadow: 0 0 0 5px rgba(109, 143, 234, 0.12), 0 0 26px rgba(109, 143, 234, 0.4);
}
.step h3 { margin: 0 0 7px; font-size: 19px; font-weight: 700; letter-spacing: -0.024em; }
.step p { margin: 0; color: var(--dim); font-size: 15px; line-height: 1.6; max-width: 62ch; text-wrap: pretty; }

/* ══ INSIDE — the bento ════════════════════════════════════════════════════
   The size differences were reading as accidental because every tile used the
   same material. The lead tile now runs its artwork full-bleed under a scrim
   with the copy sitting on top, and the readiness tile shows its six checks
   as checks instead of burying them in a sentence — that list is the most
   trust-building thing on the page, because it is the product saying when you
   are *not* ready. */
.feature-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 8px; }
.feature {
  position: relative; padding: 30px 26px; border-radius: var(--radius);
  border: 1px solid var(--line);
  background: linear-gradient(165deg, rgba(255, 255, 255, 0.065), rgba(255, 255, 255, 0.018));
  transition: border-color 340ms ease, background 340ms ease, transform 340ms var(--ease);
  transform-style: preserve-3d;
}
.feature:hover { border-color: var(--line-strong); background: linear-gradient(165deg, rgba(255,255,255,0.1), rgba(255,255,255,0.03)); }
.feature-icon {
  display: grid; place-items: center; width: 42px; height: 42px; margin-bottom: 20px;
  border-radius: 12px; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.feature-icon svg { width: 20px; height: 20px; }
.feature h3 { margin: 0 0 9px; font-size: 19px; font-weight: 700; letter-spacing: -0.024em; text-wrap: balance; }
.feature p { margin: 0; color: var(--dim); font-size: 15px; line-height: 1.62; text-wrap: pretty; }

/* Lead tile — artwork behind the copy rather than beside it. */
.feature.big {
  grid-column: span 2; grid-row: span 2;
  display: flex; align-items: flex-end; padding: 0; overflow: hidden; min-height: 340px;
  border-color: var(--line-strong);
}
.feature-art { position: absolute; inset: 0; }
.feature-art img { width: 100%; height: 100%; object-fit: cover; transition: transform 900ms var(--ease); }
.feature.big:hover .feature-art img { transform: scale(1.045); }
.feature.big::after {
  content: ''; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(0deg, rgba(6, 10, 24, 0.95) 8%, rgba(6, 10, 24, 0.72) 38%, rgba(6, 10, 24, 0.12) 72%, transparent);
}
.feature-copy { position: relative; z-index: 1; padding: 30px 30px 32px; }
.feature.big h3 { font-size: clamp(21px, 2.1vw, 27px); }
.feature.big p { max-width: 44ch; }

/* Readiness tile — the six checks, made visible. */
.feature.wide { grid-column: span 3; }
.readiness { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px 22px; margin: 20px 0 0; padding: 0; list-style: none; }
.readiness li {
  position: relative; padding-left: 26px;
  color: var(--dim); font-size: 14px; line-height: 1.5;
}
.readiness li::before {
  content: ''; position: absolute; left: 0; top: 4px; width: 15px; height: 15px;
  border-radius: 50%; border: 1.5px solid rgba(110, 231, 168, 0.55);
  background: rgba(110, 231, 168, 0.12);
}
.readiness li::after {
  content: ''; position: absolute; left: 4.5px; top: 8px; width: 6px; height: 3.5px;
  border-left: 1.6px solid #6ee7a8; border-bottom: 1.6px solid #6ee7a8;
  transform: rotate(-45deg);
}

/* ══ PRICING ═══════════════════════════════════════════════════════════════
   Five cumulative tiers in five equal columns is a layout fighting its own
   content: every tier's list opens "Everything in <previous>", a relationship
   a flat row cannot show. The inheritance line is now explicit — a labelled
   chip at the top of each list rather than a bullet indistinguishable from
   the features it inherits.

   Five columns at 1140px leaves 208px per tier, which is what made the
   buttons wrap before. Solved by dropping the button to a single short verb
   plus an arrow, and by letting the grid fall to three columns earlier. */
.cycle {
  display: inline-flex; gap: 3px; padding: 4px; margin-bottom: 40px;
  border: 1px solid var(--line-strong); border-radius: 999px;
  background: rgba(255, 255, 255, 0.045);
}
.cycle button {
  padding: 11px 22px; border: 0; border-radius: 999px; cursor: pointer;
  color: var(--dim); background: transparent;
  font-family: var(--font-text); font-size: 13.5px; font-weight: 620; letter-spacing: -0.008em;
  transition: color 220ms ease, background 220ms ease;
}
.cycle button em { font-style: normal; font-size: 10.5px; font-weight: 700; opacity: 0.85; margin-left: 6px; }
.cycle button.active {
  color: #fff;
  background: linear-gradient(135deg, var(--blue), var(--bleu));
  box-shadow: 0 6px 20px rgba(16, 66, 143, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.2);
}

.plans { display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px; align-items: stretch; }
.plan {
  position: relative; display: flex; flex-direction: column;
  padding: 28px 20px 22px; border-radius: 20px;
  border: 1px solid var(--line);
  background: linear-gradient(168deg, rgba(255, 255, 255, 0.065), rgba(255, 255, 255, 0.018));
  transition: border-color 320ms ease, box-shadow 320ms var(--ease), background 320ms ease;
}
.plan:hover {
  border-color: var(--line-strong);
  background: linear-gradient(168deg, rgba(255,255,255,0.095), rgba(255,255,255,0.028));
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
}
.plan h3 {
  margin: 0 0 14px; font-size: 15px; font-weight: 700;
  letter-spacing: 0.02em; text-transform: uppercase; color: var(--dim);
}
.plan-tag {
  position: absolute; top: -10px; left: 20px;
  padding: 5px 11px; border-radius: 999px; white-space: nowrap;
  font-family: var(--font-text);
  font-size: 9.5px; font-weight: 750; letter-spacing: 0.07em; text-transform: uppercase;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}

.price { margin: 0 0 12px; display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
.amount {
  font-family: var(--font-display);
  font-size: 34px; font-weight: 780; letter-spacing: -0.05em; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.per { color: var(--dimmer); font-size: 11px; line-height: 1.35; }
.plan-lead { margin: 0 0 18px; color: var(--dim); font-size: 13.5px; line-height: 1.5; min-height: 40px; }

.plan ul { margin: 0 0 22px; padding: 0; list-style: none; display: grid; gap: 10px; }
.plan li { position: relative; padding-left: 20px; color: var(--dim); font-size: 13px; line-height: 1.45; }
.plan li::before {
  content: ""; position: absolute; left: 0; top: 5px; width: 11px; height: 6px;
  border-left: 1.8px solid #6ee7a8; border-bottom: 1.8px solid #6ee7a8;
  transform: rotate(-45deg);
}
/* The inheritance line, made explicit rather than disguised as a feature. */
.plan li.inherits {
  padding-left: 0; margin-bottom: 3px;
  color: var(--dimmer); font-size: 11px; font-weight: 650;
  letter-spacing: 0.055em; text-transform: uppercase;
}
.plan li.inherits::before {
  content: "\2b06"; position: static; display: inline-block;
  width: auto; height: auto; border: 0; transform: none;
  margin-right: 6px; opacity: 0.75; font-size: 9px;
}

.plans-note { margin: 30px 0 0; color: var(--dimmer); font-size: 12.5px; line-height: 1.6; max-width: 78ch; }
.plans-note a { color: var(--dim); text-underline-offset: 3px; }
.plans-note a:hover { color: #fff; }

/* Featured tier — gold, because every other card on the page is blue or
   white, so the one we want chosen is the only thing wearing a colour that
   appears nowhere else. */
.plan.featured {
  --gold-lift: #f1c15c;
  border-color: rgba(217, 166, 60, 0.7);
  background: linear-gradient(168deg, rgba(217, 166, 60, 0.22), rgba(217, 166, 60, 0.055) 46%, rgba(255, 255, 255, 0.03));
  box-shadow:
    0 24px 60px rgba(217, 166, 60, 0.22),
    0 0 0 1px rgba(217, 166, 60, 0.18),
    inset 0 1px 0 rgba(255, 236, 190, 0.32);
  transform: translateY(-10px) scale(1.03);
  z-index: 1;
}
.plan.featured:hover {
  border-color: rgba(241, 193, 92, 0.9);
  box-shadow: 0 32px 78px rgba(217, 166, 60, 0.34), 0 0 0 1px rgba(217, 166, 60, 0.3), inset 0 1px 0 rgba(255, 236, 190, 0.42);
}
.plan.featured h3 { color: var(--gold-lift); }
.plan.featured li::before { border-color: var(--gold-lift); }
.plan.featured .plan-lead { color: rgba(255, 245, 225, 0.78); }

/* Travelling shine along the border: one bright wedge in a conic gradient,
   masked to a 1.5px ring so it reads as a light running around the edge. */
.plan.featured::after {
  content: ''; position: absolute; inset: -1.5px; border-radius: inherit; pointer-events: none;
  padding: 1.5px;
  background: conic-gradient(from var(--shine, 0deg), transparent 0 78%, rgba(255, 226, 150, 0.95) 88%, transparent 96%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude;
  animation: featured-shine 5.5s linear infinite;
}
@property --shine { syntax: '<angle>'; initial-value: 0deg; inherits: false; }
@keyframes featured-shine { to { --shine: 360deg; } }

.plan-flag {
  position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
  padding: 6px 14px; border-radius: 999px; white-space: nowrap;
  color: #2b1d05;
  background: linear-gradient(135deg, #ffe08a, var(--gold) 55%, #c98f22);
  box-shadow: 0 8px 22px rgba(217, 166, 60, 0.45), inset 0 1px 0 rgba(255, 255, 255, 0.55);
  font-family: var(--font-text);
  font-size: 10px; font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase;
}

/* ── Plan buttons ──────────────────────────────────────────────────────────
   Quiet on the four ordinary tiers, solid gold on the featured one, so the
   band has exactly one loud button and it is on the tier we want chosen.
   Short label plus arrow: at 208px a two-word verb phrase is what wrapped. */
.plan-cta {
  position: relative; overflow: hidden;
  margin-top: auto; display: flex; align-items: center; justify-content: center; gap: 7px;
  height: 46px; padding: 0 14px; width: 100%;
  border: 1px solid var(--line-strong); border-radius: 13px;
  color: #fff; background: rgba(255, 255, 255, 0.06);
  text-decoration: none; white-space: nowrap;
  font-family: var(--font-text); font-size: 14px; font-weight: 660; letter-spacing: -0.01em;
  transition: border-color 240ms ease, background 240ms ease, box-shadow 240ms ease;
}
.plan-cta span { position: relative; z-index: 1; }
.plan-cta .cta-arrow {
  position: relative; z-index: 1; width: 16px; height: 16px; flex: none;
  opacity: 0.55; transition: transform 260ms var(--ease), opacity 260ms ease;
}
.plan-cta:hover .cta-arrow { transform: translateX(4px); opacity: 1; }

/* The fill grows from the bottom edge, tinted by the tier's own tag colour so
   the button belongs to its card rather than to a global button style. */
.plan-cta::before {
  content: ''; position: absolute; inset: 0; z-index: 0;
  transform: translateY(101%);
  transition: transform 380ms var(--ease);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.06));
}
.plan-cta:hover::before { transform: translateY(0); }
.plan-cta:hover { border-color: rgba(255, 255, 255, 0.36); }

.plan:has(.plan-tag.tone-bleu) .plan-cta::before { background: linear-gradient(180deg, rgba(109,143,234,0.62), rgba(58,99,196,0.34)); }
.plan:has(.plan-tag.tone-violet) .plan-cta::before { background: linear-gradient(180deg, rgba(150,122,240,0.62), rgba(122,92,226,0.34)); }
.plan:has(.plan-tag.tone-green) .plan-cta::before { background: linear-gradient(180deg, rgba(52,211,153,0.58), rgba(31,122,85,0.36)); }
.plan:has(.plan-tag.tone-bleu) .plan-cta:hover { border-color: rgba(109,143,234,0.85); box-shadow: 0 16px 34px rgba(16,66,143,0.5); }
.plan:has(.plan-tag.tone-violet) .plan-cta:hover { border-color: rgba(150,122,240,0.85); box-shadow: 0 16px 34px rgba(90,60,190,0.5); }
.plan:has(.plan-tag.tone-green) .plan-cta:hover { border-color: rgba(52,211,153,0.85); box-shadow: 0 16px 34px rgba(16,120,80,0.5); }

.plan.featured .plan-cta {
  border: 0; color: #2b1d05;
  background: linear-gradient(135deg, #ffe08a, var(--gold) 58%, #c98f22);
  box-shadow: 0 12px 30px rgba(217, 166, 60, 0.42), inset 0 1px 0 rgba(255, 255, 255, 0.5);
  font-weight: 750;
}
.plan.featured .plan-cta::before {
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
  transform: translateX(-110%);
}
.plan.featured .plan-cta:hover::before { transform: translateX(110%); }
.plan.featured .plan-cta .cta-arrow { stroke: #2b1d05; opacity: 0.8; }

/* Free is the quietest thing in the row: an outline that only gains a surface
   on approach. It is a real option, not the one being sold. */
.plan:not(:has(.plan-tag)):not(.featured) .plan-cta {
  background: transparent; border-color: var(--line); color: var(--dim);
}
.plan:not(:has(.plan-tag)):not(.featured) .plan-cta:hover { color: #fff; border-color: var(--line-strong); }

/* ══ TRY IT ════════════════════════════════════════════════════════════════
   The device frame read as generic browser chrome — three dots and a
   breadcrumb. This is a phone-first learning app, so the frame now shows what
   the app shows at the top of a lesson: which lesson, what level, and how far
   through you are. The progress bar is decorative and marked aria-hidden. */
.try-inner { display: grid; grid-template-columns: minmax(0, 0.92fr) minmax(0, 1.08fr); gap: 60px; align-items: center; }
.try-copy .section-lead { margin-bottom: 28px; }
.try-points { margin: 0; padding: 0; list-style: none; display: grid; gap: 12px; }
.try-points li { position: relative; padding-left: 28px; color: var(--dim); font-size: 15px; line-height: 1.55; }
.try-points li::before {
  content: ''; position: absolute; left: 0; top: 6px; width: 13px; height: 7px;
  border-left: 1.9px solid var(--blue-lift); border-bottom: 1.9px solid var(--blue-lift);
  transform: rotate(-45deg);
}

.try-device { position: relative; }
.try-frame {
  position: relative; z-index: 1; overflow: hidden;
  border: 1px solid var(--line-strong); border-radius: 26px;
  background: linear-gradient(170deg, rgba(18, 34, 77, 0.72), rgba(6, 10, 24, 0.94));
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.55), inset 0 1px 0 rgba(255, 255, 255, 0.12);
}
.try-topbar {
  display: flex; align-items: center; gap: 12px;
  padding: 15px 18px; border-bottom: 1px solid var(--line);
  background: rgba(255, 255, 255, 0.035);
}
.try-crumb { font-size: 12.5px; font-weight: 640; color: rgba(255, 255, 255, 0.9); letter-spacing: -0.008em; }
.try-level {
  margin-left: auto; padding: 4px 10px; border-radius: 999px;
  border: 1px solid rgba(109, 143, 234, 0.45); background: rgba(58, 99, 196, 0.22);
  color: #cfe0ff; font-size: 10.5px; font-weight: 720; letter-spacing: 0.06em;
}
.try-bar { flex: 1; max-width: 92px; height: 3px; border-radius: 2px; background: rgba(255, 255, 255, 0.12); overflow: hidden; }
.try-bar i { display: block; width: 34%; height: 100%; border-radius: 2px; background: linear-gradient(90deg, var(--blue-lift), #a8c6ff); }

.try-scene { position: relative; }
.try-scene img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.try-caption {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 26px 20px 16px;
  background: linear-gradient(0deg, rgba(6, 10, 24, 0.96), transparent);
}
.try-caption p { margin: 6px 0 0; font-size: 14px; line-height: 1.5; color: rgba(255, 255, 255, 0.92); }
.try-tag {
  display: inline-block; color: #9fc0ff;
  font-size: 10px; font-weight: 750; letter-spacing: 0.14em; text-transform: uppercase;
}

.try-phrase { padding: 20px; border-bottom: 1px solid var(--line); }
/* The French is set in the serif — this is the language being taught, not the
   interface describing it, and the distinction is worth making visible. */
.try-fr {
  margin: 8px 0 8px; font-family: var(--font-fr); font-style: italic;
  font-size: 21px; line-height: 1.42; color: #fff; letter-spacing: 0;
}
.try-en { margin: 0; color: var(--dim); font-size: 13.5px; line-height: 1.55; }
.try-en em { color: var(--blue-lift); font-style: normal; font-family: var(--font-fr); font-style: italic; }

.try-quiz { padding: 20px; }
.try-q { margin: 8px 0 14px; font-size: 14.5px; font-weight: 620; line-height: 1.45; letter-spacing: -0.012em; }
.try-options { display: grid; gap: 8px; }
/* The options are French too, so they take the serif as well — a learner
   should meet the language in one consistent voice across the whole card. */
.try-opt {
  padding: 13px 15px; border-radius: 12px; cursor: pointer; text-align: left;
  border: 1px solid var(--line-strong); background: rgba(255, 255, 255, 0.05); color: #fff;
  font-family: var(--font-fr); font-size: 15.5px; line-height: 1.4;
  transition: border-color 200ms ease, background 200ms ease, transform 200ms var(--ease);
}
.try-opt:hover:not(:disabled) { border-color: rgba(255, 255, 255, 0.4); background: rgba(255, 255, 255, 0.1); transform: translateX(3px); }
.try-opt.right { border-color: rgba(110, 231, 168, 0.85); background: rgba(31, 122, 85, 0.3); }
.try-opt.wrong { border-color: rgba(255, 157, 148, 0.85); background: rgba(163, 32, 42, 0.28); }
.try-feedback { margin: 12px 0 0; min-height: 20px; font-size: 13.5px; line-height: 1.5; color: var(--dim); }
.try-feedback em { font-family: var(--font-fr); color: #cfe0ff; }

.try-done { padding: 28px 22px 26px; text-align: center; }
.try-check {
  display: grid; place-items: center; width: 52px; height: 52px; margin: 0 auto 14px;
  border-radius: 50%; background: rgba(31, 122, 85, 0.3); border: 1px solid rgba(110, 231, 168, 0.6); color: #6ee7a8;
}
.try-check svg { width: 24px; height: 24px; stroke-width: 2.4; }
.try-done h3 { margin: 0 0 8px; font-size: 21px; font-weight: 740; letter-spacing: -0.026em; }
.try-done p { margin: 0 0 18px; color: var(--dim); font-size: 14.5px; line-height: 1.55; }

/* Contained deliberately: this glow has escaped its box twice before, at
   375px and again at 1024px. inset keeps it tied to the frame at every width
   rather than to a fixed pixel spill. */
.try-glow {
  position: absolute; inset: 6% -4%; z-index: 0; pointer-events: none;
  background: radial-gradient(closest-side, rgba(58, 99, 196, 0.42), transparent 72%);
  filter: blur(46px);
}

/* ══ CLOSING CTA ═══════════════════════════════════════════════════════════
   The last thing before the FAQ and the moment someone decides, so it is the
   one band that gets to be loud. */
.waitlist { position: relative; padding: clamp(96px, 11vw, 150px) 0; overflow: hidden; }
.waitlist-inner { position: relative; text-align: center; max-width: 760px; }
.waitlist-glow {
  position: absolute; inset: -70% -20%; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side, rgba(58, 99, 196, 0.4), transparent 70%);
  filter: blur(54px);
}
.waitlist h2 {
  margin: 0 0 18px;
  font-size: clamp(33px, 5.6vw, 62px); font-weight: 780;
  letter-spacing: -0.045em; line-height: 1.03; text-wrap: balance;
}
.waitlist > .shell > p { color: var(--dim); font-size: 17px; line-height: 1.62; margin: 0 auto 34px; max-width: 540px; text-wrap: pretty; }
.waitlist-cta { display: flex; justify-content: center; margin-bottom: 20px; }
.waitlist-fine { color: var(--dimmer); font-size: 13px; margin: 0; }
.waitlist-fine a { color: #fff; text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
.waitlist-fine a:hover { text-decoration-color: var(--rouge); }

.waitlist-form { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-bottom: 14px; }
.waitlist-form input {
  flex: 1 1 300px; max-width: 380px; height: 56px; padding: 0 18px;
  border: 1px solid var(--line-strong); border-radius: 15px;
  background: rgba(255, 255, 255, 0.07); color: #fff; font: inherit; font-size: 15px;
  transition: border-color 200ms ease, background 200ms ease, box-shadow 200ms ease;
}
.waitlist-form input::placeholder { color: var(--dimmer); }
.waitlist-form input:focus { outline: none; border-color: var(--blue-lift); background: rgba(255,255,255,0.11); box-shadow: 0 0 0 4px rgba(109,143,234,0.2); }
.waitlist-form .btn { height: 56px; }
.waitlist-status { min-height: 22px; margin: 0 0 8px; font-size: 14px; font-weight: 600; }
.waitlist-status.ok { color: #6ee7a8; }
.waitlist-status.bad { color: #ff9d94; }

/* ══ FAQ ═══════════════════════════════════════════════════════════════════
   Seven rows, set larger and quieter. The disclosure animates via a
   grid-template-rows trick rather than height, because <details> toggles
   display and cannot be transitioned directly. With reduced motion or no
   support the panel simply appears — never unreadable. */
.faq { display: grid; gap: 8px; margin-top: 8px; }
.faq details {
  border-bottom: 1px solid var(--line);
  transition: border-color 260ms ease;
}
.faq details[open] { border-color: var(--line-strong); }
.faq summary {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 24px 4px; cursor: pointer; list-style: none;
  font-family: var(--font-display);
  font-size: 18px; font-weight: 640; letter-spacing: -0.022em; line-height: 1.35;
  transition: color 200ms ease;
}
.faq summary:hover { color: #cfe0ff; }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none; width: 12px; height: 12px; margin-right: 6px;
  border-right: 2px solid var(--dim); border-bottom: 2px solid var(--dim);
  transform: rotate(45deg) translateY(-3px);
  transition: transform 320ms var(--ease);
}
.faq details[open] summary::after { transform: rotate(-135deg) translateY(-3px); }
.faq p {
  margin: 0; padding: 0 60px 26px 4px;
  color: var(--dim); font-size: 15.5px; line-height: 1.7; text-wrap: pretty;
}
.faq p a { color: #cfe0ff; text-underline-offset: 3px; }

/* ══ FORM FIELDS ═══════════════════════════════════════════════════════════
   Used by /contact. The floating label rides on :placeholder-shown, which is
   why every input carries placeholder=" " — remove that and the label sits on
   top of the typed value. */
.contact-form { display: grid; gap: 15px; margin-top: 34px; }
.field { position: relative; }
.field input, .field textarea, .field select {
  width: 100%; padding: 23px 16px 10px;
  border: 1px solid var(--line-strong); border-radius: 14px;
  background: rgba(255, 255, 255, 0.05); color: #fff;
  font-family: var(--font-text); font-size: 15px;
  transition: border-color 200ms ease, background 200ms ease, box-shadow 200ms ease;
}
.field textarea { resize: vertical; min-height: 130px; line-height: 1.55; }
.field select { padding-top: 24px; appearance: none; cursor: pointer; }
.field select option { background: var(--night-2); color: #fff; }
.field input:focus, .field textarea:focus, .field select:focus {
  outline: none; border-color: var(--blue-lift); background: rgba(255, 255, 255, 0.09);
  box-shadow: 0 0 0 4px rgba(109, 143, 234, 0.18);
}
.field label {
  position: absolute; left: 17px; top: 18px; pointer-events: none;
  color: var(--dimmer); font-size: 15px; transform-origin: left top;
  transition: transform 180ms var(--ease), color 180ms ease, font-size 180ms ease;
}
.field label.static,
.field input:focus + label, .field textarea:focus + label, .field select:focus + label,
.field input:not(:placeholder-shown) + label, .field textarea:not(:placeholder-shown) + label {
  transform: translateY(-11px); color: #9fc0ff;
  font-size: 10.5px; font-weight: 680; letter-spacing: 0.05em;
}
.contact-form .btn { justify-self: start; }
.contact-status { min-height: 22px; margin: 0; font-size: 14px; font-weight: 600; }
.contact-status.ok { color: #6ee7a8; }
.contact-status.bad { color: #ff9d94; }

/* ══ FOOTER ════════════════════════════════════════════════════════════════
   The last impression, and it was four columns of small grey links. Three
   changes. The brand now carries the same travelling-light ring as the
   header, so both ends of the site are the same object rather than a logo and
   a thumbnail. Legal is its own column instead of being mixed in with
   marketing links — someone hunting for the refund policy should not have to
   read past "Blog". And the brand column states who operates this, from
   where, and in what currency, which is what a stranger checks before putting
   a card into a subscription.

   The top edge is a fading rule rather than a flat border, matching how the
   bands above separate. */
.footer { position: relative; padding: 84px 0 34px; background: linear-gradient(180deg, transparent, rgba(6, 10, 24, 0.75) 40%); }
.footer::before {
  content: ''; position: absolute; left: 6%; right: 6%; top: 0; height: 1px;
  background: linear-gradient(90deg, transparent, var(--line-strong) 20%, var(--line-strong) 80%, transparent);
}

.footer-grid { display: grid; grid-template-columns: 2.1fr 1fr 1fr 1fr; gap: 48px; }

.footer-brand .brand { font-size: 20.5px; font-weight: 600; letter-spacing: -0.035em; }
.footer-brand .brand strong { font-weight: 820; }
.footer-brand > p {
  margin: 20px 0 0; max-width: 330px;
  color: var(--dimmer); font-size: 14px; line-height: 1.65; text-wrap: pretty;
}

/* The trust line. Kept as plain facts — a stranger reads these before paying,
   and anything that sounds like marketing here has the opposite effect. */
.footer-facts {
  display: flex; flex-wrap: wrap; gap: 8px;
  margin: 22px 0 0; padding: 0; list-style: none;
}
.footer-facts li {
  padding: 6px 12px; border: 1px solid var(--line); border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: var(--dimmer); font-size: 11.5px; font-weight: 560; letter-spacing: 0.01em;
  white-space: nowrap;
}

.footer h4 {
  margin: 0 0 18px; color: #9fc0ff;
  font-family: var(--font-text);
  font-size: 11px; font-weight: 750; letter-spacing: 0.15em; text-transform: uppercase;
}
.footer nav { display: grid; gap: 12px; align-content: start; }
.footer nav a {
  position: relative; width: fit-content;
  color: var(--dimmer); text-decoration: none; font-size: 14.5px;
  transition: color 200ms ease;
}
/* An underline that grows from the left, rather than a colour flick. */
.footer nav a::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 1px;
  background: currentColor; transform: scaleX(0); transform-origin: 0 50%;
  transition: transform 300ms var(--ease);
}
.footer nav a:hover { color: #fff; }
.footer nav a:hover::after { transform: scaleX(1); }

/* The disclaimer is a compliance statement, so it gets its own line and a
   label rather than being the fourth column nobody reads. */
.footer-independent {
  display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap;
  margin: 52px 0 0; padding: 18px 22px;
  border: 1px solid var(--line); border-radius: 14px;
  background: rgba(255, 255, 255, 0.022);
  color: var(--dimmer); font-size: 12.5px; line-height: 1.65; text-wrap: pretty;
}
.footer-flag {
  flex: none; padding: 4px 10px; border-radius: 999px;
  border: 1px solid rgba(159, 192, 255, 0.32); background: rgba(58, 99, 196, 0.18);
  color: #9fc0ff; font-size: 10px; font-weight: 750; letter-spacing: 0.11em; text-transform: uppercase;
}

.footer-base {
  display: flex; align-items: center; justify-content: space-between; gap: 20px; flex-wrap: wrap;
  margin-top: 30px; padding-top: 24px; border-top: 1px solid var(--line);
}
.footer-base p { margin: 0; color: var(--dimmer); font-size: 12.5px; }
.footer-parent { color: var(--dim); text-decoration: none; border-bottom: 1px solid var(--line-strong); transition: color 200ms ease, border-color 200ms ease; }
.footer-parent:hover { color: #fff; border-color: var(--rouge); }
.tricolore { display: flex; gap: 3px; }
.tricolore i { display: block; width: 24px; height: 3px; border-radius: 2px; }
.tricolore i:nth-child(1) { background: var(--bleu); }
.tricolore i:nth-child(2) { background: #fff; opacity: 0.85; }
.tricolore i:nth-child(3) { background: var(--rouge); }


/* ── Footer CTA ────────────────────────────────────────────────────────────
   The footer's only call to action used to be the words "Join the waitlist"
   set as one grey 14.5px link among eleven others in the Company column —
   indistinguishable from Privacy. Someone who reads all the way to the bottom
   of the page is the most convinced visitor on it, and they were handed
   nothing to press.

   It now uses the signature ring, the same travelling light as the nav button
   and the brand mark, scaled up. That recipe is deliberately rationed: three
   places on the whole site, all of them the same decision. Adding a fourth
   loud element would spend the effect rather than compound it. */
.footer-cta {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 32px;
  margin-bottom: 56px;
  padding: 34px 36px;
  border: 1px solid var(--line-strong);
  border-radius: 24px;
  background:
    linear-gradient(135deg, rgba(58, 99, 196, 0.16), rgba(18, 34, 77, 0.4) 55%, rgba(206, 43, 55, 0.1)),
    rgba(255, 255, 255, 0.026);
  overflow: clip;
}

/* A slow bloom behind the panel so it reads as lit rather than filled. */
.footer-cta-glow {
  position: absolute;
  inset: -60% -10% auto -10%;
  height: 220%;
  pointer-events: none;
  background: radial-gradient(closest-side, rgba(58, 99, 196, 0.34), transparent 72%);
  filter: blur(46px);
}

.footer-cta-copy { position: relative; }
.footer-cta-copy .kicker { margin-bottom: 12px; }
.footer-cta h3 {
  margin: 0 0 9px;
  font-family: var(--font-display);
  font-size: clamp(22px, 2.6vw, 31px);
  font-weight: 780;
  letter-spacing: -0.036em;
  line-height: 1.08;
  text-wrap: balance;
}
.footer-cta-copy > p {
  margin: 0;
  max-width: 46ch;
  color: var(--dim);
  font-size: 14.5px;
  line-height: 1.6;
  text-wrap: pretty;
}

/* Larger than the nav instance: this one is the last thing on the page and has
   no competition, where the nav button sits beside a wordmark and four links. */
.btn-signature.footer-signature { padding: 2.5px; }
.btn-signature.footer-signature .sig-face {
  padding: 15px 30px;
  font-size: 15.5px;
  letter-spacing: -0.014em;
}
.btn-signature.footer-signature .sig-arrow { width: 17px; height: 17px; }
.btn-signature.footer-signature::before { inset: -5px; filter: blur(15px); opacity: 0.6; }
.btn-signature.footer-signature:hover::before { opacity: 0.95; }

@media (max-width: 860px) {
  .footer-cta {
    grid-template-columns: 1fr;
    gap: 24px;
    padding: 28px 24px;
    margin-bottom: 44px;
  }
  /* Full width on a phone: a right-aligned pill next to stacked copy reads as
     an afterthought, which is the opposite of the point. */
  .btn-signature.footer-signature { justify-content: center; }
  .btn-signature.footer-signature .sig-face { width: 100%; justify-content: center; }
}

/* The ring animates via @property. Under reduced motion the global block at the
   top of this file already stops it; this keeps the border a visible edge
   rather than whatever angle it froze on. */
@media (prefers-reduced-motion: reduce) {
  .footer-cta-glow { display: none; }
  .btn-signature.footer-signature .sig-ring {
    background: linear-gradient(120deg, var(--rouge), var(--gold) 45%, var(--blue-lift));
  }
}

/* ══ RESPONSIVE ════════════════════════════════════════════════════════════ */
@media (max-width: 1180px) {
  /* Five columns below this width is what made the plan buttons wrap. Three
     columns with the featured tier leading is a better read than five
     cramped ones. */
  .plans { grid-template-columns: repeat(3, 1fr); }
  .plan.featured { transform: none; }
}
@media (max-width: 1020px) {
  .feature-grid { grid-template-columns: repeat(2, 1fr); }
  .feature.big { grid-column: span 2; grid-row: auto; min-height: 300px; }
  .feature.wide { grid-column: span 2; }
  .readiness { grid-template-columns: repeat(2, 1fr); }
  .try-inner { grid-template-columns: 1fr; gap: 40px; }
  .try-copy { text-align: center; }
  .try-points { justify-items: center; }
  .try-points li { text-align: left; }
  .footer-grid { grid-template-columns: 1.6fr 1fr 1fr; gap: 34px; }
  .footer-brand { grid-column: 1 / -1; }
}
@media (max-width: 860px) {
  .why-grid { grid-template-columns: 1fr; gap: 0; }
  .why-card { padding: 26px 22px; }
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
  .stat:nth-child(odd) { border-left: 0; }
  .stat:nth-child(n + 3) { border-top: 1px solid var(--line); }
  .plans { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 620px) {
  .band { padding: 72px 0; }
  .steps { padding-left: 58px; }
  .steps::before { left: 20px; }
  .step { padding: 18px 0 18px 4px; }
  .step-n { left: -58px; width: 42px; height: 42px; font-size: 13px; }
  .feature-grid { grid-template-columns: 1fr; }
  .feature.big, .feature.wide { grid-column: span 1; }
  .readiness { grid-template-columns: 1fr; }
  .plans { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }
  .waitlist-form .btn { width: 100%; }
  .try-frame { border-radius: 20px; }
  .try-fr { font-size: 18px; }
  .faq summary { font-size: 16.5px; padding: 20px 4px; }
  .faq p { padding-right: 20px; }
}

/* ══ REDUCED MOTION ════════════════════════════════════════════════════════
   The global block at the top of this file already zeroes durations. These
   are the cases where a *resting* state depends on motion, or where a
   transform would otherwise be left applied. */
@media (prefers-reduced-motion: reduce) {
  .band::after { display: none; }
  .reveal-armed .section-title.reveal { clip-path: none !important; }
  .plan.featured::after { animation: none !important; }
  .plan.featured { transform: translateY(-10px) scale(1.03) !important; }
  .step:hover, .try-opt:hover, .footer nav a:hover, .contact-route:hover { transform: none; }
  .plan-cta::before { display: none; }
  .try-check { animation: none; }
}
